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 nest_depth: usize,
181}
182
183const MAX_NEST_DEPTH: usize = 64;
190
191const MAX_BINARY_CHAIN: usize = 256;
197
198enum NamedTableConstraintKind {
202 Check,
203 Unique,
204 PrimaryKey,
205}
206
207impl Parser {
208 fn new(tokens: Vec<Token>) -> Self {
209 Self {
210 tokens,
211 pos: 0,
212 nest_depth: 0,
213 }
214 }
215
216 fn enter_nested(&mut self) -> Result<(), ParseError> {
219 self.nest_depth += 1;
220 if self.nest_depth > MAX_NEST_DEPTH {
221 self.nest_depth -= 1;
222 return Err(self.err(alloc::format!(
223 "statement nests deeper than {MAX_NEST_DEPTH} levels"
224 )));
225 }
226 Ok(())
227 }
228
229 fn peek(&self) -> &Token {
230 &self.tokens[self.pos]
232 }
233
234 fn advance(&mut self) -> Token {
235 let t = mem::replace(&mut self.tokens[self.pos], Token::Eof);
236 if self.pos + 1 < self.tokens.len() {
237 self.pos += 1;
238 }
239 t
240 }
241
242 fn err(&self, message: String) -> ParseError {
243 ParseError {
244 message,
245 token_pos: self.pos,
246 }
247 }
248
249 fn expect_eof(&self) -> Result<(), ParseError> {
250 if matches!(self.peek(), Token::Eof) {
251 Ok(())
252 } else {
253 Err(self.err(format!("expected end of input, got {:?}", self.peek())))
254 }
255 }
256
257 fn consume_until_statement_boundary(&mut self) {
262 loop {
263 match self.peek() {
264 Token::Semicolon | Token::Eof => return,
265 _ => self.advance(),
266 };
267 }
268 }
269
270 fn scan_sequence_name_until_boundary(&mut self) -> Option<String> {
276 let mut seq: Option<String> = None;
277 let mut after_sequence_kw = false;
278 let mut after_name_kw = false;
279 loop {
280 match self.peek().clone() {
281 Token::Semicolon | Token::Eof => break,
282 Token::Ident(s) | Token::QuotedIdent(s) => {
283 if after_name_kw && seq.is_none() {
284 self.advance();
285 let mut name = s;
286 while matches!(self.peek(), Token::Dot) {
289 self.advance();
290 if let Token::Ident(n) | Token::QuotedIdent(n) = self.advance() {
291 name = n;
292 }
293 }
294 seq = Some(name);
295 after_name_kw = false;
296 continue;
297 }
298 if after_sequence_kw && s.eq_ignore_ascii_case("name") {
299 after_name_kw = true;
300 after_sequence_kw = false;
301 } else {
302 after_sequence_kw = s.eq_ignore_ascii_case("sequence");
303 }
304 self.advance();
305 }
306 Token::String(s) => {
307 if seq.is_none() {
308 let bare = s
310 .rsplit_once('.')
311 .map_or_else(|| s.clone(), |(_, b)| b.to_string());
312 seq = Some(bare);
313 }
314 self.advance();
315 }
316 _ => {
317 after_sequence_kw = false;
318 after_name_kw = false;
319 self.advance();
320 }
321 }
322 }
323 seq
324 }
325
326 fn expect_ident_like(&mut self) -> Result<String, ParseError> {
327 let first = match self.advance() {
328 Token::Ident(s) | Token::QuotedIdent(s) => s,
329 other => {
330 return Err(ParseError {
331 message: format!("expected identifier, got {other:?}"),
332 token_pos: self.pos.saturating_sub(1),
333 });
334 }
335 };
336 if matches!(self.peek(), Token::Dot) {
343 self.advance();
344 match self.advance() {
345 Token::Ident(s) | Token::QuotedIdent(s) => return Ok(s),
346 other => {
347 return Err(ParseError {
348 message: format!("expected identifier after '{first}.', got {other:?}"),
349 token_pos: self.pos.saturating_sub(1),
350 });
351 }
352 }
353 }
354 Ok(first)
355 }
356
357 #[allow(clippy::too_many_lines)]
358 fn parse_one_statement(&mut self) -> Result<Statement, ParseError> {
359 if matches!(self.peek(), Token::Eof | Token::Semicolon) {
367 return Ok(Statement::Empty);
368 }
369 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
379 let lc = s.to_ascii_lowercase();
380 if is_dump_noise_statement(&lc) {
381 self.consume_until_statement_boundary();
382 return Ok(Statement::Empty);
383 }
384 }
385 match self.peek() {
386 Token::Select => self.parse_select_stmt(),
387 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {
403 self.advance();
404 let body_text = match self.advance() {
405 Token::String(s) => s,
406 other => {
407 return Err(self.err(alloc::format!(
408 "expected dollar-quoted body after DO, got {other:?}"
409 )));
410 }
411 };
412 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("language")) {
414 self.advance();
415 let _ = self.expect_ident_like()?;
416 }
417 let block = parse_plpgsql_body(&body_text)?;
422 Ok(Statement::DoBlock(block))
423 }
424 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with") => {
428 self.advance();
429 self.parse_with_cte_then_select()
430 }
431 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("explain") => {
434 self.advance();
435 let mut analyze = false;
436 let mut suggest = false;
437 if matches!(self.peek(), Token::LParen) {
439 self.advance();
440 let opt = match self.peek().clone() {
441 Token::Ident(s) | Token::QuotedIdent(s) => s,
442 other => {
443 return Err(self.err(format!(
444 "expected option keyword inside EXPLAIN (…), got {other:?}"
445 )));
446 }
447 };
448 if !opt.eq_ignore_ascii_case("suggest") {
449 return Err(self.err(format!(
450 "unknown EXPLAIN option {opt:?}; v6.8.3 supports SUGGEST"
451 )));
452 }
453 self.advance();
454 if !matches!(self.peek(), Token::RParen) {
455 return Err(self.err(format!(
456 "expected ')' after EXPLAIN option, got {:?}",
457 self.peek()
458 )));
459 }
460 self.advance();
461 suggest = true;
462 } else if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
463 && (s.eq_ignore_ascii_case("analyze") || s.eq_ignore_ascii_case("analyse"))
464 {
465 self.advance();
466 analyze = true;
467 }
468 let inner = self.parse_select_stmt()?;
469 let Statement::Select(s) = inner else {
470 return Err(self.err(format!("EXPLAIN body must be a SELECT, got {inner:?}")));
471 };
472 Ok(Statement::Explain(crate::ast::ExplainStatement {
473 analyze,
474 inner: Box::new(s),
475 suggest,
476 }))
477 }
478 Token::Create => self.parse_create_stmt(),
479 Token::Insert => self.parse_insert_stmt(),
480 Token::Begin => {
481 self.advance();
482 Ok(Statement::Begin)
483 }
484 Token::Commit => {
485 self.advance();
486 Ok(Statement::Commit)
487 }
488 Token::Rollback => {
489 self.advance();
490 if matches!(self.peek(), Token::To) {
494 self.advance();
495 if matches!(self.peek(), Token::Savepoint) {
496 self.advance();
497 }
498 let name = self.expect_ident_like()?;
499 Ok(Statement::RollbackToSavepoint(name))
500 } else {
501 Ok(Statement::Rollback)
502 }
503 }
504 Token::Savepoint => {
505 self.advance();
506 let name = self.expect_ident_like()?;
507 Ok(Statement::Savepoint(name))
508 }
509 Token::Release => {
510 self.advance();
511 if matches!(self.peek(), Token::Savepoint) {
514 self.advance();
515 }
516 let name = self.expect_ident_like()?;
517 Ok(Statement::ReleaseSavepoint(name))
518 }
519 Token::Show => {
520 self.advance();
521 let target = match self.advance() {
527 Token::Tables => "tables".to_string(),
528 Token::Create => "create".to_string(),
532 Token::Index => "index".to_string(),
535 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
536 other => {
537 return Err(self.err(format!(
538 "expected SHOW target, got {other:?}"
539 )));
540 }
541 };
542 match target.as_str() {
543 "tables" => Ok(Statement::ShowTables),
544 "users" => Ok(Statement::ShowUsers),
545 "indexes" | "index" | "keys" => {
555 if !matches!(self.peek(), Token::From) {
556 return Err(self.err(format!(
557 "expected FROM after SHOW INDEXES, got {:?}",
558 self.peek()
559 )));
560 }
561 self.advance();
562 let table = self.expect_ident_like()?;
563 Ok(Statement::ShowIndexes(table))
564 }
565 "status" => Ok(Statement::ShowStatus),
570 "variables" => Ok(Statement::ShowVariables),
571 "processlist" => Ok(Statement::ShowProcesslist),
573 "create" => {
574 let kind = match self.advance() {
577 Token::Ident(s) | Token::QuotedIdent(s) => s,
578 Token::Table => "table".to_string(),
579 other => {
580 return Err(self.err(format!(
581 "expected TABLE after SHOW CREATE, got {other:?}"
582 )));
583 }
584 };
585 if !kind.eq_ignore_ascii_case("table") {
586 return Err(self.err(format!(
587 "unsupported SHOW CREATE {kind:?}; v7.17 supports TABLE only"
588 )));
589 }
590 let name = self.expect_ident_like()?;
591 Ok(Statement::ShowCreateTable(name))
592 }
593 "databases" | "schemas" => Ok(Statement::ShowDatabases),
599 "publications" => Ok(Statement::ShowPublications),
604 "subscriptions" => Ok(Statement::ShowSubscriptions),
606 "columns" => {
607 if !matches!(self.peek(), Token::From) {
608 return Err(self.err(format!(
609 "expected FROM after SHOW COLUMNS, got {:?}",
610 self.peek()
611 )));
612 }
613 self.advance();
614 let table = self.expect_ident_like()?;
615 Ok(Statement::ShowColumns(table))
616 }
617 other => Err(self.err(format!(
618 "unknown SHOW target {other:?}; supported: TABLES, COLUMNS, USERS, PUBLICATIONS"
619 ))),
620 }
621 }
622 Token::Drop => {
628 self.advance();
629 match self.peek() {
630 Token::Publication => {
631 self.advance();
632 let name = self.expect_ident_or_string()?;
633 Ok(Statement::DropPublication(name))
634 }
635 Token::Subscription => {
636 self.advance();
637 let name = self.expect_ident_or_string()?;
638 Ok(Statement::DropSubscription(name))
639 }
640 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("user") => {
641 self.advance();
642 let name = self.expect_ident_or_string()?;
643 Ok(Statement::DropUser(name))
644 }
645 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("trigger") => {
647 self.advance();
648 let if_exists = self.consume_if_exists();
649 let name = self.expect_ident_like()?;
650 if !matches!(self.peek(), Token::On) {
652 return Err(self.err(alloc::format!(
653 "expected ON <table> after DROP TRIGGER {name:?}, got {:?}",
654 self.peek()
655 )));
656 }
657 self.advance();
658 let table = self.expect_ident_like()?;
659 Ok(Statement::DropTrigger {
660 name,
661 table,
662 if_exists,
663 })
664 }
665 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("function") => {
669 self.advance();
670 let if_exists = self.consume_if_exists();
671 let name = self.expect_ident_like()?;
672 if matches!(self.peek(), Token::LParen) {
674 self.advance();
675 let mut depth = 1usize;
677 while depth > 0 {
678 match self.peek() {
679 Token::LParen => depth += 1,
680 Token::RParen => depth -= 1,
681 Token::Eof => {
682 return Err(self.err(alloc::format!(
683 "unterminated arg list in DROP FUNCTION {name:?}"
684 )));
685 }
686 _ => {}
687 }
688 self.advance();
689 }
690 }
691 Ok(Statement::DropFunction { name, if_exists })
692 }
693 Token::Table => {
701 self.advance();
702 let if_exists = self.consume_if_exists();
703 let mut names: Vec<String> = Vec::new();
704 loop {
705 names.push(self.expect_ident_like()?);
706 if matches!(self.peek(), Token::Comma) {
707 self.advance();
708 continue;
709 }
710 break;
711 }
712 if matches!(
713 self.peek(),
714 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
715 || s.eq_ignore_ascii_case("restrict")
716 ) {
717 self.advance();
718 }
719 Ok(Statement::DropTable { names, if_exists })
720 }
721 Token::Index => {
727 self.advance();
728 let if_exists = self.consume_if_exists();
729 let name = self.expect_ident_like()?;
730 if matches!(
731 self.peek(),
732 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
733 || s.eq_ignore_ascii_case("restrict")
734 ) {
735 self.advance();
736 }
737 Ok(Statement::DropIndex { name, if_exists })
738 }
739 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("schema") => {
745 self.advance();
746 let if_exists = self.consume_if_exists();
747 let mut names = vec![self.expect_ident_like()?];
748 while matches!(self.peek(), Token::Comma) {
749 self.advance();
750 names.push(self.expect_ident_like()?);
751 }
752 if matches!(
753 self.peek(),
754 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
755 || s.eq_ignore_ascii_case("restrict")
756 ) {
757 self.advance();
758 }
759 Ok(Statement::DropSchema { names, if_exists })
760 }
761 Token::Ident(s) | Token::QuotedIdent(s)
764 if s.eq_ignore_ascii_case("type") =>
765 {
766 self.advance();
767 let if_exists = self.consume_if_exists();
768 let mut names = vec![self.expect_ident_like()?];
769 while matches!(self.peek(), Token::Comma) {
770 self.advance();
771 names.push(self.expect_ident_like()?);
772 }
773 if matches!(
774 self.peek(),
775 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
776 || s.eq_ignore_ascii_case("restrict")
777 ) {
778 self.advance();
779 }
780 Ok(Statement::DropType { names, if_exists })
781 }
782 Token::Ident(s) | Token::QuotedIdent(s)
785 if s.eq_ignore_ascii_case("domain") =>
786 {
787 self.advance();
788 let if_exists = self.consume_if_exists();
789 let mut names = vec![self.expect_ident_like()?];
790 while matches!(self.peek(), Token::Comma) {
791 self.advance();
792 names.push(self.expect_ident_like()?);
793 }
794 if matches!(
795 self.peek(),
796 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
797 || s.eq_ignore_ascii_case("restrict")
798 ) {
799 self.advance();
800 }
801 Ok(Statement::DropDomain { names, if_exists })
802 }
803 Token::Ident(s) | Token::QuotedIdent(s)
806 if s.eq_ignore_ascii_case("materialized") =>
807 {
808 self.advance();
809 let nxt = self.peek().clone();
810 if !matches!(&nxt, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("view"))
811 {
812 return Err(self.err(alloc::format!(
813 "expected VIEW after DROP MATERIALIZED, got {nxt:?}"
814 )));
815 }
816 self.advance();
817 let if_exists = self.consume_if_exists();
818 let mut names = vec![self.expect_ident_like()?];
819 while matches!(self.peek(), Token::Comma) {
820 self.advance();
821 names.push(self.expect_ident_like()?);
822 }
823 if matches!(
824 self.peek(),
825 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
826 || s.eq_ignore_ascii_case("restrict")
827 ) {
828 self.advance();
829 }
830 Ok(Statement::DropMaterializedView { names, if_exists })
831 }
832 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("view") => {
835 self.advance();
836 let if_exists = self.consume_if_exists();
837 let mut names = vec![self.expect_ident_like()?];
838 while matches!(self.peek(), Token::Comma) {
839 self.advance();
840 names.push(self.expect_ident_like()?);
841 }
842 if matches!(
843 self.peek(),
844 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
845 || s.eq_ignore_ascii_case("restrict")
846 ) {
847 self.advance();
848 }
849 Ok(Statement::DropView { names, if_exists })
850 }
851 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sequence") => {
855 self.advance();
856 let if_exists = self.consume_if_exists();
857 let mut names = vec![self.expect_ident_like()?];
858 while matches!(self.peek(), Token::Comma) {
859 self.advance();
860 names.push(self.expect_ident_like()?);
861 }
862 if matches!(
863 self.peek(),
864 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
865 || s.eq_ignore_ascii_case("restrict")
866 ) {
867 self.advance();
868 }
869 Ok(Statement::DropSequence { names, if_exists })
870 }
871 other => Err(self.err(format!(
872 "expected TABLE / INDEX / SCHEMA / SEQUENCE / USER / PUBLICATION / \
873 SUBSCRIPTION / TRIGGER / FUNCTION after DROP, got {other:?}"
874 ))),
875 }
876 }
877 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("refresh") => {
879 self.advance();
880 let nxt = self.peek().clone();
881 if !matches!(&nxt, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("materialized"))
882 {
883 return Err(self.err(alloc::format!(
884 "expected MATERIALIZED after REFRESH, got {nxt:?}"
885 )));
886 }
887 self.advance();
888 let nxt2 = self.peek().clone();
889 if !matches!(&nxt2, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("view"))
890 {
891 return Err(self.err(alloc::format!(
892 "expected VIEW after REFRESH MATERIALIZED, got {nxt2:?}"
893 )));
894 }
895 self.advance();
896 let name = self.expect_ident_like()?;
897 let with_data = self.parse_optional_with_data(true)?;
898 Ok(Statement::RefreshMaterializedView { name, with_data })
899 }
900 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
901 self.advance();
902 self.parse_update_after_keyword()
903 }
904 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
905 self.advance();
906 self.parse_delete_after_keyword()
907 }
908 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("alter") => {
912 self.advance();
913 self.parse_alter_after_keyword()
914 }
915 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("wait") => {
919 self.advance();
920 self.parse_wait_after_keyword()
921 }
922 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("compact") => {
933 self.advance();
934 let next = self.peek().clone();
935 let cold = match next {
936 Token::Ident(s) | Token::QuotedIdent(s) => s,
937 _ => {
938 return Err(
939 self.err(format!("expected COLD after COMPACT, got {:?}", self.peek()))
940 );
941 }
942 };
943 if !cold.eq_ignore_ascii_case("cold") {
944 return Err(self.err(format!("expected COLD after COMPACT, got {cold:?}")));
945 }
946 self.advance();
947 let next = self.peek().clone();
948 let segments = match next {
949 Token::Ident(s) | Token::QuotedIdent(s) => s,
950 _ => {
951 return Err(self.err(format!(
952 "expected SEGMENTS after COMPACT COLD, got {:?}",
953 self.peek()
954 )));
955 }
956 };
957 if !segments.eq_ignore_ascii_case("segments") {
958 return Err(self.err(format!(
959 "expected SEGMENTS after COMPACT COLD, got {segments:?}"
960 )));
961 }
962 self.advance();
963 Ok(Statement::CompactColdSegments)
964 }
965 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("merge") => {
972 self.advance();
973 self.parse_merge_after_keyword()
974 }
975 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("analyze") => {
976 self.advance();
977 let target = match self.peek() {
978 Token::Eof | Token::Semicolon => None,
979 Token::Ident(_) | Token::QuotedIdent(_) => {
980 Some(self.expect_ident_like()?)
981 }
982 other => {
983 return Err(self.err(format!(
984 "expected table name or end of statement after ANALYZE, got {other:?}"
985 )));
986 }
987 };
988 Ok(Statement::Analyze(target))
989 }
990 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {
996 self.advance();
997 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"))
1002 {
1003 self.advance();
1004 }
1005 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("names"))
1010 {
1011 self.advance();
1012 if matches!(
1014 self.peek(),
1015 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
1016 ) {
1017 self.advance();
1018 }
1019 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("collate"))
1021 {
1022 self.advance();
1023 if matches!(
1024 self.peek(),
1025 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
1026 ) {
1027 self.advance();
1028 }
1029 }
1030 return Ok(Statement::Empty);
1031 }
1032 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("authorization"))
1042 {
1043 self.advance(); match self.peek().clone() {
1045 Token::Default => {
1046 self.advance();
1047 }
1048 Token::String(_)
1049 | Token::Ident(_)
1050 | Token::QuotedIdent(_) => {
1051 self.advance();
1052 }
1053 other => {
1054 return Err(self.err(alloc::format!(
1055 "expected DEFAULT / '<role>' / <ident> after SET SESSION AUTHORIZATION, got {other:?}"
1056 )));
1057 }
1058 }
1059 return Ok(Statement::Empty);
1060 }
1061 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("character"))
1064 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("set"))
1065 {
1066 self.advance(); self.advance(); if matches!(
1069 self.peek(),
1070 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
1071 ) {
1072 self.advance();
1073 }
1074 return Ok(Statement::Empty);
1075 }
1076 let mut pairs: Vec<(String, crate::ast::SetValue)> = Vec::new();
1081 loop {
1082 let lhs = match self.peek().clone() {
1083 Token::SessionVar(s) => {
1084 self.advance();
1085 s
1086 }
1087 Token::Ident(_) | Token::QuotedIdent(_) => self.parse_set_param_name()?,
1088 other => {
1089 return Err(self.err(format!(
1090 "expected parameter name after SET, got {other:?}"
1091 )));
1092 }
1093 };
1094 match self.peek() {
1096 Token::Eq => {
1097 self.advance();
1098 }
1099 Token::To => {
1100 self.advance();
1101 }
1102 other => {
1103 return Err(self.err(format!(
1104 "expected `=` or TO after SET {lhs}, got {other:?}"
1105 )));
1106 }
1107 }
1108 let value = self.parse_set_value()?;
1109 pairs.push((lhs, value));
1110 if matches!(self.peek(), Token::Comma) {
1111 self.advance();
1112 continue;
1113 }
1114 break;
1115 }
1116 if pairs.len() == 1 {
1117 let (name, value) = pairs.into_iter().next().unwrap();
1118 Ok(Statement::SetParameter { name, value })
1119 } else {
1120 Ok(Statement::SetParameterList(pairs))
1121 }
1122 }
1123 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("reset") => {
1125 self.advance();
1126 match self.peek().clone() {
1127 Token::All => {
1128 self.advance();
1129 Ok(Statement::ResetParameter(None))
1130 }
1131 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("all") => {
1132 self.advance();
1133 Ok(Statement::ResetParameter(None))
1134 }
1135 _ => {
1136 let name = self.parse_set_param_name()?;
1137 Ok(Statement::ResetParameter(Some(name)))
1138 }
1139 }
1140 }
1141 other => Err(self.err(format!(
1142 "expected SELECT / CREATE / DROP / INSERT / UPDATE / DELETE / ALTER / BEGIN / COMMIT / \
1143 ROLLBACK / SAVEPOINT / RELEASE / SHOW at start of statement, got {other:?}"
1144 ))),
1145 }
1146 }
1147
1148 fn parse_create_stmt(&mut self) -> Result<Statement, ParseError> {
1149 debug_assert!(matches!(self.peek(), Token::Create));
1150 self.advance();
1151 match self.peek() {
1152 Token::Table => self.parse_create_table_stmt_after_create(),
1153 Token::Index => self.parse_create_index_stmt_after_create(false),
1154 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unique") => {
1161 self.advance();
1162 if !matches!(self.peek(), Token::Index) {
1163 return Err(self.err(alloc::format!(
1164 "expected INDEX after CREATE UNIQUE, got {:?}",
1165 self.peek()
1166 )));
1167 }
1168 self.parse_create_index_stmt_after_create(true)
1169 }
1170 Token::Publication => {
1171 self.advance();
1172 self.parse_create_publication_after_keyword()
1173 }
1174 Token::Subscription => {
1175 self.advance();
1176 self.parse_create_subscription_after_keyword()
1177 }
1178 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("user") => {
1182 self.advance();
1183 self.parse_create_user_after_keyword()
1184 }
1185 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("extension") => {
1189 self.advance();
1190 self.parse_create_extension_after_keyword()
1191 }
1192 Token::Or => {
1198 self.advance();
1199 let next = self.peek();
1200 let (Token::Ident(s2) | Token::QuotedIdent(s2)) = next else {
1201 return Err(self.err(alloc::format!(
1202 "expected REPLACE after CREATE OR, got {next:?}"
1203 )));
1204 };
1205 if !s2.eq_ignore_ascii_case("replace") {
1206 return Err(self.err(alloc::format!(
1207 "expected REPLACE after CREATE OR, got {s2:?}"
1208 )));
1209 }
1210 self.advance();
1211 self.parse_create_function_or_trigger_after_or_replace(true)
1212 }
1213 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("function") => {
1214 self.advance();
1215 self.parse_create_function_after_keyword(false)
1216 }
1217 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("trigger") => {
1218 self.advance();
1219 self.parse_create_trigger_after_keyword(false)
1220 }
1221 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sequence") => {
1223 self.advance();
1224 self.parse_create_sequence_after_keyword(false)
1225 }
1226 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("view") => {
1228 self.advance();
1229 self.parse_create_view_after_keyword(false, false, false)
1230 }
1231 Token::Ident(s) | Token::QuotedIdent(s)
1245 if s.eq_ignore_ascii_case("algorithm")
1246 || s.eq_ignore_ascii_case("definer")
1247 || s.eq_ignore_ascii_case("sql") =>
1248 {
1249 self.consume_mysql_view_prefix()?;
1250 let next = self.peek().clone();
1255 if matches!(&next, Token::Ident(s2) | Token::QuotedIdent(s2)
1256 if s2.eq_ignore_ascii_case("view"))
1257 {
1258 self.advance();
1259 self.parse_create_view_after_keyword(false, false, false)
1260 } else {
1261 Err(self.err(alloc::format!(
1262 "expected VIEW after MySQL view prefix (ALGORITHM/DEFINER/SQL SECURITY), got {next:?}"
1263 )))
1264 }
1265 }
1266 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("type") => {
1268 self.advance();
1269 self.parse_create_type_after_keyword()
1270 }
1271 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("domain") => {
1274 self.advance();
1275 self.parse_create_domain_after_keyword()
1276 }
1277 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("schema") => {
1281 self.advance();
1282 let if_not_exists = self.parse_if_not_exists();
1283 let name = self.expect_ident_like()?;
1284 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
1287 if s.eq_ignore_ascii_case("authorization"))
1288 {
1289 self.advance();
1290 let _ = self.expect_ident_like()?;
1291 }
1292 Ok(Statement::CreateSchema { name, if_not_exists })
1293 }
1294 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("materialized") => {
1296 self.advance();
1297 let next = self.peek().clone();
1298 if matches!(&next, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("view"))
1299 {
1300 self.advance();
1301 self.parse_create_materialized_view_after_keyword()
1302 } else {
1303 Err(self.err(alloc::format!(
1304 "expected VIEW after CREATE MATERIALIZED, got {next:?}"
1305 )))
1306 }
1307 }
1308 Token::Ident(s) | Token::QuotedIdent(s)
1309 if s.eq_ignore_ascii_case("temporary") || s.eq_ignore_ascii_case("temp") =>
1310 {
1311 self.advance();
1312 let next = self.peek().clone();
1314 if matches!(&next, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("sequence"))
1315 {
1316 self.advance();
1317 self.parse_create_sequence_after_keyword(true)
1318 } else if matches!(&next, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("view"))
1319 {
1320 self.advance();
1321 self.parse_create_view_after_keyword(false, false, true)
1322 } else {
1323 self.consume_until_statement_boundary();
1325 Ok(Statement::Empty)
1326 }
1327 }
1328 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("procedure") => {
1338 self.consume_mysql_routine_body();
1339 Ok(Statement::Empty)
1340 }
1341 Token::Ident(s) | Token::QuotedIdent(s)
1352 if matches!(
1353 s.to_ascii_lowercase().as_str(),
1354 "database"
1355 | "role"
1356 | "policy"
1357 | "operator"
1358 | "cast"
1359 | "rule"
1360 | "aggregate"
1361 | "language"
1362 | "collation"
1363 | "conversion"
1364 | "statistics"
1372 | "event"
1373 | "foreign"
1374 ) =>
1375 {
1376 self.consume_until_statement_boundary();
1377 Ok(Statement::Empty)
1378 }
1379 other => Err(self.err(format!(
1380 "expected TABLE / INDEX / USER / EXTENSION / PUBLICATION / SUBSCRIPTION / FUNCTION / TRIGGER / SEQUENCE / SCHEMA / VIEW / TYPE / DOMAIN [OR REPLACE …] after CREATE, got {other:?}"
1381 ))),
1382 }
1383 }
1384
1385 fn parse_create_function_or_trigger_after_or_replace(
1390 &mut self,
1391 or_replace: bool,
1392 ) -> Result<Statement, ParseError> {
1393 let tok = self.peek();
1394 let (Token::Ident(s) | Token::QuotedIdent(s)) = tok else {
1395 return Err(self.err(alloc::format!(
1396 "expected FUNCTION / TRIGGER / VIEW after CREATE OR REPLACE, got {tok:?}"
1397 )));
1398 };
1399 if s.eq_ignore_ascii_case("function") {
1400 self.advance();
1401 self.parse_create_function_after_keyword(or_replace)
1402 } else if s.eq_ignore_ascii_case("trigger") {
1403 self.advance();
1404 self.parse_create_trigger_after_keyword(or_replace)
1405 } else if s.eq_ignore_ascii_case("view") {
1406 self.advance();
1408 self.parse_create_view_after_keyword(or_replace, false, false)
1409 } else if s.eq_ignore_ascii_case("temporary") || s.eq_ignore_ascii_case("temp") {
1410 self.advance();
1412 let nxt = self.peek().clone();
1413 if matches!(&nxt, Token::Ident(n) | Token::QuotedIdent(n) if n.eq_ignore_ascii_case("view"))
1414 {
1415 self.advance();
1416 self.parse_create_view_after_keyword(or_replace, false, true)
1417 } else {
1418 Err(self.err(alloc::format!(
1419 "expected VIEW after CREATE OR REPLACE TEMPORARY, got {nxt:?}"
1420 )))
1421 }
1422 } else {
1423 Err(self.err(alloc::format!(
1424 "expected FUNCTION / TRIGGER / VIEW after CREATE OR REPLACE, got {s:?}"
1425 )))
1426 }
1427 }
1428
1429 fn parse_create_extension_after_keyword(&mut self) -> Result<Statement, ParseError> {
1434 self.consume_if_not_exists();
1436 let name = self.expect_ident_like()?;
1437 loop {
1440 match self.peek() {
1441 Token::Ident(s) if s.eq_ignore_ascii_case("with") => {
1442 self.advance();
1443 continue;
1444 }
1445 Token::Ident(s) if s.eq_ignore_ascii_case("schema") => {
1446 self.advance();
1447 let _ = self.expect_ident_like()?;
1448 continue;
1449 }
1450 Token::Ident(s) if s.eq_ignore_ascii_case("version") => {
1451 self.advance();
1452 let _ = self.advance();
1454 continue;
1455 }
1456 Token::Ident(s) if s.eq_ignore_ascii_case("from") => {
1457 self.advance();
1458 let _ = self.advance();
1459 continue;
1460 }
1461 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => {
1462 self.advance();
1463 continue;
1464 }
1465 _ => break,
1466 }
1467 }
1468 Ok(Statement::CreateExtension(name))
1469 }
1470
1471 fn parse_create_function_after_keyword(
1483 &mut self,
1484 or_replace: bool,
1485 ) -> Result<Statement, ParseError> {
1486 let name = self.expect_ident_like()?;
1487 if !matches!(self.peek(), Token::LParen) {
1491 return Err(self.err(alloc::format!(
1492 "expected '(' after function name {name:?}, got {:?}",
1493 self.peek()
1494 )));
1495 }
1496 self.advance();
1497 let args = self.parse_function_arg_list()?;
1498 let tok = self.peek();
1500 let (Token::Ident(s) | Token::QuotedIdent(s)) = tok else {
1501 return Err(self.err(alloc::format!(
1502 "expected RETURNS after function arg list, got {tok:?}"
1503 )));
1504 };
1505 if !s.eq_ignore_ascii_case("returns") {
1506 return Err(self.err(alloc::format!(
1507 "expected RETURNS after function arg list, got {s:?}"
1508 )));
1509 }
1510 self.advance();
1511 let returns = self.parse_function_return()?;
1512 let mut language: Option<String> = self.parse_optional_language()?;
1515 if !matches!(self.peek(), Token::As) {
1519 return Err(self.err(alloc::format!(
1520 "expected AS before function body, got {:?}",
1521 self.peek()
1522 )));
1523 }
1524 self.advance();
1525 let body_text = match self.peek() {
1526 Token::String(s) => {
1527 let body = s.clone();
1528 self.advance();
1529 body
1530 }
1531 other => {
1532 return Err(self.err(alloc::format!(
1533 "expected $$-quoted function body after AS, got {other:?}"
1534 )));
1535 }
1536 };
1537 if language.is_none() {
1539 language = self.parse_optional_language()?;
1540 }
1541 let language = language.unwrap_or_else(|| String::from("sql"));
1542 let body = if language.eq_ignore_ascii_case("plpgsql") {
1547 match parse_plpgsql_body(&body_text) {
1548 Ok(block) => FunctionBody::PlPgSql(block),
1549 Err(_) => FunctionBody::Raw(body_text),
1555 }
1556 } else {
1557 FunctionBody::Raw(body_text)
1558 };
1559 Ok(Statement::CreateFunction(CreateFunctionStatement {
1560 name,
1561 or_replace,
1562 args,
1563 returns,
1564 language,
1565 body,
1566 }))
1567 }
1568
1569 fn parse_function_arg_list(&mut self) -> Result<Vec<FunctionArg>, ParseError> {
1573 let mut args: Vec<FunctionArg> = Vec::new();
1574 if matches!(self.peek(), Token::RParen) {
1575 self.advance();
1576 return Ok(args);
1577 }
1578 loop {
1579 let mode = if matches!(self.peek(), Token::In) {
1582 self.advance();
1583 FunctionArgMode::In
1584 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("out"))
1585 {
1586 self.advance();
1587 FunctionArgMode::Out
1588 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("inout"))
1589 {
1590 self.advance();
1591 FunctionArgMode::InOut
1592 } else {
1593 FunctionArgMode::In
1594 };
1595 let (name, ty_token) = {
1601 let first = self.expect_ident_like()?;
1602 match self.peek() {
1605 Token::Ident(_) | Token::QuotedIdent(_) => {
1606 let ty = self.expect_ident_like()?;
1607 (Some(first), ty)
1608 }
1609 _ => (None, first),
1610 }
1611 };
1612 let ty = match map_type_ident_to_column_type_name(&ty_token) {
1614 Some(t) => FunctionArgType::Typed(t),
1615 None => FunctionArgType::Raw(ty_token),
1616 };
1617 args.push(FunctionArg { mode, name, ty });
1618 match self.peek() {
1619 Token::Comma => {
1620 self.advance();
1621 continue;
1622 }
1623 Token::RParen => {
1624 self.advance();
1625 return Ok(args);
1626 }
1627 other => {
1628 return Err(self.err(alloc::format!(
1629 "expected , or ) in function arg list, got {other:?}"
1630 )));
1631 }
1632 }
1633 }
1634 }
1635
1636 fn parse_function_return(&mut self) -> Result<FunctionReturn, ParseError> {
1637 let ident = self.expect_ident_like()?;
1638 if ident.eq_ignore_ascii_case("trigger") {
1639 return Ok(FunctionReturn::Trigger);
1640 }
1641 if ident.eq_ignore_ascii_case("void") {
1642 return Ok(FunctionReturn::Void);
1643 }
1644 match map_type_ident_to_column_type_name(&ident) {
1645 Some(t) => Ok(FunctionReturn::Type(t)),
1646 None => Ok(FunctionReturn::Other(ident)),
1647 }
1648 }
1649
1650 fn parse_optional_language(&mut self) -> Result<Option<String>, ParseError> {
1651 match self.peek() {
1652 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("language") => {
1653 self.advance();
1654 let lang = self.expect_ident_like()?;
1655 Ok(Some(lang.to_ascii_lowercase()))
1656 }
1657 _ => Ok(None),
1658 }
1659 }
1660
1661 fn parse_create_domain_after_keyword(&mut self) -> Result<Statement, ParseError> {
1667 let name = self.expect_ident_like()?;
1668 if matches!(self.peek(), Token::As) {
1670 self.advance();
1671 }
1672 let base_type = self.parse_column_type_name()?;
1673 let mut default: Option<Expr> = None;
1674 let mut not_null = false;
1675 let mut checks: Vec<Expr> = Vec::new();
1676 loop {
1677 match self.peek() {
1678 Token::Default => {
1679 if default.is_some() {
1680 return Err(self.err("DOMAIN DEFAULT specified twice".into()));
1681 }
1682 self.advance();
1683 default = Some(self.parse_expr(0)?);
1684 }
1685 Token::Not => {
1686 self.advance();
1687 if !matches!(self.peek(), Token::Null) {
1688 return Err(self.err(alloc::format!(
1689 "expected NULL after NOT in DOMAIN, got {:?}",
1690 self.peek()
1691 )));
1692 }
1693 self.advance();
1694 not_null = true;
1695 }
1696 Token::Null => {
1697 self.advance();
1698 }
1702 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("check") => {
1703 self.advance();
1704 if !matches!(self.peek(), Token::LParen) {
1705 return Err(self.err(alloc::format!(
1706 "expected '(' after CHECK in DOMAIN, got {:?}",
1707 self.peek()
1708 )));
1709 }
1710 self.advance();
1711 let expr = self.parse_expr(0)?;
1712 if !matches!(self.peek(), Token::RParen) {
1713 return Err(self.err(alloc::format!(
1714 "expected ')' after CHECK expr, got {:?}",
1715 self.peek()
1716 )));
1717 }
1718 self.advance();
1719 checks.push(expr);
1720 }
1721 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("constraint") => {
1725 self.advance();
1726 let _ = self.expect_ident_like()?;
1727 }
1728 _ => break,
1729 }
1730 }
1731 Ok(Statement::CreateDomain(crate::ast::CreateDomainStatement {
1732 name,
1733 base_type,
1734 default,
1735 not_null,
1736 checks,
1737 }))
1738 }
1739
1740 fn parse_create_type_after_keyword(&mut self) -> Result<Statement, ParseError> {
1744 let name = self.expect_ident_like()?;
1745 if !matches!(self.peek(), Token::As) {
1747 return Err(self.err(alloc::format!(
1748 "expected AS after CREATE TYPE {name:?}, got {:?}",
1749 self.peek()
1750 )));
1751 }
1752 self.advance();
1753 let kind_ident = match self.peek().clone() {
1755 Token::Ident(s) | Token::QuotedIdent(s) => s,
1756 other => {
1757 return Err(self.err(alloc::format!(
1758 "expected ENUM after CREATE TYPE {name:?} AS, got {other:?}"
1759 )));
1760 }
1761 };
1762 if !kind_ident.eq_ignore_ascii_case("enum") {
1763 return Err(self.err(alloc::format!(
1764 "Phase 1.4 only supports ENUM; got {kind_ident:?}"
1765 )));
1766 }
1767 self.advance();
1768 if !matches!(self.peek(), Token::LParen) {
1769 return Err(self.err(alloc::format!(
1770 "expected '(' after ENUM, got {:?}",
1771 self.peek()
1772 )));
1773 }
1774 self.advance();
1775 let mut labels: Vec<String> = Vec::new();
1776 loop {
1777 match self.peek().clone() {
1778 Token::String(s) => {
1779 self.advance();
1780 labels.push(s);
1781 }
1782 other => {
1783 return Err(
1784 self.err(alloc::format!("expected enum label string, got {other:?}"))
1785 );
1786 }
1787 }
1788 if matches!(self.peek(), Token::Comma) {
1789 self.advance();
1790 continue;
1791 }
1792 if matches!(self.peek(), Token::RParen) {
1793 self.advance();
1794 break;
1795 }
1796 return Err(self.err(alloc::format!(
1797 "expected , or ) in ENUM label list, got {:?}",
1798 self.peek()
1799 )));
1800 }
1801 if labels.is_empty() {
1802 return Err(self.err("CREATE TYPE … AS ENUM must declare at least one label".into()));
1803 }
1804 Ok(Statement::CreateType(crate::ast::CreateTypeStatement {
1805 name,
1806 kind: crate::ast::TypeKind::Enum { labels },
1807 }))
1808 }
1809
1810 fn parse_create_materialized_view_after_keyword(&mut self) -> Result<Statement, ParseError> {
1815 let if_not_exists = self.parse_if_not_exists();
1816 let name = self.expect_ident_like()?;
1817 let mut columns: Vec<String> = Vec::new();
1818 if matches!(self.peek(), Token::LParen) {
1819 self.advance();
1820 loop {
1821 let c = self.expect_ident_like()?;
1822 columns.push(c);
1823 if matches!(self.peek(), Token::Comma) {
1824 self.advance();
1825 continue;
1826 }
1827 if matches!(self.peek(), Token::RParen) {
1828 self.advance();
1829 break;
1830 }
1831 return Err(self.err(alloc::format!(
1832 "expected , or ) in MATERIALIZED VIEW column list, got {:?}",
1833 self.peek()
1834 )));
1835 }
1836 }
1837 if !matches!(self.peek(), Token::As) {
1838 return Err(self.err(alloc::format!(
1839 "expected AS <SELECT …> after CREATE MATERIALIZED VIEW {name:?}, got {:?}",
1840 self.peek()
1841 )));
1842 }
1843 self.advance();
1844 let body_stmt = self.parse_select_stmt()?;
1845 let Statement::Select(body) = body_stmt else {
1846 return Err(self.err(alloc::format!(
1847 "CREATE MATERIALIZED VIEW body must be a SELECT, got {body_stmt:?}"
1848 )));
1849 };
1850 let with_data = self.parse_optional_with_data(true)?;
1852 Ok(Statement::CreateMaterializedView(
1853 crate::ast::CreateMaterializedViewStatement {
1854 name,
1855 if_not_exists,
1856 columns,
1857 body,
1858 with_data,
1859 },
1860 ))
1861 }
1862
1863 fn parse_optional_with_data(&mut self, default_when_absent: bool) -> Result<bool, ParseError> {
1868 let save = self.pos;
1869 let is_with = match self.peek() {
1871 Token::Ident(s) | Token::QuotedIdent(s) => s.eq_ignore_ascii_case("with"),
1872 _ => false,
1873 };
1874 if !is_with {
1875 return Ok(default_when_absent);
1876 }
1877 self.advance();
1878 let mut with_data = true;
1880 let is_no = match self.peek() {
1881 Token::Ident(s) | Token::QuotedIdent(s) => s.eq_ignore_ascii_case("no"),
1882 _ => false,
1883 };
1884 if is_no {
1885 self.advance();
1886 with_data = false;
1887 }
1888 let is_data = match self.peek() {
1890 Token::Ident(s) | Token::QuotedIdent(s) => s.eq_ignore_ascii_case("data"),
1891 _ => false,
1892 };
1893 if is_data {
1894 self.advance();
1895 Ok(with_data)
1896 } else {
1897 self.pos = save;
1900 Ok(default_when_absent)
1901 }
1902 }
1903
1904 fn parse_create_view_after_keyword(
1909 &mut self,
1910 or_replace: bool,
1911 _materialized_unused: bool,
1912 temporary: bool,
1913 ) -> Result<Statement, ParseError> {
1914 let if_not_exists = self.parse_if_not_exists();
1915 let name = self.expect_ident_like()?;
1916 let mut columns: Vec<String> = Vec::new();
1918 if matches!(self.peek(), Token::LParen) {
1919 self.advance();
1920 loop {
1921 let c = self.expect_ident_like()?;
1922 columns.push(c);
1923 if matches!(self.peek(), Token::Comma) {
1924 self.advance();
1925 continue;
1926 }
1927 if matches!(self.peek(), Token::RParen) {
1928 self.advance();
1929 break;
1930 }
1931 return Err(self.err(alloc::format!(
1932 "expected , or ) in VIEW column list, got {:?}",
1933 self.peek()
1934 )));
1935 }
1936 }
1937 if !matches!(self.peek(), Token::As) {
1939 return Err(self.err(alloc::format!(
1940 "expected AS <SELECT …> after CREATE VIEW {name:?}, got {:?}",
1941 self.peek()
1942 )));
1943 }
1944 self.advance();
1945 let body_stmt = self.parse_select_stmt()?;
1947 let Statement::Select(body) = body_stmt else {
1948 return Err(self.err(alloc::format!(
1949 "CREATE VIEW body must be a SELECT statement, got {body_stmt:?}"
1950 )));
1951 };
1952 Ok(Statement::CreateView(crate::ast::CreateViewStatement {
1953 name,
1954 or_replace,
1955 if_not_exists,
1956 temporary,
1957 columns,
1958 body,
1959 }))
1960 }
1961
1962 fn parse_create_sequence_after_keyword(
1966 &mut self,
1967 temporary: bool,
1968 ) -> Result<Statement, ParseError> {
1969 let if_not_exists = self.parse_if_not_exists();
1970 let name = self.expect_ident_like()?;
1971 let data_type = if matches!(self.peek(), Token::As) {
1973 self.advance();
1974 Some(self.parse_sequence_data_type()?)
1975 } else {
1976 None
1977 };
1978 let options = self.parse_sequence_options(false)?;
1979 Ok(Statement::CreateSequence(
1980 crate::ast::CreateSequenceStatement {
1981 name,
1982 if_not_exists,
1983 temporary,
1984 data_type,
1985 options,
1986 },
1987 ))
1988 }
1989
1990 fn parse_alter_sequence_after_keyword(&mut self) -> Result<Statement, ParseError> {
1993 let if_exists = self.parse_if_exists();
1994 let name = self.expect_ident_like()?;
1995 let options = self.parse_sequence_options(true)?;
1996 Ok(Statement::AlterSequence(
1997 crate::ast::AlterSequenceStatement {
1998 name,
1999 if_exists,
2000 options,
2001 },
2002 ))
2003 }
2004
2005 fn parse_sequence_data_type(&mut self) -> Result<crate::ast::SequenceDataType, ParseError> {
2006 let kw = self.expect_ident_like()?;
2007 match kw.to_ascii_lowercase().as_str() {
2008 "smallint" | "int2" => Ok(crate::ast::SequenceDataType::SmallInt),
2009 "integer" | "int" | "int4" => Ok(crate::ast::SequenceDataType::Int),
2010 "bigint" | "int8" => Ok(crate::ast::SequenceDataType::BigInt),
2011 other => Err(self.err(alloc::format!(
2012 "expected SMALLINT / INTEGER / BIGINT after SEQUENCE AS, got {other:?}"
2013 ))),
2014 }
2015 }
2016
2017 fn parse_sequence_options(
2018 &mut self,
2019 allow_restart: bool,
2020 ) -> Result<crate::ast::SequenceOptions, ParseError> {
2021 use crate::ast::{SeqBound, SequenceOptions, SequenceOwnedBy};
2022 let mut opts = SequenceOptions::default();
2023 #[allow(clippy::while_let_loop)]
2024 loop {
2025 let kw_lc = match self.peek() {
2028 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
2029 _ => break,
2030 };
2031 match kw_lc.as_str() {
2032 "increment" => {
2033 self.advance();
2034 if matches!(self.peek(), Token::By) {
2036 self.advance();
2037 }
2038 opts.increment = Some(self.expect_signed_int()?);
2039 }
2040 "minvalue" => {
2041 self.advance();
2042 opts.min_value = Some(SeqBound::Value(self.expect_signed_int()?));
2043 }
2044 "maxvalue" => {
2045 self.advance();
2046 opts.max_value = Some(SeqBound::Value(self.expect_signed_int()?));
2047 }
2048 "no" => {
2049 self.advance();
2050 let what = self.expect_ident_like()?;
2051 match what.to_ascii_lowercase().as_str() {
2052 "minvalue" => opts.min_value = Some(SeqBound::NoBound),
2053 "maxvalue" => opts.max_value = Some(SeqBound::NoBound),
2054 "cycle" => opts.cycle = Some(false),
2055 other => {
2056 return Err(self.err(alloc::format!(
2057 "expected MINVALUE / MAXVALUE / CYCLE after NO, got {other:?}"
2058 )));
2059 }
2060 }
2061 }
2062 "start" => {
2063 self.advance();
2064 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
2066 if s.eq_ignore_ascii_case("with"))
2067 {
2068 self.advance();
2069 }
2070 opts.start = Some(self.expect_signed_int()?);
2071 }
2072 "restart" if allow_restart => {
2073 self.advance();
2074 let mut with_val: Option<i64> = None;
2076 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
2077 if s.eq_ignore_ascii_case("with"))
2078 {
2079 self.advance();
2080 with_val = Some(self.expect_signed_int()?);
2081 } else if matches!(self.peek(), Token::Integer(_) | Token::Minus) {
2082 with_val = Some(self.expect_signed_int()?);
2083 }
2084 opts.restart = Some(with_val);
2085 }
2086 "cache" => {
2087 self.advance();
2088 opts.cache = Some(self.expect_signed_int()?);
2089 }
2090 "cycle" => {
2091 self.advance();
2092 opts.cycle = Some(true);
2093 }
2094 "owned" => {
2095 self.advance();
2096 match self.peek() {
2098 Token::By => {
2099 self.advance();
2100 }
2101 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("by") => {
2102 self.advance();
2103 }
2104 other => {
2105 return Err(
2106 self.err(alloc::format!("expected BY after OWNED, got {other:?}"))
2107 );
2108 }
2109 }
2110 let first = match self.advance() {
2114 Token::Ident(s) | Token::QuotedIdent(s) => s,
2115 other => {
2116 return Err(self.err(alloc::format!(
2117 "expected identifier or NONE after OWNED BY, got {other:?}"
2118 )));
2119 }
2120 };
2121 if first.eq_ignore_ascii_case("none") {
2122 opts.owned_by = Some(SequenceOwnedBy::None);
2123 } else if matches!(self.peek(), Token::Dot) {
2124 self.advance();
2125 let second = match self.advance() {
2126 Token::Ident(s) | Token::QuotedIdent(s) => s,
2127 other => {
2128 return Err(self.err(alloc::format!(
2129 "expected column name after OWNED BY {first}., got {other:?}"
2130 )));
2131 }
2132 };
2133 if matches!(self.peek(), Token::Dot) {
2142 self.advance();
2143 let third = match self.advance() {
2144 Token::Ident(s) | Token::QuotedIdent(s) => s,
2145 other => {
2146 return Err(self.err(alloc::format!(
2147 "expected column name after OWNED BY {first}.{second}., got {other:?}"
2148 )));
2149 }
2150 };
2151 let _ = first; opts.owned_by = Some(SequenceOwnedBy::Column {
2153 table: second,
2154 column: third,
2155 });
2156 } else {
2157 opts.owned_by = Some(SequenceOwnedBy::Column {
2158 table: first,
2159 column: second,
2160 });
2161 }
2162 } else {
2163 return Err(self.err(alloc::format!(
2164 "expected table.column or NONE after OWNED BY, got {first:?}"
2165 )));
2166 }
2167 }
2168 _ => break,
2169 }
2170 }
2171 Ok(opts)
2172 }
2173
2174 fn expect_signed_int(&mut self) -> Result<i64, ParseError> {
2175 let neg = if matches!(self.peek(), Token::Minus) {
2176 self.advance();
2177 true
2178 } else {
2179 false
2180 };
2181 match self.peek() {
2182 Token::Integer(n) => {
2183 let v = *n;
2184 self.advance();
2185 Ok(if neg { -v } else { v })
2186 }
2187 other => Err(self.err(alloc::format!("expected signed integer, got {other:?}"))),
2188 }
2189 }
2190
2191 fn consume_optional_deferrable_clauses(&mut self) -> Result<(), ParseError> {
2201 loop {
2202 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("deferrable")) {
2204 self.advance();
2205 self.consume_optional_initially_clause()?;
2206 continue;
2207 }
2208 if matches!(self.peek(), Token::Not) {
2210 let look = self.tokens.get(self.pos + 1);
2211 if matches!(look, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("deferrable")) {
2212 self.advance(); self.advance(); self.consume_optional_initially_clause()?;
2215 continue;
2216 }
2217 break;
2218 }
2219 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("initially")) {
2224 self.consume_optional_initially_clause()?;
2225 continue;
2226 }
2227 break;
2228 }
2229 Ok(())
2230 }
2231
2232 fn consume_optional_initially_clause(&mut self) -> Result<(), ParseError> {
2236 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("initially")) {
2237 return Ok(());
2238 }
2239 self.advance(); match self.advance() {
2241 Token::Ident(s)
2242 if s.eq_ignore_ascii_case("deferred") || s.eq_ignore_ascii_case("immediate") =>
2243 {
2244 Ok(())
2245 }
2246 other => Err(self.err(alloc::format!(
2247 "expected DEFERRED or IMMEDIATE after INITIALLY, got {other:?}"
2248 ))),
2249 }
2250 }
2251
2252 fn consume_mysql_routine_body(&mut self) {
2270 let mut depth: i32 = 0;
2275 let mut started = false;
2276 loop {
2277 match self.peek().clone() {
2278 Token::Begin => {
2279 self.advance();
2280 depth += 1;
2281 started = true;
2282 }
2283 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("end") => {
2284 self.advance();
2285 if started {
2286 depth -= 1;
2287 if depth <= 0 {
2288 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
2295 let is_inner_close = matches!(
2300 self.peek(),
2301 Token::Ident(s) | Token::QuotedIdent(s)
2302 if matches!(
2303 s.to_ascii_lowercase().as_str(),
2304 "if" | "loop" | "while" | "case" | "repeat"
2305 )
2306 );
2307 if is_inner_close {
2308 self.advance();
2309 depth += 1;
2310 continue;
2311 }
2312 }
2313 if matches!(self.peek(), Token::Semicolon) {
2315 self.advance();
2316 }
2317 return;
2318 }
2319 }
2320 }
2321 Token::Eof => return,
2322 _ => {
2323 self.advance();
2324 }
2325 }
2326 }
2327 }
2328
2329 fn consume_mysql_view_prefix(&mut self) -> Result<(), ParseError> {
2343 loop {
2344 match self.peek().clone() {
2345 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("algorithm") => {
2346 self.advance(); if matches!(self.peek(), Token::Eq) {
2350 self.advance();
2351 }
2352 if matches!(
2356 self.peek(),
2357 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
2358 ) {
2359 self.advance();
2360 }
2361 }
2362 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("definer") => {
2363 self.advance(); if matches!(self.peek(), Token::Eq) {
2365 self.advance();
2366 }
2367 match self.peek().clone() {
2370 Token::String(_) | Token::Ident(_) | Token::QuotedIdent(_) => {
2371 self.advance();
2372 if matches!(self.peek(), Token::At) {
2374 self.advance();
2375 if matches!(
2376 self.peek(),
2377 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
2378 ) {
2379 self.advance();
2380 }
2381 }
2382 }
2383 _ => {}
2384 }
2385 }
2386 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sql") => {
2387 let save = self.pos;
2392 self.advance(); if matches!(self.peek(), Token::Ident(s2) | Token::QuotedIdent(s2)
2394 if s2.eq_ignore_ascii_case("security"))
2395 {
2396 self.advance(); if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
2399 self.advance();
2400 }
2401 } else {
2402 self.pos = save;
2405 return Ok(());
2406 }
2407 }
2408 _ => return Ok(()),
2409 }
2410 }
2411 }
2412
2413 fn parse_if_not_exists(&mut self) -> bool {
2414 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("if"))
2415 {
2416 let save = self.pos;
2417 self.advance();
2418 if matches!(self.peek(), Token::Not) {
2419 self.advance();
2420 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists"))
2421 {
2422 self.advance();
2423 return true;
2424 }
2425 }
2426 self.pos = save;
2427 }
2428 false
2429 }
2430
2431 fn parse_if_exists(&mut self) -> bool {
2432 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("if"))
2433 {
2434 let save = self.pos;
2435 self.advance();
2436 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists"))
2437 {
2438 self.advance();
2439 return true;
2440 }
2441 self.pos = save;
2442 }
2443 false
2444 }
2445
2446 fn parse_create_trigger_after_keyword(
2450 &mut self,
2451 or_replace: bool,
2452 ) -> Result<Statement, ParseError> {
2453 let name = self.expect_ident_like()?;
2454 let timing = {
2455 let ident = self.expect_ident_like()?;
2456 if ident.eq_ignore_ascii_case("before") {
2457 TriggerTiming::Before
2458 } else if ident.eq_ignore_ascii_case("after") {
2459 TriggerTiming::After
2460 } else if ident.eq_ignore_ascii_case("instead") {
2461 let next = self.expect_ident_like()?;
2462 if !next.eq_ignore_ascii_case("of") {
2463 return Err(self.err(alloc::format!(
2464 "expected OF after INSTEAD in trigger timing, got {next:?}"
2465 )));
2466 }
2467 TriggerTiming::InsteadOf
2468 } else {
2469 return Err(self.err(alloc::format!(
2470 "expected BEFORE / AFTER / INSTEAD OF in trigger timing, got {ident:?}"
2471 )));
2472 }
2473 };
2474 let mut events: Vec<TriggerEvent> = Vec::new();
2481 let mut update_columns: Vec<String> = Vec::new();
2482 let (first_ev, first_cols) = self.parse_trigger_event_with_optional_of()?;
2483 events.push(first_ev);
2484 if !first_cols.is_empty() {
2485 update_columns = first_cols;
2486 }
2487 while matches!(self.peek(), Token::Or) {
2488 self.advance();
2489 let (ev, cols) = self.parse_trigger_event_with_optional_of()?;
2490 events.push(ev);
2491 if !cols.is_empty() {
2492 if !update_columns.is_empty() {
2493 return Err(
2494 self.err("CREATE TRIGGER: `UPDATE OF cols` may appear at most once".into())
2495 );
2496 }
2497 update_columns = cols;
2498 }
2499 }
2500 let tok = self.peek();
2502 let Token::On = tok else {
2503 return Err(self.err(alloc::format!(
2504 "expected ON after trigger events, got {tok:?}"
2505 )));
2506 };
2507 self.advance();
2508 let table = self.expect_ident_like()?;
2509 if !matches!(self.peek(), Token::For) {
2513 return Err(self.err(alloc::format!(
2514 "expected FOR EACH ROW / STATEMENT, got {:?}",
2515 self.peek()
2516 )));
2517 }
2518 self.advance();
2519 let for_each = {
2520 let e = self.expect_ident_like()?;
2521 if !e.eq_ignore_ascii_case("each") {
2522 return Err(self.err(alloc::format!("expected EACH after FOR, got {e:?}")));
2523 }
2524 let unit = self.expect_ident_like()?;
2525 if unit.eq_ignore_ascii_case("row") {
2526 TriggerForEach::Row
2527 } else if unit.eq_ignore_ascii_case("statement") {
2528 TriggerForEach::Statement
2529 } else {
2530 return Err(self.err(alloc::format!(
2531 "expected ROW / STATEMENT after FOR EACH, got {unit:?}"
2532 )));
2533 }
2534 };
2535 let exec = self.expect_ident_like()?;
2537 if !exec.eq_ignore_ascii_case("execute") {
2538 return Err(self.err(alloc::format!(
2539 "expected EXECUTE FUNCTION/PROCEDURE in CREATE TRIGGER, got {exec:?}"
2540 )));
2541 }
2542 let fn_or_proc = self.expect_ident_like()?;
2543 if !(fn_or_proc.eq_ignore_ascii_case("function")
2544 || fn_or_proc.eq_ignore_ascii_case("procedure"))
2545 {
2546 return Err(self.err(alloc::format!(
2547 "expected FUNCTION / PROCEDURE after EXECUTE, got {fn_or_proc:?}"
2548 )));
2549 }
2550 let function = self.expect_ident_like()?;
2551 if matches!(self.peek(), Token::LParen) {
2553 self.advance();
2554 if !matches!(self.peek(), Token::RParen) {
2555 return Err(self.err(alloc::format!(
2556 "v7.12.4 trigger function calls take no args; got {:?}",
2557 self.peek()
2558 )));
2559 }
2560 self.advance();
2561 }
2562 Ok(Statement::CreateTrigger(CreateTriggerStatement {
2563 name,
2564 or_replace,
2565 timing,
2566 events,
2567 table,
2568 for_each,
2569 function,
2570 update_columns,
2571 }))
2572 }
2573
2574 fn parse_trigger_event_with_optional_of(
2578 &mut self,
2579 ) -> Result<(TriggerEvent, Vec<String>), ParseError> {
2580 let ev = self.parse_trigger_event()?;
2581 if !matches!(ev, TriggerEvent::Update) {
2582 return Ok((ev, Vec::new()));
2583 }
2584 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("of")) {
2586 return Ok((ev, Vec::new()));
2587 }
2588 self.advance(); let mut cols: Vec<String> = Vec::new();
2590 loop {
2591 cols.push(self.expect_ident_like()?);
2592 if matches!(self.peek(), Token::Comma) {
2593 self.advance();
2594 continue;
2595 }
2596 break;
2597 }
2598 if cols.is_empty() {
2599 return Err(
2600 self.err("CREATE TRIGGER: `UPDATE OF` requires at least one column name".into())
2601 );
2602 }
2603 Ok((ev, cols))
2604 }
2605
2606 pub(crate) fn parse_plpgsql_block(&mut self) -> Result<PlPgSqlBlock, ParseError> {
2613 let declarations = if matches!(
2615 self.peek(),
2616 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("declare")
2617 ) {
2618 self.advance();
2619 self.parse_plpgsql_declare_block()?
2620 } else {
2621 Vec::new()
2622 };
2623 if !matches!(self.peek(), Token::Begin) {
2628 return Err(self.err(alloc::format!(
2629 "expected BEGIN at start of plpgsql block, got {:?}",
2630 self.peek()
2631 )));
2632 }
2633 self.advance();
2634 let statements = self.parse_plpgsql_stmt_list_until_end()?;
2635 Ok(PlPgSqlBlock {
2636 declarations,
2637 statements,
2638 })
2639 }
2640
2641 fn parse_plpgsql_declare_block(&mut self) -> Result<Vec<PlPgSqlDeclare>, ParseError> {
2645 let mut out: Vec<PlPgSqlDeclare> = Vec::new();
2646 loop {
2647 if matches!(self.peek(), Token::Begin) {
2648 return Ok(out);
2649 }
2650 let name = self.expect_ident_like()?;
2651 let ty_token = self.expect_ident_like()?;
2652 let ty = match map_type_ident_to_column_type_name(&ty_token) {
2653 Some(t) => FunctionArgType::Typed(t),
2654 None => FunctionArgType::Raw(ty_token),
2655 };
2656 let default = match self.peek() {
2657 Token::ColonEq => {
2658 self.advance();
2659 Some(self.parse_expr(0)?)
2660 }
2661 Token::Eq => {
2662 self.advance();
2666 Some(self.parse_expr(0)?)
2667 }
2668 _ => None,
2669 };
2670 if !matches!(self.peek(), Token::Semicolon) {
2672 return Err(self.err(alloc::format!(
2673 "expected ; after DECLARE entry for {name:?}, got {:?}",
2674 self.peek()
2675 )));
2676 }
2677 self.advance();
2678 out.push(PlPgSqlDeclare { name, ty, default });
2679 }
2680 }
2681
2682 fn parse_plpgsql_stmt_list_until_end(&mut self) -> Result<Vec<PlPgSqlStmt>, ParseError> {
2687 let mut statements: Vec<PlPgSqlStmt> = Vec::new();
2688 loop {
2689 while matches!(self.peek(), Token::Semicolon) {
2691 self.advance();
2692 }
2693 if matches!(
2695 self.peek(),
2696 Token::Ident(s) | Token::QuotedIdent(s)
2697 if s.eq_ignore_ascii_case("end")
2698 || s.eq_ignore_ascii_case("else")
2699 || s.eq_ignore_ascii_case("elsif")
2700 || s.eq_ignore_ascii_case("elseif")
2701 ) {
2702 return Ok(statements);
2703 }
2704 let stmt = self.parse_plpgsql_stmt()?;
2707 statements.push(stmt);
2708 match self.peek() {
2709 Token::Semicolon => {
2710 self.advance();
2711 }
2712 Token::Ident(s) | Token::QuotedIdent(s)
2713 if s.eq_ignore_ascii_case("end")
2714 || s.eq_ignore_ascii_case("else")
2715 || s.eq_ignore_ascii_case("elsif")
2716 || s.eq_ignore_ascii_case("elseif") =>
2717 {
2718 }
2720 other => {
2721 return Err(self.err(alloc::format!(
2722 "expected ; or END/ELSE/ELSIF after plpgsql statement, got {other:?}"
2723 )));
2724 }
2725 }
2726 }
2727 }
2728
2729 fn parse_plpgsql_stmt(&mut self) -> Result<PlPgSqlStmt, ParseError> {
2730 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("return"))
2732 {
2733 self.advance();
2734 return self.parse_plpgsql_return();
2735 }
2736 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("if"))
2738 {
2739 self.advance();
2740 return self.parse_plpgsql_if();
2741 }
2742 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("raise"))
2744 {
2745 self.advance();
2746 return self.parse_plpgsql_raise();
2747 }
2748 if matches!(self.peek(), Token::Select)
2759 && let Some((select_body, var_name)) = self.try_parse_plpgsql_select_into()?
2760 {
2761 return Ok(PlPgSqlStmt::SelectInto {
2762 var: var_name,
2763 body: Box::new(select_body),
2764 });
2765 }
2766 if matches!(self.peek(), Token::Insert)
2776 || matches!(self.peek(), Token::Select)
2777 || matches!(self.peek(), Token::Create)
2778 || matches!(self.peek(), Token::Drop)
2779 || matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
2780 if s.eq_ignore_ascii_case("update")
2781 || s.eq_ignore_ascii_case("delete")
2782 || s.eq_ignore_ascii_case("alter"))
2783 {
2784 let stmt = self.parse_one_statement()?;
2785 return Ok(PlPgSqlStmt::EmbeddedSql(Box::new(stmt)));
2786 }
2787 let target = self.parse_plpgsql_assign_target()?;
2790 match self.peek() {
2793 Token::ColonEq => {
2794 self.advance();
2795 }
2796 Token::Colon => {
2797 self.advance();
2798 if !matches!(self.peek(), Token::Eq) {
2799 return Err(self.err(alloc::format!(
2800 "expected := after plpgsql assign target, got `:` then {:?}",
2801 self.peek()
2802 )));
2803 }
2804 self.advance();
2805 }
2806 other => {
2807 return Err(self.err(alloc::format!(
2808 "expected := after plpgsql assign target, got {other:?}"
2809 )));
2810 }
2811 }
2812 let value = self.parse_expr(0)?;
2813 Ok(PlPgSqlStmt::Assign { target, value })
2814 }
2815
2816 fn parse_plpgsql_if(&mut self) -> Result<PlPgSqlStmt, ParseError> {
2819 let mut branches: Vec<(Expr, Vec<PlPgSqlStmt>)> = Vec::new();
2820 let mut else_branch: Vec<PlPgSqlStmt> = Vec::new();
2821 loop {
2822 let cond = self.parse_expr(0)?;
2824 let then_kw = self.expect_ident_like()?;
2825 if !then_kw.eq_ignore_ascii_case("then") {
2826 return Err(self.err(alloc::format!(
2827 "expected THEN after IF/ELSIF condition, got {then_kw:?}"
2828 )));
2829 }
2830 let body = self.parse_plpgsql_stmt_list_until_end()?;
2831 branches.push((cond, body));
2832 match self.peek() {
2834 Token::Ident(s) | Token::QuotedIdent(s)
2835 if s.eq_ignore_ascii_case("elsif") || s.eq_ignore_ascii_case("elseif") =>
2836 {
2837 self.advance();
2838 continue;
2839 }
2840 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("else") => {
2841 self.advance();
2842 else_branch = self.parse_plpgsql_stmt_list_until_end()?;
2843 break;
2844 }
2845 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("end") => {
2846 break;
2847 }
2848 other => {
2849 return Err(self.err(alloc::format!(
2850 "expected ELSIF / ELSE / END after IF branch body, got {other:?}"
2851 )));
2852 }
2853 }
2854 }
2855 let end_kw = self.expect_ident_like()?;
2858 if !end_kw.eq_ignore_ascii_case("end") {
2859 return Err(self.err(alloc::format!("expected END IF, got {end_kw:?}")));
2860 }
2861 let if_kw = self.expect_ident_like()?;
2862 if !if_kw.eq_ignore_ascii_case("if") {
2863 return Err(self.err(alloc::format!("expected END IF, got END {if_kw:?}")));
2864 }
2865 Ok(PlPgSqlStmt::If {
2866 branches,
2867 else_branch,
2868 })
2869 }
2870
2871 fn parse_plpgsql_raise(&mut self) -> Result<PlPgSqlStmt, ParseError> {
2875 let lvl_ident = self.expect_ident_like()?;
2876 let level = match lvl_ident.to_ascii_lowercase().as_str() {
2877 "notice" => RaiseLevel::Notice,
2878 "warning" => RaiseLevel::Warning,
2879 "info" => RaiseLevel::Info,
2880 "log" => RaiseLevel::Log,
2881 "debug" => RaiseLevel::Debug,
2882 "exception" => RaiseLevel::Exception,
2883 other => {
2884 return Err(self.err(alloc::format!(
2885 "expected RAISE level (NOTICE/WARNING/INFO/LOG/DEBUG/EXCEPTION), got {other:?}"
2886 )));
2887 }
2888 };
2889 let Token::String(msg) = self.peek() else {
2893 return Err(self.err(alloc::format!(
2894 "expected RAISE message string, got {:?}",
2895 self.peek()
2896 )));
2897 };
2898 let message = msg.clone();
2899 self.advance();
2900 let mut args: Vec<Expr> = Vec::new();
2902 while matches!(self.peek(), Token::Comma) {
2903 self.advance();
2904 args.push(self.parse_expr(0)?);
2905 }
2906 Ok(PlPgSqlStmt::Raise {
2907 level,
2908 message,
2909 args,
2910 })
2911 }
2912
2913 #[allow(clippy::too_many_lines)]
2921 fn try_parse_plpgsql_select_into(
2922 &mut self,
2923 ) -> Result<Option<(SelectStatement, String)>, ParseError> {
2924 let start = self.pos;
2929 let mut into_pos: Option<usize> = None;
2930 let mut depth: i32 = 0;
2931 let mut i = start + 1;
2932 while i < self.tokens.len() {
2933 match &self.tokens[i] {
2934 Token::LParen => depth += 1,
2935 Token::RParen => depth -= 1,
2936 Token::Semicolon if depth == 0 => break,
2937 Token::Ident(s)
2938 if depth == 0
2939 && (s.eq_ignore_ascii_case("end")
2940 || s.eq_ignore_ascii_case("else")
2941 || s.eq_ignore_ascii_case("elsif")) =>
2942 {
2943 break;
2944 }
2945 Token::Into if depth == 0 => {
2946 into_pos = Some(i);
2947 break;
2948 }
2949 _ => {}
2950 }
2951 i += 1;
2952 }
2953 let Some(into_at) = into_pos else {
2954 return Ok(None);
2955 };
2956 let var = match self.tokens.get(into_at + 1) {
2960 Some(Token::Ident(s) | Token::QuotedIdent(s)) => s.clone(),
2961 other => {
2962 return Err(self.err(alloc::format!(
2963 "expected variable name after SELECT … INTO, got {other:?}"
2964 )));
2965 }
2966 };
2967 let mut end = into_at + 2;
2970 let mut depth2: i32 = 0;
2971 while end < self.tokens.len() {
2972 match &self.tokens[end] {
2973 Token::LParen => depth2 += 1,
2974 Token::RParen => depth2 -= 1,
2975 Token::Semicolon if depth2 == 0 => break,
2976 Token::Ident(s)
2977 if depth2 == 0
2978 && (s.eq_ignore_ascii_case("end")
2979 || s.eq_ignore_ascii_case("else")
2980 || s.eq_ignore_ascii_case("elsif")) =>
2981 {
2982 break;
2983 }
2984 _ => {}
2985 }
2986 end += 1;
2987 }
2988 let mut rebuilt: Vec<Token> = Vec::with_capacity(end - start);
2993 for j in start..into_at {
2994 rebuilt.push(self.tokens[j].clone());
2995 }
2996 for j in (into_at + 2)..end {
2997 rebuilt.push(self.tokens[j].clone());
2998 }
2999 rebuilt.push(Token::Eof);
3000 let saved_pos = self.pos;
3001 let saved_tokens = core::mem::replace(&mut self.tokens, rebuilt);
3002 self.pos = 0;
3003 if !matches!(self.peek(), Token::Select) {
3005 self.tokens = saved_tokens;
3006 self.pos = saved_pos;
3007 return Err(self.err("plpgsql SELECT … INTO: rebuilt stream missing SELECT".into()));
3008 }
3009 let sel = self.parse_select_stmt();
3010 self.tokens = saved_tokens;
3011 self.pos = end;
3012 let sel = sel?;
3013 let Statement::Select(body) = sel else {
3014 return Err(self.err(alloc::format!(
3015 "plpgsql SELECT … INTO: rebuilt SELECT did not produce a Select node, got {sel:?}"
3016 )));
3017 };
3018 Ok(Some((body, var)))
3019 }
3020
3021 fn parse_plpgsql_assign_target(&mut self) -> Result<AssignTarget, ParseError> {
3022 let head = match self.advance() {
3036 Token::Ident(s) | Token::QuotedIdent(s) => s,
3037 other => {
3038 return Err(self.err(alloc::format!(
3039 "expected NEW / OLD / <local_var> as plpgsql assign target, got {other:?}"
3040 )));
3041 }
3042 };
3043 if matches!(self.peek(), Token::Dot) {
3044 self.advance();
3045 let col = self.expect_ident_like()?;
3046 if head.eq_ignore_ascii_case("new") {
3047 return Ok(AssignTarget::NewColumn(col));
3048 }
3049 if head.eq_ignore_ascii_case("old") {
3050 return Ok(AssignTarget::OldColumn(col));
3051 }
3052 return Err(self.err(alloc::format!(
3053 "plpgsql assign target must be NEW.<col> / OLD.<col> / <local_var>; \
3054 got {head:?}.<col>"
3055 )));
3056 }
3057 Ok(AssignTarget::Local(head))
3058 }
3059
3060 fn parse_plpgsql_return(&mut self) -> Result<PlPgSqlStmt, ParseError> {
3061 match self.peek() {
3063 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("new") => {
3064 self.advance();
3065 return Ok(PlPgSqlStmt::Return(ReturnTarget::New));
3066 }
3067 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("old") => {
3068 self.advance();
3069 return Ok(PlPgSqlStmt::Return(ReturnTarget::Old));
3070 }
3071 Token::Null => {
3072 self.advance();
3073 return Ok(PlPgSqlStmt::Return(ReturnTarget::Null));
3074 }
3075 Token::Semicolon => {
3078 return Ok(PlPgSqlStmt::Return(ReturnTarget::Null));
3079 }
3080 _ => {}
3081 }
3082 let e = self.parse_expr(0)?;
3084 Ok(PlPgSqlStmt::Return(ReturnTarget::Expr(e)))
3085 }
3086
3087 fn parse_trigger_event(&mut self) -> Result<TriggerEvent, ParseError> {
3088 if matches!(self.peek(), Token::Insert) {
3093 self.advance();
3094 return Ok(TriggerEvent::Insert);
3095 }
3096 match self.peek() {
3097 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
3098 self.advance();
3099 Ok(TriggerEvent::Update)
3100 }
3101 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
3102 self.advance();
3103 Ok(TriggerEvent::Delete)
3104 }
3105 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("truncate") => {
3106 self.advance();
3107 Ok(TriggerEvent::Truncate)
3108 }
3109 other => Err(self.err(alloc::format!(
3110 "expected INSERT / UPDATE / DELETE / TRUNCATE in trigger event list, got {other:?}"
3111 ))),
3112 }
3113 }
3114
3115 fn parse_create_publication_after_keyword(&mut self) -> Result<Statement, ParseError> {
3122 let name = self.expect_ident_or_string()?;
3123 let scope = if matches!(self.peek(), Token::For) {
3126 self.advance();
3127 if matches!(self.peek(), Token::All) {
3128 self.advance();
3129 if !matches!(self.peek(), Token::Tables) {
3130 return Err(self.err(format!(
3131 "expected TABLES after FOR ALL, got {:?}",
3132 self.peek()
3133 )));
3134 }
3135 self.advance();
3136 if matches!(self.peek(), Token::Except) {
3137 self.advance();
3138 let tables = self.parse_publication_table_list()?;
3139 PublicationScope::AllTablesExcept(tables)
3140 } else {
3141 PublicationScope::AllTables
3142 }
3143 } else if matches!(self.peek(), Token::Table | Token::Tables) {
3144 self.advance();
3147 let tables = self.parse_publication_table_list()?;
3148 PublicationScope::ForTables(tables)
3149 } else {
3150 return Err(self.err(format!(
3151 "expected ALL TABLES or TABLE <list> after FOR, got {:?}",
3152 self.peek()
3153 )));
3154 }
3155 } else {
3156 PublicationScope::AllTables
3157 };
3158 Ok(Statement::CreatePublication(CreatePublicationStatement {
3159 name,
3160 scope,
3161 }))
3162 }
3163
3164 fn parse_publication_table_list(&mut self) -> Result<Vec<String>, ParseError> {
3169 let first = self.expect_ident_like()?;
3170 let mut out = alloc::vec![first];
3171 while matches!(self.peek(), Token::Comma) {
3172 self.advance();
3173 out.push(self.expect_ident_like()?);
3174 }
3175 Ok(out)
3176 }
3177
3178 fn parse_create_subscription_after_keyword(&mut self) -> Result<Statement, ParseError> {
3186 let name = self.expect_ident_or_string()?;
3187 if !matches!(self.peek(), Token::Connection) {
3188 return Err(self.err(format!(
3189 "expected CONNECTION after CREATE SUBSCRIPTION <name>, got {:?}",
3190 self.peek()
3191 )));
3192 }
3193 self.advance();
3194 let conn_str = self.expect_string_literal()?;
3195 if !matches!(self.peek(), Token::Publication) {
3196 return Err(self.err(format!(
3197 "expected PUBLICATION after CONNECTION '<conn>', got {:?}",
3198 self.peek()
3199 )));
3200 }
3201 self.advance();
3202 let first = self.expect_ident_like()?;
3205 let mut publications = alloc::vec![first];
3206 while matches!(self.peek(), Token::Comma) {
3207 self.advance();
3208 publications.push(self.expect_ident_like()?);
3209 }
3210 Ok(Statement::CreateSubscription(CreateSubscriptionStatement {
3211 name,
3212 conn_str,
3213 publications,
3214 }))
3215 }
3216
3217 fn parse_set_param_name(&mut self) -> Result<String, ParseError> {
3224 let mut name = self.expect_ident_like()?;
3225 while matches!(self.peek(), Token::Dot) {
3226 self.advance();
3227 let next = self.expect_ident_like()?;
3228 name.push('.');
3229 name.push_str(&next);
3230 }
3231 Ok(name.to_ascii_lowercase())
3232 }
3233
3234 fn parse_set_value(&mut self) -> Result<crate::ast::SetValue, ParseError> {
3235 match self.advance() {
3236 Token::String(s) => Ok(crate::ast::SetValue::String(s)),
3237 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("default") => {
3238 Ok(crate::ast::SetValue::Default)
3239 }
3240 Token::Ident(s) | Token::QuotedIdent(s) => {
3241 let mut accum = s;
3242 while matches!(self.peek(), Token::Dot) {
3243 self.advance();
3244 let next = self.expect_ident_like()?;
3245 accum.push('.');
3246 accum.push_str(&next);
3247 }
3248 Ok(crate::ast::SetValue::Ident(accum))
3249 }
3250 Token::Integer(n) => Ok(crate::ast::SetValue::Number(n.to_string())),
3251 Token::Float(f) => Ok(crate::ast::SetValue::Number(f.to_string())),
3252 Token::On => Ok(crate::ast::SetValue::Ident("on".to_string())),
3257 Token::True => Ok(crate::ast::SetValue::Ident("true".to_string())),
3258 Token::False => Ok(crate::ast::SetValue::Ident("false".to_string())),
3259 Token::SessionVar(s) => Ok(crate::ast::SetValue::Ident(s)),
3265 Token::Minus => match self.advance() {
3270 Token::Integer(n) => Ok(crate::ast::SetValue::Number(alloc::format!("-{n}"))),
3271 Token::Float(f) => Ok(crate::ast::SetValue::Number(alloc::format!("-{f}"))),
3272 other => Err(self.err(format!(
3273 "expected numeric after `-` in SET value, got {other:?}"
3274 ))),
3275 },
3276 other => Err(self.err(format!(
3277 "expected literal, identifier, or DEFAULT after `=` in SET, got {other:?}"
3278 ))),
3279 }
3280 }
3281
3282 fn parse_wait_after_keyword(&mut self) -> Result<Statement, ParseError> {
3283 if !matches!(self.peek(), Token::For) {
3287 return Err(self.err(format!("expected FOR after WAIT, got {:?}", self.peek())));
3288 }
3289 self.advance();
3290 self.expect_keyword_ident("wal")?;
3291 self.expect_keyword_ident("position")?;
3292 let pos = self.expect_u64_literal()?;
3293 let timeout_ms = if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with"))
3294 {
3295 self.advance();
3296 self.expect_keyword_ident("timeout")?;
3297 Some(self.expect_u64_literal()?)
3298 } else {
3299 None
3300 };
3301 Ok(Statement::WaitForWalPosition { pos, timeout_ms })
3302 }
3303
3304 fn expect_u64_literal(&mut self) -> Result<u64, ParseError> {
3308 match self.advance() {
3309 Token::Integer(n) if n >= 0 => Ok(n as u64),
3310 Token::Integer(n) => Err(ParseError {
3311 message: format!("expected non-negative integer, got {n}"),
3312 token_pos: self.pos.saturating_sub(1),
3313 }),
3314 other => Err(ParseError {
3315 message: format!("expected integer literal, got {other:?}"),
3316 token_pos: self.pos.saturating_sub(1),
3317 }),
3318 }
3319 }
3320
3321 fn parse_create_user_after_keyword(&mut self) -> Result<Statement, ParseError> {
3325 let name = self.expect_ident_or_string()?;
3326 self.expect_keyword_ident("with")?;
3327 self.expect_keyword_ident("password")?;
3328 let password = self.expect_string_literal()?;
3329 let role = if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
3330 && s.eq_ignore_ascii_case("role")
3331 {
3332 self.advance();
3333 self.expect_string_literal()?
3334 } else {
3335 "readonly".to_string()
3336 };
3337 Ok(Statement::CreateUser(crate::ast::CreateUserStatement {
3338 name,
3339 password,
3340 role,
3341 }))
3342 }
3343
3344 fn parse_update_after_keyword(&mut self) -> Result<Statement, ParseError> {
3347 let table = self.expect_ident_like()?;
3348 self.expect_keyword_ident("set")?;
3349 let mut assignments = Vec::new();
3350 loop {
3351 let col = self.expect_ident_like()?;
3352 if !matches!(self.peek(), Token::Eq) {
3353 return Err(self.err(format!(
3354 "expected `=` after column name in UPDATE SET, got {:?}",
3355 self.peek()
3356 )));
3357 }
3358 self.advance();
3359 let value = self.parse_expr(0)?;
3360 assignments.push((col, value));
3361 if matches!(self.peek(), Token::Comma) {
3362 self.advance();
3363 continue;
3364 }
3365 break;
3366 }
3367 let where_ = if matches!(self.peek(), Token::Where) {
3368 self.advance();
3369 Some(self.parse_expr(0)?)
3370 } else {
3371 None
3372 };
3373 let returning = self.parse_optional_returning()?;
3374 Ok(Statement::Update(crate::ast::UpdateStatement {
3375 table,
3376 assignments,
3377 where_,
3378 returning,
3379 }))
3380 }
3381
3382 fn parse_delete_after_keyword(&mut self) -> Result<Statement, ParseError> {
3385 if !matches!(self.peek(), Token::From) {
3386 return Err(self.err(format!("expected FROM after DELETE, got {:?}", self.peek())));
3387 }
3388 self.advance();
3389 let table = self.expect_ident_like()?;
3390 let where_ = if matches!(self.peek(), Token::Where) {
3391 self.advance();
3392 Some(self.parse_expr(0)?)
3393 } else {
3394 None
3395 };
3396 let returning = self.parse_optional_returning()?;
3397 Ok(Statement::Delete(crate::ast::DeleteStatement {
3398 table,
3399 where_,
3400 returning,
3401 }))
3402 }
3403
3404 fn parse_merge_after_keyword(&mut self) -> Result<Statement, ParseError> {
3414 let is_into_kw = matches!(self.peek(), Token::Into)
3416 || matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("into"));
3417 if !is_into_kw {
3418 return Err(self.err(format!("expected INTO after MERGE, got {:?}", self.peek())));
3419 }
3420 self.advance();
3421 let target = self.expect_ident_like()?;
3422 let target_alias = match self.peek() {
3424 Token::Ident(s) | Token::QuotedIdent(s) if !s.eq_ignore_ascii_case("using") => {
3425 Some(self.expect_ident_like()?)
3426 }
3427 _ => None,
3428 };
3429 let is_using_kw = matches!(
3431 self.peek(),
3432 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("using")
3433 );
3434 if !is_using_kw {
3435 return Err(self.err(format!(
3436 "expected USING after MERGE INTO target, got {:?}",
3437 self.peek()
3438 )));
3439 }
3440 self.advance();
3441 let source = self.expect_ident_like()?;
3442 let source_alias = match self.peek() {
3443 Token::Ident(s) | Token::QuotedIdent(s) if !s.eq_ignore_ascii_case("on") => {
3444 Some(self.expect_ident_like()?)
3445 }
3446 _ => None,
3447 };
3448 if !matches!(self.peek(), Token::On) {
3450 return Err(self.err(format!(
3451 "expected ON after MERGE … USING source, got {:?}",
3452 self.peek()
3453 )));
3454 }
3455 self.advance();
3456 let on = self.parse_expr(0)?;
3457 let mut clauses: Vec<crate::ast::MergeWhenClause> = Vec::new();
3459 loop {
3460 let is_when_kw = matches!(
3461 self.peek(),
3462 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("when")
3463 );
3464 if !is_when_kw {
3465 break;
3466 }
3467 self.advance(); let matched = if matches!(self.peek(), Token::Not) {
3470 self.advance();
3471 crate::ast::MergeMatched::NotMatched
3472 } else {
3473 crate::ast::MergeMatched::Matched
3474 };
3475 let is_matched_kw = matches!(
3476 self.peek(),
3477 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("matched")
3478 );
3479 if !is_matched_kw {
3480 return Err(self.err(format!(
3481 "expected MATCHED in WHEN clause, got {:?}",
3482 self.peek()
3483 )));
3484 }
3485 self.advance();
3486 let condition = if matches!(self.peek(), Token::And) {
3488 self.advance();
3489 Some(self.parse_expr(0)?)
3490 } else {
3491 None
3492 };
3493 let is_then_kw = matches!(
3495 self.peek(),
3496 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("then")
3497 );
3498 if !is_then_kw {
3499 return Err(self.err(format!(
3500 "expected THEN in WHEN clause, got {:?}",
3501 self.peek()
3502 )));
3503 }
3504 self.advance();
3505 let action = match self.peek().clone() {
3507 Token::Insert => {
3508 self.advance();
3509 if !matches!(self.peek(), Token::LParen) {
3511 return Err(self.err(format!(
3512 "expected '(' after INSERT in MERGE, got {:?}",
3513 self.peek()
3514 )));
3515 }
3516 self.advance();
3517 let mut columns: Vec<String> = Vec::new();
3518 loop {
3519 columns.push(self.expect_ident_like()?);
3520 if matches!(self.peek(), Token::Comma) {
3521 self.advance();
3522 continue;
3523 }
3524 break;
3525 }
3526 if !matches!(self.peek(), Token::RParen) {
3527 return Err(self.err(format!(
3528 "expected ')' after INSERT column list, got {:?}",
3529 self.peek()
3530 )));
3531 }
3532 self.advance();
3533 if !matches!(self.peek(), Token::Values) {
3535 return Err(self.err(format!(
3536 "expected VALUES in MERGE INSERT, got {:?}",
3537 self.peek()
3538 )));
3539 }
3540 self.advance();
3541 if !matches!(self.peek(), Token::LParen) {
3542 return Err(self.err(format!(
3543 "expected '(' after VALUES in MERGE INSERT, got {:?}",
3544 self.peek()
3545 )));
3546 }
3547 self.advance();
3548 let mut values: Vec<crate::ast::Expr> = Vec::new();
3549 loop {
3550 values.push(self.parse_expr(0)?);
3551 if matches!(self.peek(), Token::Comma) {
3552 self.advance();
3553 continue;
3554 }
3555 break;
3556 }
3557 if !matches!(self.peek(), Token::RParen) {
3558 return Err(self.err(format!(
3559 "expected ')' after MERGE INSERT values, got {:?}",
3560 self.peek()
3561 )));
3562 }
3563 self.advance();
3564 if columns.len() != values.len() {
3565 return Err(self.err(format!(
3566 "MERGE INSERT column count ({}) ≠ value count ({})",
3567 columns.len(),
3568 values.len()
3569 )));
3570 }
3571 crate::ast::MergeAction::Insert { columns, values }
3572 }
3573 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
3574 self.advance();
3575 let is_set_kw = matches!(
3577 self.peek(),
3578 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set")
3579 );
3580 if !is_set_kw {
3581 return Err(self.err(format!(
3582 "expected SET after UPDATE in MERGE, got {:?}",
3583 self.peek()
3584 )));
3585 }
3586 self.advance();
3587 let mut assignments: Vec<(String, crate::ast::Expr)> = Vec::new();
3588 loop {
3589 let col = self.expect_ident_like()?;
3590 if !matches!(self.peek(), Token::Eq) {
3591 return Err(self.err(format!(
3592 "expected '=' in MERGE UPDATE assignment, got {:?}",
3593 self.peek()
3594 )));
3595 }
3596 self.advance();
3597 let expr = self.parse_expr(0)?;
3598 assignments.push((col, expr));
3599 if matches!(self.peek(), Token::Comma) {
3600 self.advance();
3601 continue;
3602 }
3603 break;
3604 }
3605 crate::ast::MergeAction::Update { assignments }
3606 }
3607 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
3608 self.advance();
3609 crate::ast::MergeAction::Delete
3610 }
3611 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {
3612 self.advance();
3613 let is_nothing_kw = matches!(
3614 self.peek(),
3615 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing")
3616 );
3617 if !is_nothing_kw {
3618 return Err(self.err(format!(
3619 "expected NOTHING after DO in MERGE clause, got {:?}",
3620 self.peek()
3621 )));
3622 }
3623 self.advance();
3624 crate::ast::MergeAction::DoNothing
3625 }
3626 other => {
3627 return Err(self.err(format!(
3628 "expected INSERT / UPDATE / DELETE / DO NOTHING in MERGE clause, got {other:?}"
3629 )));
3630 }
3631 };
3632 clauses.push(crate::ast::MergeWhenClause {
3633 matched,
3634 condition,
3635 action,
3636 });
3637 }
3638 if clauses.is_empty() {
3639 return Err(self.err(String::from("MERGE requires at least one WHEN clause")));
3640 }
3641 Ok(Statement::Merge(crate::ast::MergeStatement {
3642 target,
3643 target_alias,
3644 source,
3645 source_alias,
3646 on,
3647 clauses,
3648 }))
3649 }
3650
3651 fn parse_optional_returning(
3656 &mut self,
3657 ) -> Result<Option<Vec<crate::ast::SelectItem>>, ParseError> {
3658 let is_returning_kw = matches!(
3659 self.peek(),
3660 Token::Ident(s) if s.eq_ignore_ascii_case("returning")
3661 );
3662 if !is_returning_kw {
3663 return Ok(None);
3664 }
3665 self.advance();
3666 let mut items = Vec::new();
3667 loop {
3668 items.push(self.parse_select_item()?);
3669 if matches!(self.peek(), Token::Comma) {
3670 self.advance();
3671 continue;
3672 }
3673 break;
3674 }
3675 Ok(Some(items))
3676 }
3677
3678 fn parse_alter_after_keyword(&mut self) -> Result<Statement, ParseError> {
3686 match self.advance() {
3693 Token::Index => {}
3694 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("index") => {}
3695 Token::Table => {
3698 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("only")) {
3699 self.advance();
3700 }
3701 return self.parse_alter_table_after_keyword();
3702 }
3703 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("table") => {
3704 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("only")) {
3705 self.advance();
3706 }
3707 return self.parse_alter_table_after_keyword();
3708 }
3709 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sequence") => {
3712 return self.parse_alter_sequence_after_keyword();
3713 }
3714 Token::Ident(s) | Token::QuotedIdent(s)
3720 if matches!(
3721 s.to_ascii_lowercase().as_str(),
3722 "view"
3723 | "function"
3724 | "type"
3725 | "domain"
3726 | "database"
3727 | "role"
3728 | "schema"
3729 | "owner"
3730 | "default"
3731 | "extension"
3732 | "materialized"
3733 | "policy"
3734 | "publication"
3735 | "subscription"
3736 ) =>
3737 {
3738 self.consume_until_statement_boundary();
3739 return Ok(Statement::Empty);
3740 }
3741 other => {
3742 return Err(self.err(format!(
3743 "expected INDEX / TABLE / SEQUENCE / VIEW / FUNCTION / TYPE / OWNER / etc \
3744 after ALTER, got {other:?}"
3745 )));
3746 }
3747 }
3748 let if_exists = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
3753 let next = self.tokens.get(self.pos + 1);
3754 if matches!(next, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")) {
3755 self.advance();
3756 self.advance();
3757 true
3758 } else {
3759 false
3760 }
3761 } else {
3762 false
3763 };
3764 let name = self.expect_ident_like()?;
3765 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("rename")) {
3769 self.advance();
3770 if matches!(self.peek(), Token::To) {
3771 self.advance();
3772 } else {
3773 self.expect_keyword_ident("to")?;
3774 }
3775 let new = self.expect_ident_like()?;
3776 return Ok(Statement::AlterIndex(crate::ast::AlterIndexStatement {
3777 name,
3778 target: crate::ast::AlterIndexTarget::Rename { new, if_exists },
3779 }));
3780 }
3781 self.expect_keyword_ident("rebuild")?;
3783 let encoding = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
3785 self.advance();
3786 if !matches!(self.peek(), Token::LParen) {
3787 return Err(self.err(format!(
3788 "expected '(' after WITH in ALTER INDEX REBUILD, got {:?}",
3789 self.peek()
3790 )));
3791 }
3792 self.advance();
3793 self.expect_keyword_ident("encoding")?;
3794 if !matches!(self.peek(), Token::Eq) {
3795 return Err(self.err(format!(
3796 "expected '=' after encoding in ALTER INDEX REBUILD, got {:?}",
3797 self.peek()
3798 )));
3799 }
3800 self.advance();
3801 let enc_ident = match self.advance() {
3802 Token::Ident(s) | Token::QuotedIdent(s) => s,
3803 other => {
3804 return Err(self.err(format!("expected encoding name after =, got {other:?}")));
3805 }
3806 };
3807 let enc = match enc_ident.to_ascii_lowercase().as_str() {
3808 "f32" => VecEncoding::F32,
3809 "sq8" => VecEncoding::Sq8,
3810 "half" => VecEncoding::F16,
3811 other => {
3812 return Err(self.err(format!(
3813 "unknown vector encoding {other:?} in ALTER INDEX REBUILD; supported: F32, SQ8, HALF"
3814 )));
3815 }
3816 };
3817 if !matches!(self.peek(), Token::RParen) {
3818 return Err(self.err(format!(
3819 "expected ')' after encoding value, got {:?}",
3820 self.peek()
3821 )));
3822 }
3823 self.advance();
3824 Some(enc)
3825 } else {
3826 None
3827 };
3828 Ok(Statement::AlterIndex(crate::ast::AlterIndexStatement {
3829 name,
3830 target: crate::ast::AlterIndexTarget::Rebuild { encoding },
3831 }))
3832 }
3833
3834 fn parse_alter_table_after_keyword(&mut self) -> Result<Statement, ParseError> {
3840 let table_name = self.expect_ident_like()?;
3841 let mut targets: Vec<crate::ast::AlterTableTarget> = Vec::new();
3842 loop {
3843 let subaction = self.parse_alter_table_subaction()?;
3844 targets.extend(subaction);
3848 if matches!(self.peek(), Token::Comma) {
3849 self.advance();
3850 continue;
3851 }
3852 break;
3853 }
3854 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
3855 name: table_name,
3856 targets,
3857 }))
3858 }
3859
3860 fn parse_alter_table_subaction(
3864 &mut self,
3865 ) -> Result<Vec<crate::ast::AlterTableTarget>, ParseError> {
3866 match self.peek() {
3867 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
3868 self.advance();
3869 let setting = self.expect_ident_like()?;
3870 if !setting.eq_ignore_ascii_case("hot_tier_bytes") {
3871 return Err(self.err(alloc::format!(
3872 "ALTER TABLE SET: unknown setting {setting:?}; supported: hot_tier_bytes"
3873 )));
3874 }
3875 if !matches!(self.peek(), Token::Eq) {
3876 return Err(self.err(alloc::format!(
3877 "expected '=' after hot_tier_bytes, got {:?}",
3878 self.peek()
3879 )));
3880 }
3881 self.advance();
3882 let n = self.expect_u64_literal()?;
3883 Ok(alloc::vec![crate::ast::AlterTableTarget::SetHotTierBytes(n)])
3884 }
3885 Token::Ident(s) if s.eq_ignore_ascii_case("add") => {
3886 self.advance();
3887 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint"))
3894 {
3895 let kind_pos = self.pos + 2;
3898 let kind = self.tokens.get(kind_pos).cloned();
3899 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("foreign"))
3900 {
3901 let fk = self.parse_table_level_fk()?;
3902 return Ok(alloc::vec![
3903 crate::ast::AlterTableTarget::AddForeignKey(fk)
3904 ]);
3905 }
3906 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("primary"))
3907 {
3908 self.advance(); let _name = self.expect_ident_like()?;
3910 self.advance(); self.expect_keyword_ident("key")?;
3912 let cols = self.parse_paren_ident_list("PRIMARY KEY")?;
3913 return Ok(alloc::vec![
3914 crate::ast::AlterTableTarget::AddTableConstraint(
3915 crate::ast::TableConstraint::PrimaryKey {
3916 name: None,
3917 columns: cols,
3918 }
3919 )
3920 ]);
3921 }
3922 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("unique"))
3923 {
3924 self.advance(); let _name = self.expect_ident_like()?;
3926 let uc = self.parse_table_level_unique()?;
3932 return Ok(alloc::vec![
3933 crate::ast::AlterTableTarget::AddTableConstraint(uc)
3934 ]);
3935 }
3936 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("check"))
3937 {
3938 self.advance(); let _name = self.expect_ident_like()?;
3940 self.advance(); if !matches!(self.peek(), Token::LParen) {
3942 return Err(self.err(alloc::format!(
3943 "expected '(' after CHECK, got {:?}", self.peek()
3944 )));
3945 }
3946 self.advance();
3947 let expr = self.parse_expr(0)?;
3948 if matches!(self.peek(), Token::RParen) {
3949 self.advance();
3950 }
3951 return Ok(alloc::vec![
3952 crate::ast::AlterTableTarget::AddTableConstraint(
3953 crate::ast::TableConstraint::Check { name: None, expr }
3954 )
3955 ]);
3956 }
3957 }
3960 let is_fk = matches!(
3961 self.peek(),
3962 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
3963 || s.eq_ignore_ascii_case("foreign")
3964 );
3965 if is_fk {
3966 let fk = self.parse_table_level_fk()?;
3967 return Ok(alloc::vec![crate::ast::AlterTableTarget::AddForeignKey(fk)]);
3968 }
3969 match self.peek().clone() {
3972 Token::Ident(s) if s.eq_ignore_ascii_case("primary") => {
3973 self.advance();
3974 self.expect_keyword_ident("key")?;
3975 let cols = self.parse_paren_ident_list("PRIMARY KEY")?;
3976 return Ok(alloc::vec![
3977 crate::ast::AlterTableTarget::AddTableConstraint(
3978 crate::ast::TableConstraint::PrimaryKey {
3979 name: None,
3980 columns: cols,
3981 }
3982 )
3983 ]);
3984 }
3985 Token::Ident(s) if s.eq_ignore_ascii_case("unique") => {
3986 let uc = self.parse_table_level_unique()?;
3988 return Ok(alloc::vec![
3989 crate::ast::AlterTableTarget::AddTableConstraint(uc)
3990 ]);
3991 }
3992 _ => {}
3993 }
3994 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
3995 self.advance();
3996 }
3997 let mut if_not_exists = false;
3998 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
3999 self.advance();
4000 if !matches!(self.peek(), Token::Not) {
4001 return Err(self.err(alloc::format!(
4002 "expected NOT after IF in ALTER TABLE ADD COLUMN, got {:?}",
4003 self.peek()
4004 )));
4005 }
4006 self.advance();
4007 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("exists")) {
4008 return Err(self.err(alloc::format!(
4009 "expected EXISTS after IF NOT in ALTER TABLE ADD COLUMN, got {:?}",
4010 self.peek()
4011 )));
4012 }
4013 self.advance();
4014 if_not_exists = true;
4015 }
4016 let (column, col_level_fk) = self.parse_column_def_with_fk()?;
4020 let col_name = column.name.clone();
4021 let mut out = alloc::vec![crate::ast::AlterTableTarget::AddColumn {
4022 column,
4023 if_not_exists,
4024 }];
4025 if let Some(mut fk) = col_level_fk {
4026 if fk.columns.is_empty() {
4027 fk.columns.push(col_name);
4028 }
4029 out.push(crate::ast::AlterTableTarget::AddForeignKey(fk));
4030 }
4031 Ok(out)
4032 }
4033 Token::Drop => {
4034 self.advance();
4035 let subject = match self.peek() {
4042 Token::Ident(s) if s.eq_ignore_ascii_case("constraint") => {
4043 self.advance();
4044 "constraint"
4045 }
4046 Token::Ident(s) if s.eq_ignore_ascii_case("column") => {
4047 self.advance();
4048 "column"
4049 }
4050 Token::Ident(_) | Token::QuotedIdent(_) => "column",
4054 other => {
4055 return Err(self.err(alloc::format!(
4056 "expected COLUMN / CONSTRAINT after DROP in ALTER TABLE, got {other:?}"
4057 )));
4058 }
4059 };
4060 let mut if_exists = false;
4061 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
4062 let n1 = self.tokens.get(self.pos + 1);
4063 if matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")) {
4064 self.advance();
4065 self.advance();
4066 if_exists = true;
4067 }
4068 }
4069 let name = self.expect_ident_like()?;
4070 let mut cascade = false;
4071 if matches!(
4072 self.peek(),
4073 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
4074 || s.eq_ignore_ascii_case("restrict")
4075 ) {
4076 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("cascade"))
4077 {
4078 cascade = true;
4079 }
4080 self.advance();
4081 }
4082 if subject == "constraint" {
4083 Ok(alloc::vec![crate::ast::AlterTableTarget::DropForeignKey {
4084 name,
4085 if_exists,
4086 }])
4087 } else {
4088 Ok(alloc::vec![crate::ast::AlterTableTarget::DropColumn {
4089 column: name,
4090 if_exists,
4091 cascade,
4092 }])
4093 }
4094 }
4095 Token::Ident(s) if s.eq_ignore_ascii_case("alter") => {
4096 self.advance();
4097 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
4098 self.advance();
4099 }
4100 let col_name = self.expect_ident_like()?;
4101 match self.peek() {
4102 Token::Ident(s) if s.eq_ignore_ascii_case("type") => {
4103 self.advance();
4104 }
4105 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
4113 let is_default_nextval =
4124 matches!(self.tokens.get(self.pos + 1), Some(Token::Default))
4125 && matches!(
4126 self.tokens.get(self.pos + 2),
4127 Some(Token::Ident(f)) if f.eq_ignore_ascii_case("nextval")
4128 );
4129 let seq_name = if is_default_nextval {
4132 self.scan_sequence_name_until_boundary()
4133 } else {
4134 self.consume_until_statement_boundary();
4135 None
4136 };
4137 if is_default_nextval {
4138 return Ok(alloc::vec![
4139 crate::ast::AlterTableTarget::SetColumnAutoIncrement {
4140 column: col_name,
4141 seq_name,
4142 }
4143 ]);
4144 }
4145 return Ok(Vec::new());
4151 }
4152 Token::Ident(s) if s.eq_ignore_ascii_case("drop") => {
4153 self.consume_until_statement_boundary();
4155 return Ok(Vec::new());
4156 }
4157 Token::Ident(s) if s.eq_ignore_ascii_case("add") => {
4158 let is_generated = matches!(
4166 self.tokens.get(self.pos + 1),
4167 Some(Token::Ident(g)) if g.eq_ignore_ascii_case("generated")
4168 );
4169 if !is_generated {
4170 return Err(self.err(alloc::format!(
4171 "expected GENERATED after ALTER COLUMN {col_name} ADD, got {:?}",
4172 self.tokens.get(self.pos + 1)
4173 )));
4174 }
4175 let seq_name = self.scan_sequence_name_until_boundary();
4176 return Ok(alloc::vec![
4177 crate::ast::AlterTableTarget::SetColumnAutoIncrement {
4178 column: col_name,
4179 seq_name,
4180 }
4181 ]);
4182 }
4183 other => {
4184 return Err(self.err(alloc::format!(
4185 "expected TYPE / SET / DROP / ADD after ALTER COLUMN <name>, got {other:?}"
4186 )));
4187 }
4188 }
4189 let new_type = self.parse_column_type_name()?;
4190 let using = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using"))
4191 {
4192 self.advance();
4193 Some(self.parse_expr(0)?)
4194 } else {
4195 None
4196 };
4197 Ok(alloc::vec![crate::ast::AlterTableTarget::AlterColumnType {
4198 column: col_name,
4199 new_type,
4200 using,
4201 }])
4202 }
4203 Token::Ident(s) if s.eq_ignore_ascii_case("rename") => {
4210 self.advance();
4211 if matches!(self.peek(), Token::To)
4216 || matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("to"))
4217 {
4218 self.advance();
4219 let new = self.expect_ident_like()?;
4220 return Ok(alloc::vec![crate::ast::AlterTableTarget::RenameTable {
4221 new,
4222 }]);
4223 }
4224 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
4225 self.advance();
4226 }
4227 let old = self.expect_ident_like()?;
4228 if matches!(self.peek(), Token::To) {
4231 self.advance();
4232 } else {
4233 self.expect_keyword_ident("to")?;
4234 }
4235 let new = self.expect_ident_like()?;
4236 Ok(alloc::vec![crate::ast::AlterTableTarget::RenameColumn {
4237 old,
4238 new,
4239 }])
4240 }
4241 Token::Ident(s)
4248 if s.eq_ignore_ascii_case("enable") || s.eq_ignore_ascii_case("disable") =>
4249 {
4250 let enabled = s.eq_ignore_ascii_case("enable");
4251 self.advance();
4252 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("keys")) {
4262 self.advance();
4263 return Ok(Vec::new());
4264 }
4265 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("trigger")) {
4266 return Err(self.err(alloc::format!(
4267 "expected TRIGGER after {}, got {:?}",
4268 if enabled { "ENABLE" } else { "DISABLE" },
4269 self.peek()
4270 )));
4271 }
4272 self.advance();
4273 let which = if matches!(self.peek(), Token::All)
4276 || matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("all"))
4277 {
4278 self.advance();
4279 crate::ast::TriggerSelector::All
4280 } else {
4281 let name = self.expect_ident_like()?;
4282 crate::ast::TriggerSelector::Named(name)
4283 };
4284 Ok(alloc::vec![crate::ast::AlterTableTarget::SetTriggerEnabled {
4285 which,
4286 enabled,
4287 }])
4288 }
4289 other => Err(self.err(alloc::format!(
4290 "expected SET / ADD / DROP / ALTER / RENAME / ENABLE / DISABLE in ALTER TABLE, got {other:?}"
4291 ))),
4292 }
4293 }
4294
4295 fn try_peek_meta_qualified(&mut self) -> Option<String> {
4304 let schema = match self.tokens.get(self.pos) {
4306 Some(Token::Ident(s) | Token::QuotedIdent(s)) => s.clone(),
4307 _ => return None,
4308 };
4309 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Dot)) {
4311 return None;
4312 }
4313 let tbl = match self.tokens.get(self.pos + 2)? {
4317 Token::Ident(t) | Token::QuotedIdent(t) => t.clone(),
4318 Token::Tables => "tables".to_string(),
4319 _ => return None,
4322 };
4323 let (prefix, normalised) = if schema.eq_ignore_ascii_case("information_schema") {
4327 ("__spg_info_", tbl.to_ascii_lowercase())
4328 } else if schema.eq_ignore_ascii_case("pg_catalog") {
4329 let bare = tbl
4330 .to_ascii_lowercase()
4331 .strip_prefix("pg_")
4332 .map(alloc::string::String::from)
4333 .unwrap_or_else(|| tbl.to_ascii_lowercase());
4334 ("__spg_pg_", bare)
4335 } else if schema.eq_ignore_ascii_case("mysql") {
4336 ("__spg_mysql_", tbl.to_ascii_lowercase())
4340 } else {
4341 return None;
4342 };
4343 self.advance(); self.advance(); self.advance(); Some(alloc::format!("{prefix}{normalised}"))
4347 }
4348
4349 fn try_peek_meta_bare(&mut self) -> Option<String> {
4356 const PG_META_TABLES: &[&str] = &[
4357 "pg_attribute",
4358 "pg_class",
4359 "pg_constraint",
4360 "pg_database",
4361 "pg_extension",
4362 "pg_index",
4363 "pg_indexes",
4364 "pg_matviews",
4365 "pg_namespace",
4366 "pg_proc",
4367 "pg_roles",
4368 "pg_settings",
4369 "pg_trigger",
4370 "pg_type",
4371 "pg_user",
4372 "pg_views",
4373 ];
4374 let name = match self.tokens.get(self.pos) {
4375 Some(Token::Ident(s)) => s.to_ascii_lowercase(),
4376 _ => return None,
4377 };
4378 if matches!(self.tokens.get(self.pos + 1), Some(Token::Dot)) {
4381 return None;
4382 }
4383 if !PG_META_TABLES.contains(&name.as_str()) {
4384 return None;
4385 }
4386 self.advance();
4387 let bare = name.strip_prefix("pg_").unwrap_or(&name);
4388 Some(alloc::format!("__spg_pg_{bare}"))
4389 }
4390
4391 fn expect_keyword_ident(&mut self, kw: &str) -> Result<(), ParseError> {
4393 match self.advance() {
4394 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case(kw) => Ok(()),
4395 other => Err(ParseError {
4396 message: format!("expected {kw:?}, got {other:?}"),
4397 token_pos: self.pos.saturating_sub(1),
4398 }),
4399 }
4400 }
4401
4402 fn expect_ident_or_string(&mut self) -> Result<String, ParseError> {
4406 match self.advance() {
4407 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => Ok(s),
4408 other => Err(ParseError {
4409 message: format!("expected identifier or string, got {other:?}"),
4410 token_pos: self.pos.saturating_sub(1),
4411 }),
4412 }
4413 }
4414
4415 fn expect_string_literal(&mut self) -> Result<String, ParseError> {
4416 match self.advance() {
4417 Token::String(s) => Ok(s),
4418 other => Err(ParseError {
4419 message: format!("expected quoted string, got {other:?}"),
4420 token_pos: self.pos.saturating_sub(1),
4421 }),
4422 }
4423 }
4424
4425 fn parse_select_stmt(&mut self) -> Result<Statement, ParseError> {
4426 self.enter_nested()?;
4430 let r = self.parse_select_stmt_inner();
4431 self.nest_depth -= 1;
4432 r
4433 }
4434
4435 fn parse_select_stmt_inner(&mut self) -> Result<Statement, ParseError> {
4436 let mut head = self.parse_bare_select()?;
4441 while matches!(self.peek(), Token::Union) {
4442 self.advance();
4443 let kind = if matches!(self.peek(), Token::All) {
4444 self.advance();
4445 UnionKind::All
4446 } else {
4447 UnionKind::Distinct
4448 };
4449 let peer = self.parse_bare_select()?;
4450 head.unions.push((kind, peer));
4451 }
4452 head.order_by = if matches!(self.peek(), Token::Order) {
4453 self.advance();
4454 if !matches!(self.peek(), Token::By) {
4455 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
4456 }
4457 self.advance();
4458 let mut keys = Vec::new();
4461 loop {
4462 let expr = self.parse_expr(0)?;
4463 let desc = if matches!(self.peek(), Token::Desc) {
4464 self.advance();
4465 true
4466 } else if matches!(self.peek(), Token::Asc) {
4467 self.advance();
4468 false
4469 } else {
4470 false
4471 };
4472 let nulls_first = self.parse_optional_nulls_placement()?;
4474 keys.push(OrderBy {
4475 expr,
4476 desc,
4477 nulls_first,
4478 });
4479 if matches!(self.peek(), Token::Comma) {
4480 self.advance();
4481 } else {
4482 break;
4483 }
4484 }
4485 keys
4486 } else {
4487 Vec::new()
4488 };
4489 head.limit = if matches!(self.peek(), Token::Limit) {
4490 self.advance();
4491 if self.consume_limit_unbounded_sentinel() {
4498 None
4499 } else {
4500 Some(self.parse_limit_expr("LIMIT")?)
4501 }
4502 } else {
4503 None
4504 };
4505 head.offset = if matches!(self.peek(), Token::Offset) {
4506 self.advance();
4507 let off = self.parse_limit_expr("OFFSET")?;
4511 self.consume_optional_rows_keyword();
4512 Some(off)
4513 } else {
4514 None
4515 };
4516 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("fetch"))
4522 {
4523 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4526 if s.eq_ignore_ascii_case("first") || s.eq_ignore_ascii_case("next"))
4527 {
4528 self.advance();
4529 }
4530 let count = if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4533 if s.eq_ignore_ascii_case("row") || s.eq_ignore_ascii_case("rows"))
4534 {
4535 crate::ast::LimitExpr::Literal(1)
4537 } else {
4538 self.parse_limit_expr("FETCH FIRST")?
4539 };
4540 self.consume_optional_rows_keyword();
4542 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4548 if s.eq_ignore_ascii_case("only"))
4549 {
4550 self.advance();
4551 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4552 if s.eq_ignore_ascii_case("with"))
4553 {
4554 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4556 if s.eq_ignore_ascii_case("ties"))
4557 {
4558 self.advance();
4559 head.limit_with_ties = true;
4560 }
4561 }
4562 head.limit = Some(count);
4563 }
4564 self.consume_optional_for_lock_clauses();
4579 Ok(Statement::Select(head))
4580 }
4581
4582 fn consume_optional_for_lock_clauses(&mut self) {
4589 while matches!(self.peek(), Token::For) {
4590 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4594 if s.eq_ignore_ascii_case("no"))
4595 {
4596 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4600 if s.eq_ignore_ascii_case("key"))
4601 {
4602 self.advance(); }
4604 }
4605 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4607 if s.eq_ignore_ascii_case("key"))
4608 {
4609 self.advance(); }
4611 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4617 if s.eq_ignore_ascii_case("update") || s.eq_ignore_ascii_case("share"))
4618 {
4619 self.advance();
4620 } else {
4621 return;
4626 }
4627 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4630 if s.eq_ignore_ascii_case("of"))
4631 {
4632 self.advance(); #[allow(clippy::while_let_loop)]
4634 loop {
4635 match self.peek() {
4636 Token::Ident(_) | Token::QuotedIdent(_) => {
4637 self.advance();
4638 if matches!(self.peek(), Token::Dot) {
4640 self.advance();
4641 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
4642 self.advance();
4643 }
4644 }
4645 }
4646 _ => break,
4647 }
4648 if matches!(self.peek(), Token::Comma) {
4649 self.advance();
4650 } else {
4651 break;
4652 }
4653 }
4654 }
4655 match self.peek().clone() {
4657 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nowait") => {
4658 self.advance();
4659 }
4660 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("skip") => {
4661 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4663 if s.eq_ignore_ascii_case("locked"))
4664 {
4665 self.advance(); }
4667 }
4668 _ => {}
4669 }
4670 }
4672 }
4673
4674 fn consume_limit_unbounded_sentinel(&mut self) -> bool {
4683 if matches!(self.peek(), Token::Null) {
4684 self.advance();
4685 return true;
4686 }
4687 if matches!(self.peek(), Token::All) {
4688 self.advance();
4689 return true;
4690 }
4691 false
4692 }
4693
4694 fn consume_optional_rows_keyword(&mut self) {
4698 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4699 if s.eq_ignore_ascii_case("row") || s.eq_ignore_ascii_case("rows"))
4700 {
4701 self.advance();
4702 }
4703 }
4704
4705 fn parse_limit_expr(&mut self, label: &str) -> Result<crate::ast::LimitExpr, ParseError> {
4706 match self.advance() {
4707 Token::Integer(n) if n >= 0 => u32::try_from(n)
4708 .map(crate::ast::LimitExpr::Literal)
4709 .map_err(|_| ParseError {
4710 message: alloc::format!("{label} value too large: {n}"),
4711 token_pos: self.pos.saturating_sub(1),
4712 }),
4713 Token::Placeholder(n) => Ok(crate::ast::LimitExpr::Placeholder(n)),
4714 other => Err(ParseError {
4715 message: alloc::format!(
4716 "expected non-negative integer or $N placeholder after {label}, got {other:?}"
4717 ),
4718 token_pos: self.pos.saturating_sub(1),
4719 }),
4720 }
4721 }
4722
4723 fn parse_bare_select(&mut self) -> Result<SelectStatement, ParseError> {
4728 if !matches!(self.peek(), Token::Select) {
4729 return Err(self.err(format!(
4730 "expected SELECT to start a query block, got {:?}",
4731 self.peek()
4732 )));
4733 }
4734 self.advance();
4735 let distinct = if matches!(self.peek(), Token::Distinct) {
4736 self.advance();
4737 true
4738 } else {
4739 false
4740 };
4741 let items = self.parse_select_list()?;
4742 let from = if matches!(self.peek(), Token::From) {
4743 self.advance();
4744 Some(self.parse_from_clause()?)
4745 } else {
4746 None
4747 };
4748 let where_ = if matches!(self.peek(), Token::Where) {
4749 self.advance();
4750 Some(self.parse_expr(0)?)
4751 } else {
4752 None
4753 };
4754 let mut group_by_all = false;
4755 let group_by = if matches!(self.peek(), Token::Group) {
4756 self.advance();
4757 if !matches!(self.peek(), Token::By) {
4758 return Err(self.err(format!("expected BY after GROUP, got {:?}", self.peek())));
4759 }
4760 self.advance();
4761 if matches!(self.peek(), Token::All) {
4764 self.advance();
4765 group_by_all = true;
4766 None
4767 } else {
4768 let mut groups = Vec::new();
4769 loop {
4770 groups.push(self.parse_expr(0)?);
4771 if matches!(self.peek(), Token::Comma) {
4772 self.advance();
4773 } else {
4774 break;
4775 }
4776 }
4777 Some(groups)
4778 }
4779 } else {
4780 None
4781 };
4782 let having = if matches!(self.peek(), Token::Having) {
4783 self.advance();
4784 Some(self.parse_expr(0)?)
4785 } else {
4786 None
4787 };
4788 Ok(SelectStatement {
4789 ctes: Vec::new(),
4790 distinct,
4791 items,
4792 from,
4793 where_,
4794 group_by,
4795 group_by_all,
4796 having,
4797 unions: Vec::new(),
4798 order_by: Vec::new(),
4799 limit: None,
4800 offset: None,
4801 limit_with_ties: false,
4802 })
4803 }
4804
4805 fn parse_create_table_stmt_after_create(&mut self) -> Result<Statement, ParseError> {
4806 debug_assert!(matches!(self.peek(), Token::Table));
4808 self.advance();
4809 let if_not_exists = self.consume_if_not_exists();
4810 let name = self.expect_ident_like()?;
4811 if !matches!(self.peek(), Token::LParen) {
4812 return Err(self.err(format!(
4813 "expected '(' after table name, got {:?}",
4814 self.peek()
4815 )));
4816 }
4817 self.advance();
4818 let mut columns = Vec::new();
4819 let mut foreign_keys: Vec<ForeignKeyConstraint> = Vec::new();
4820 let mut table_constraints: Vec<crate::ast::TableConstraint> = Vec::new();
4821 loop {
4822 if self.peek_table_level_pk_start() {
4828 table_constraints.push(self.parse_table_level_primary_key()?);
4829 } else if self.peek_table_level_unique_start() {
4830 table_constraints.push(self.parse_table_level_unique()?);
4831 } else if self.peek_table_level_check_start() {
4832 table_constraints.push(self.parse_table_level_check()?);
4834 } else if self.peek_mysql_inline_key_start() {
4835 if let Some(uc) = self.parse_mysql_inline_key()? {
4841 table_constraints.push(uc);
4842 }
4843 } else if let Some(kind) = self.peek_named_table_constraint_kind() {
4844 self.advance(); let _name = self.expect_ident_like()?;
4852 table_constraints.push(match kind {
4853 NamedTableConstraintKind::Check => self.parse_table_level_check()?,
4854 NamedTableConstraintKind::Unique => self.parse_table_level_unique()?,
4855 NamedTableConstraintKind::PrimaryKey => self.parse_table_level_primary_key()?,
4856 });
4857 } else if self.peek_constraint_or_fk_start() {
4858 foreign_keys.push(self.parse_table_level_fk()?);
4859 } else {
4860 let (col, col_level_fk) = self.parse_column_def_with_fk()?;
4861 if col.is_unique {
4865 table_constraints.push(crate::ast::TableConstraint::Unique {
4866 name: None,
4867 columns: alloc::vec![col.name.clone()],
4868 nulls_not_distinct: false,
4869 });
4870 }
4871 if let Some(check_expr) = col.check.clone() {
4872 table_constraints.push(crate::ast::TableConstraint::Check {
4873 name: None,
4874 expr: check_expr,
4875 });
4876 }
4877 columns.push(col);
4878 if let Some(fk) = col_level_fk {
4879 foreign_keys.push(fk);
4880 }
4881 }
4882 match self.peek() {
4883 Token::Comma => {
4884 self.advance();
4885 }
4886 Token::RParen => {
4887 self.advance();
4888 break;
4889 }
4890 other => {
4891 return Err(
4892 self.err(format!("expected ',' or ')' in column list, got {other:?}"))
4893 );
4894 }
4895 }
4896 }
4897 if columns.is_empty() {
4898 return Err(self.err("CREATE TABLE requires at least one column".into()));
4899 }
4900 self.consume_mysql_table_options();
4907 Ok(Statement::CreateTable(CreateTableStatement {
4908 name,
4909 columns,
4910 if_not_exists,
4911 foreign_keys,
4912 table_constraints,
4913 }))
4914 }
4915
4916 fn peek_mysql_inline_key_start(&self) -> bool {
4925 let cur = self.peek();
4926 let after_keyword_followed_by_paren_or_ident_paren = |skip: usize| -> bool {
4936 match self.tokens.get(skip) {
4939 Some(Token::LParen) => true,
4940 Some(Token::Ident(_) | Token::QuotedIdent(_)) => {
4941 matches!(self.tokens.get(skip + 1), Some(Token::LParen))
4942 }
4943 _ => false,
4944 }
4945 };
4946 let is_key_or_index_tok = |t: &Token| -> bool {
4950 matches!(t, Token::Index)
4951 || matches!(t, Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index"))
4952 };
4953 match cur {
4954 Token::Index => after_keyword_followed_by_paren_or_ident_paren(self.pos + 1),
4955 Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index") => {
4956 after_keyword_followed_by_paren_or_ident_paren(self.pos + 1)
4957 }
4958 Token::Ident(s)
4959 if s.eq_ignore_ascii_case("fulltext") || s.eq_ignore_ascii_case("spatial") =>
4960 {
4961 let nxt = self.tokens.get(self.pos + 1);
4962 let after_after = if nxt.is_some_and(is_key_or_index_tok) {
4963 self.pos + 2
4964 } else {
4965 self.pos + 1
4966 };
4967 after_keyword_followed_by_paren_or_ident_paren(after_after)
4968 }
4969 Token::Ident(s) if s.eq_ignore_ascii_case("unique") => {
4970 let nxt = self.tokens.get(self.pos + 1);
4971 if !nxt.is_some_and(is_key_or_index_tok) {
4972 return false;
4973 }
4974 after_keyword_followed_by_paren_or_ident_paren(self.pos + 2)
4975 }
4976 _ => false,
4977 }
4978 }
4979
4980 fn parse_mysql_inline_key(
4989 &mut self,
4990 ) -> Result<Option<crate::ast::TableConstraint>, ParseError> {
4991 let is_unique = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unique"))
4993 {
4994 self.advance();
4995 true
4996 } else {
4997 false
4998 };
4999 let mut is_fulltext = false;
5005 let mut is_spatial = false;
5006 if let Token::Ident(s) = self.peek().clone() {
5007 if s.eq_ignore_ascii_case("fulltext") {
5008 self.advance();
5009 is_fulltext = true;
5010 } else if s.eq_ignore_ascii_case("spatial") {
5011 self.advance();
5012 is_spatial = true;
5013 }
5014 }
5015 match self.peek() {
5018 Token::Index => {
5019 self.advance();
5020 }
5021 Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index") => {
5022 self.advance();
5023 }
5024 other => {
5025 return Err(self.err(alloc::format!(
5026 "expected KEY/INDEX in inline index declaration, got {other:?}"
5027 )));
5028 }
5029 }
5030 let mut idx_name: Option<String> = None;
5035 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_))
5036 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
5037 {
5038 if let Token::Ident(s) | Token::QuotedIdent(s) = self.advance() {
5039 idx_name = Some(s);
5040 }
5041 }
5042 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
5044 self.advance();
5045 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
5046 self.advance();
5047 }
5048 }
5049 if !matches!(self.peek(), Token::LParen) {
5051 return Err(self.err(alloc::format!(
5052 "expected '(' in inline KEY/INDEX, got {:?}",
5053 self.peek()
5054 )));
5055 }
5056 self.advance();
5057 let mut cols: Vec<String> = Vec::new();
5058 while let Token::Ident(s) | Token::QuotedIdent(s) = self.peek().clone() {
5059 self.advance();
5060 cols.push(s);
5061 if matches!(self.peek(), Token::LParen) {
5063 let mut depth = 1usize;
5064 self.advance();
5065 while depth > 0 {
5066 match self.peek() {
5067 Token::LParen => depth += 1,
5068 Token::RParen => depth -= 1,
5069 Token::Eof => break,
5070 _ => {}
5071 }
5072 self.advance();
5073 }
5074 }
5075 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("asc") || s.eq_ignore_ascii_case("desc"))
5077 || matches!(self.peek(), Token::Asc | Token::Desc)
5078 {
5079 self.advance();
5080 }
5081 if matches!(self.peek(), Token::Comma) {
5082 self.advance();
5083 continue;
5084 }
5085 break;
5086 }
5087 if matches!(self.peek(), Token::RParen) {
5088 self.advance();
5089 }
5090 while !matches!(self.peek(), Token::Comma | Token::RParen | Token::Eof) {
5093 self.advance();
5094 }
5095 if cols.is_empty() {
5096 return Ok(None);
5097 }
5098 if is_unique {
5099 Ok(Some(crate::ast::TableConstraint::Unique {
5105 name: idx_name,
5106 columns: cols,
5107 nulls_not_distinct: false,
5108 }))
5109 } else if is_fulltext {
5110 Ok(Some(crate::ast::TableConstraint::FulltextIndex {
5116 name: idx_name,
5117 columns: cols,
5118 }))
5119 } else if is_spatial {
5120 Ok(None)
5123 } else {
5124 Ok(Some(crate::ast::TableConstraint::Index {
5127 name: idx_name,
5128 columns: cols,
5129 }))
5130 }
5131 }
5132
5133 fn consume_mysql_table_options(&mut self) {
5138 loop {
5139 let name_lc = match self.peek().clone() {
5143 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
5144 Token::Default => alloc::string::String::from("default"),
5145 _ => break,
5146 };
5147 let known = matches!(
5148 name_lc.as_str(),
5149 "engine"
5150 | "default"
5151 | "charset"
5152 | "collate"
5153 | "auto_increment"
5154 | "row_format"
5155 | "comment"
5156 | "pack_keys"
5157 | "stats_persistent"
5158 | "stats_auto_recalc"
5159 | "stats_sample_pages"
5160 | "key_block_size"
5161 | "tablespace"
5162 | "min_rows"
5163 | "max_rows"
5164 | "checksum"
5165 | "delay_key_write"
5166 | "insert_method"
5167 | "data"
5168 | "index"
5169 | "encryption"
5170 | "compression"
5171 );
5172 if !known {
5173 break;
5174 }
5175 self.advance(); if name_lc == "default" {
5179 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
5180 self.advance();
5181 }
5182 }
5183 if matches!(self.peek(), Token::Eq) {
5184 self.advance();
5185 }
5186 match self.peek() {
5187 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_) | Token::Integer(_) => {
5188 self.advance();
5189 }
5190 _ => {}
5191 }
5192 }
5193 }
5194
5195 fn peek_table_level_pk_start(&self) -> bool {
5200 let cur = self.peek();
5201 let nxt = self.tokens.get(self.pos + 1);
5202 let nxt2 = self.tokens.get(self.pos + 2);
5203 let is_primary = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("primary"));
5204 let is_key = matches!(nxt, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("key"));
5205 let is_lparen = matches!(nxt2, Some(Token::LParen));
5206 is_primary && is_key && is_lparen
5207 }
5208
5209 fn peek_table_level_unique_start(&self) -> bool {
5213 let cur = self.peek();
5214 let is_unique = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("unique"));
5215 if !is_unique {
5216 return false;
5217 }
5218 let n1 = self.tokens.get(self.pos + 1);
5219 if matches!(n1, Some(Token::LParen)) {
5221 return true;
5222 }
5223 let is_nulls = matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("nulls"));
5225 if !is_nulls {
5226 return false;
5227 }
5228 let n2 = self.tokens.get(self.pos + 2);
5229 let n3 = self.tokens.get(self.pos + 3);
5230 let n4 = self.tokens.get(self.pos + 4);
5231 if matches!(n2, Some(Token::Distinct)) && matches!(n3, Some(Token::LParen)) {
5233 return true;
5234 }
5235 if matches!(n2, Some(Token::Not))
5237 && matches!(n3, Some(Token::Distinct))
5238 && matches!(n4, Some(Token::LParen))
5239 {
5240 return true;
5241 }
5242 false
5243 }
5244
5245 fn parse_table_level_primary_key(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
5246 self.advance(); self.advance(); let columns = self.parse_paren_ident_list("PRIMARY KEY")?;
5249 Ok(crate::ast::TableConstraint::PrimaryKey {
5250 name: None,
5251 columns,
5252 })
5253 }
5254
5255 fn parse_table_level_unique(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
5256 self.advance(); let mut nulls_not_distinct = false;
5261 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("nulls")) {
5262 let n1 = self.tokens.get(self.pos + 1);
5263 let n2 = self.tokens.get(self.pos + 2);
5264 let is_not = matches!(n1, Some(Token::Not));
5265 let is_distinct = matches!(n2, Some(Token::Distinct));
5266 if is_not && is_distinct {
5267 self.advance(); self.advance(); self.advance(); nulls_not_distinct = true;
5271 } else if matches!(n1, Some(Token::Distinct)) {
5272 self.advance(); self.advance(); }
5275 }
5276 let columns = self.parse_paren_ident_list("UNIQUE")?;
5277 Ok(crate::ast::TableConstraint::Unique {
5278 name: None,
5279 columns,
5280 nulls_not_distinct,
5281 })
5282 }
5283
5284 fn parse_table_level_check(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
5288 self.advance(); if !matches!(self.peek(), Token::LParen) {
5290 return Err(self.err(alloc::format!(
5291 "expected '(' after CHECK, got {:?}",
5292 self.peek()
5293 )));
5294 }
5295 self.advance();
5296 let expr = self.parse_expr(0)?;
5297 if !matches!(self.peek(), Token::RParen) {
5298 return Err(self.err(alloc::format!(
5299 "expected ')' to close CHECK predicate, got {:?}",
5300 self.peek()
5301 )));
5302 }
5303 self.advance();
5304 Ok(crate::ast::TableConstraint::Check { name: None, expr })
5305 }
5306
5307 fn peek_table_level_check_start(&self) -> bool {
5309 matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("check"))
5310 }
5311
5312 fn peek_named_table_constraint_kind(&self) -> Option<NamedTableConstraintKind> {
5317 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
5318 return None;
5319 }
5320 match self.tokens.get(self.pos + 2) {
5323 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("check") => {
5324 Some(NamedTableConstraintKind::Check)
5325 }
5326 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("unique") => {
5327 Some(NamedTableConstraintKind::Unique)
5328 }
5329 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("primary") => {
5330 Some(NamedTableConstraintKind::PrimaryKey)
5331 }
5332 _ => None,
5333 }
5334 }
5335
5336 fn parse_paren_ident_list(&mut self, ctx: &str) -> Result<Vec<String>, ParseError> {
5337 if !matches!(self.peek(), Token::LParen) {
5338 return Err(self.err(alloc::format!(
5339 "expected '(' after {ctx}, got {:?}",
5340 self.peek()
5341 )));
5342 }
5343 self.advance();
5344 let mut out = Vec::new();
5345 loop {
5346 out.push(self.expect_ident_like()?);
5347 match self.peek() {
5348 Token::Comma => {
5349 self.advance();
5350 }
5351 Token::RParen => {
5352 self.advance();
5353 break;
5354 }
5355 other => {
5356 return Err(self.err(alloc::format!(
5357 "expected ',' or ')' in {ctx} list, got {other:?}"
5358 )));
5359 }
5360 }
5361 }
5362 if out.is_empty() {
5363 return Err(self.err(alloc::format!("{ctx} requires at least one column")));
5364 }
5365 Ok(out)
5366 }
5367
5368 fn peek_constraint_or_fk_start(&self) -> bool {
5373 let is_constraint_kw = matches!(
5374 self.peek(),
5375 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
5376 );
5377 let is_foreign_kw = matches!(
5378 self.peek(),
5379 Token::Ident(s) if s.eq_ignore_ascii_case("foreign")
5380 );
5381 is_constraint_kw || is_foreign_kw
5382 }
5383
5384 fn parse_table_level_fk(&mut self) -> Result<ForeignKeyConstraint, ParseError> {
5388 let mut name: Option<String> = None;
5389 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
5390 self.advance();
5391 name = Some(self.expect_ident_like()?);
5392 }
5393 match self.advance() {
5395 Token::Ident(s) if s.eq_ignore_ascii_case("foreign") => {}
5396 other => return Err(self.err(format!("expected FOREIGN, got {other:?}"))),
5397 }
5398 match self.advance() {
5400 Token::Ident(s) if s.eq_ignore_ascii_case("key") => {}
5401 other => return Err(self.err(format!("expected KEY after FOREIGN, got {other:?}"))),
5402 }
5403 if !matches!(self.peek(), Token::LParen) {
5405 return Err(self.err(format!(
5406 "expected '(' after FOREIGN KEY, got {:?}",
5407 self.peek()
5408 )));
5409 }
5410 self.advance();
5411 let mut columns = Vec::new();
5412 loop {
5413 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 FK column list, got {other:?}"
5425 )));
5426 }
5427 }
5428 }
5429 if columns.is_empty() {
5430 return Err(self.err("FOREIGN KEY requires at least one column".into()));
5431 }
5432 let (parent_table, parent_columns, on_delete, on_update) =
5433 self.parse_references_tail(columns.len())?;
5434 Ok(ForeignKeyConstraint {
5435 name,
5436 columns,
5437 parent_table,
5438 parent_columns,
5439 on_delete,
5440 on_update,
5441 })
5442 }
5443
5444 fn parse_references_tail(
5449 &mut self,
5450 expected_arity: usize,
5451 ) -> Result<(String, Vec<String>, FkAction, FkAction), ParseError> {
5452 match self.advance() {
5453 Token::Ident(s) if s.eq_ignore_ascii_case("references") => {}
5454 other => return Err(self.err(format!("expected REFERENCES, got {other:?}"))),
5455 }
5456 let parent_table = self.expect_ident_like()?;
5457 let mut parent_columns: Vec<String> = Vec::new();
5458 if matches!(self.peek(), Token::LParen) {
5459 self.advance();
5460 loop {
5461 parent_columns.push(self.expect_ident_like()?);
5462 match self.peek() {
5463 Token::Comma => {
5464 self.advance();
5465 }
5466 Token::RParen => {
5467 self.advance();
5468 break;
5469 }
5470 other => {
5471 return Err(self.err(format!(
5472 "expected ',' or ')' in REFERENCES column list, got {other:?}"
5473 )));
5474 }
5475 }
5476 }
5477 }
5478 if !parent_columns.is_empty() && parent_columns.len() != expected_arity {
5479 return Err(self.err(format!(
5480 "FK arity mismatch: {} local column(s) vs {} parent column(s)",
5481 expected_arity,
5482 parent_columns.len()
5483 )));
5484 }
5485 let mut on_delete = FkAction::Restrict;
5498 let mut on_update = FkAction::Restrict;
5499 let mut seen_on_delete = false;
5500 let mut seen_on_update = false;
5501 loop {
5502 let before = self.pos;
5504 self.consume_optional_deferrable_clauses()?;
5505 if self.pos != before {
5506 continue;
5507 }
5508 if !matches!(self.peek(), Token::On) {
5510 break;
5511 }
5512 self.advance();
5513 let which = self.advance();
5514 let action = self.parse_fk_action()?;
5515 match which {
5516 Token::Ident(ref s) if s.eq_ignore_ascii_case("delete") => {
5517 if seen_on_delete {
5518 return Err(self.err("ON DELETE specified twice".into()));
5519 }
5520 seen_on_delete = true;
5521 on_delete = action;
5522 }
5523 Token::Ident(ref s) if s.eq_ignore_ascii_case("update") => {
5524 if seen_on_update {
5525 return Err(self.err("ON UPDATE specified twice".into()));
5526 }
5527 seen_on_update = true;
5528 on_update = action;
5529 }
5530 other => {
5531 return Err(
5532 self.err(format!("expected DELETE or UPDATE after ON, got {other:?}"))
5533 );
5534 }
5535 }
5536 }
5537 Ok((parent_table, parent_columns, on_delete, on_update))
5538 }
5539
5540 fn parse_fk_action(&mut self) -> Result<FkAction, ParseError> {
5543 match self.advance() {
5544 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => Ok(FkAction::Cascade),
5545 Token::Ident(s) if s.eq_ignore_ascii_case("restrict") => Ok(FkAction::Restrict),
5546 Token::Ident(s) if s.eq_ignore_ascii_case("set") => match self.advance() {
5547 Token::Null => Ok(FkAction::SetNull),
5548 Token::Default => Ok(FkAction::SetDefault),
5549 other => Err(self.err(format!(
5550 "expected NULL or DEFAULT after SET in FK action, got {other:?}"
5551 ))),
5552 },
5553 Token::Ident(s) if s.eq_ignore_ascii_case("no") => match self.advance() {
5554 Token::Ident(s) if s.eq_ignore_ascii_case("action") => Ok(FkAction::NoAction),
5555 other => Err(self.err(format!(
5556 "expected ACTION after NO in FK action, got {other:?}"
5557 ))),
5558 },
5559 other => Err(self.err(format!(
5560 "expected CASCADE | RESTRICT | SET NULL | SET DEFAULT | NO ACTION, got {other:?}"
5561 ))),
5562 }
5563 }
5564
5565 fn consume_if_not_exists(&mut self) -> bool {
5568 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
5572 if !looks_like_if {
5573 return false;
5574 }
5575 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Not)) {
5578 return false;
5579 }
5580 if !matches!(
5581 self.tokens.get(self.pos + 2),
5582 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
5583 ) {
5584 return false;
5585 }
5586 self.advance(); self.advance(); self.advance(); true
5590 }
5591
5592 fn consume_if_exists(&mut self) -> bool {
5596 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
5597 if !looks_like_if {
5598 return false;
5599 }
5600 if !matches!(
5601 self.tokens.get(self.pos + 1),
5602 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
5603 ) {
5604 return false;
5605 }
5606 self.advance(); self.advance(); true
5609 }
5610
5611 fn parse_optional_nulls_placement(&mut self) -> Result<Option<bool>, ParseError> {
5619 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("nulls")) {
5620 return Ok(None);
5621 }
5622 self.advance();
5623 match self.advance() {
5624 Token::Ident(s) if s.eq_ignore_ascii_case("first") => Ok(Some(true)),
5625 Token::Ident(s) if s.eq_ignore_ascii_case("last") => Ok(Some(false)),
5626 other => Err(self.err(alloc::format!(
5627 "expected FIRST or LAST after NULLS, got {other:?}"
5628 ))),
5629 }
5630 }
5631
5632 fn consume_optional_index_column_qualifiers(&mut self) {
5633 loop {
5634 match self.peek() {
5635 Token::Asc | Token::Desc => {
5636 self.advance();
5637 }
5638 Token::Ident(s) if s.eq_ignore_ascii_case("nulls") => {
5639 let look = self.tokens.get(self.pos + 1);
5640 if matches!(
5641 look,
5642 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("first")
5643 || k.eq_ignore_ascii_case("last")
5644 ) {
5645 self.advance();
5646 self.advance();
5647 } else {
5648 break;
5649 }
5650 }
5651 _ => break,
5652 }
5653 }
5654 }
5655
5656 fn parse_create_index_stmt_after_create(
5657 &mut self,
5658 is_unique: bool,
5659 ) -> Result<Statement, ParseError> {
5660 debug_assert!(matches!(self.peek(), Token::Index));
5662 self.advance();
5663 let if_not_exists = self.consume_if_not_exists();
5664 let name = self.expect_ident_like()?;
5665 if !matches!(self.peek(), Token::On) {
5666 return Err(self.err(format!(
5667 "expected ON after CREATE INDEX <name>, got {:?}",
5668 self.peek()
5669 )));
5670 }
5671 self.advance();
5672 let table = self.expect_ident_like()?;
5673 let method = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
5678 self.advance();
5679 let m = self.expect_ident_like()?;
5680 match m.to_ascii_lowercase().as_str() {
5681 "hnsw" => IndexMethod::Hnsw,
5682 "btree" => IndexMethod::BTree,
5683 "brin" => IndexMethod::Brin,
5684 "gin" => IndexMethod::Gin,
5689 "gist" | "spgist" | "hash" => IndexMethod::BTree,
5698 "ivfflat" => IndexMethod::Hnsw,
5706 other => {
5707 return Err(self.err(alloc::format!(
5708 "unknown index method {other:?}; supported: hnsw, btree, brin, gin (gist/spgist/hash accepted as BTree fallback)"
5709 )));
5710 }
5711 }
5712 } else {
5713 IndexMethod::BTree
5714 };
5715 if !matches!(self.peek(), Token::LParen) {
5716 return Err(self.err(format!(
5717 "expected '(' before indexed column, got {:?}",
5718 self.peek()
5719 )));
5720 }
5721 self.advance();
5722 let mut opclass: Option<String> = None;
5731 let (column, expression): (String, Option<Expr>) = match self.peek().clone() {
5732 Token::Ident(s) | Token::QuotedIdent(s)
5739 if matches!(
5740 self.tokens.get(self.pos + 1),
5741 Some(Token::RParen | Token::Comma)
5742 ) =>
5743 {
5744 self.advance();
5745 (s, None)
5746 }
5747 Token::Ident(s) | Token::QuotedIdent(s)
5761 if matches!(
5762 self.tokens.get(self.pos + 1),
5763 Some(Token::Ident(_) | Token::QuotedIdent(_))
5764 ) && matches!(self.tokens.get(self.pos + 2), Some(Token::Dot))
5765 && matches!(
5766 self.tokens.get(self.pos + 3),
5767 Some(Token::Ident(op) | Token::QuotedIdent(op))
5768 if is_vector_opclass_name(op)
5769 ) =>
5770 {
5771 self.advance(); self.advance(); self.advance(); let op_tok = self.advance();
5775 if let Token::Ident(op) | Token::QuotedIdent(op) = op_tok {
5776 opclass = Some(op.to_ascii_lowercase());
5777 }
5778 (s, None)
5779 }
5780 Token::Ident(s) | Token::QuotedIdent(s)
5781 if matches!(
5782 self.tokens.get(self.pos + 1),
5783 Some(Token::Ident(op) | Token::QuotedIdent(op))
5784 if is_vector_opclass_name(op)
5785 ) =>
5786 {
5787 self.advance(); let op_tok = self.advance();
5791 if let Token::Ident(op) | Token::QuotedIdent(op) = op_tok {
5792 opclass = Some(op.to_ascii_lowercase());
5793 }
5794 (s, None)
5795 }
5796 Token::Ident(_) | Token::QuotedIdent(_) => {
5797 let key_expr = self.parse_expr(0)?;
5798 let primary = extract_first_column(&key_expr).ok_or_else(|| {
5799 self.err("expression index key must reference at least one column".into())
5800 })?;
5801 (primary, Some(key_expr))
5802 }
5803 other => {
5804 return Err(self.err(format!(
5805 "expected column ident or expression, got {other:?}"
5806 )));
5807 }
5808 };
5809 let mut extra_columns: Vec<String> = Vec::new();
5818 self.consume_optional_index_column_qualifiers();
5820 while matches!(self.peek(), Token::Comma) {
5821 self.advance();
5822 let extra = self.expect_ident_like()?;
5823 self.consume_optional_index_column_qualifiers();
5824 extra_columns.push(extra);
5825 }
5826 if !matches!(self.peek(), Token::RParen) {
5827 return Err(self.err(format!(
5828 "expected ')' after indexed column / expression, got {:?}",
5829 self.peek()
5830 )));
5831 }
5832 self.advance();
5833 let included_columns = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("include"))
5837 {
5838 self.advance();
5839 if !matches!(self.peek(), Token::LParen) {
5840 return Err(self.err(format!("expected '(' after INCLUDE, got {:?}", self.peek())));
5841 }
5842 self.advance();
5843 let mut cols = Vec::new();
5844 loop {
5845 cols.push(self.expect_ident_like()?);
5846 match self.peek() {
5847 Token::Comma => {
5848 self.advance();
5849 }
5850 Token::RParen => {
5851 self.advance();
5852 break;
5853 }
5854 other => {
5855 return Err(self.err(format!(
5856 "expected ',' or ')' in INCLUDE list, got {other:?}"
5857 )));
5858 }
5859 }
5860 }
5861 cols
5862 } else {
5863 Vec::new()
5864 };
5865 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
5871 self.advance();
5872 if !matches!(self.peek(), Token::LParen) {
5873 return Err(self.err(format!(
5874 "expected '(' after WITH in CREATE INDEX, got {:?}",
5875 self.peek()
5876 )));
5877 }
5878 self.advance();
5879 loop {
5880 if matches!(self.peek(), Token::RParen) {
5881 self.advance();
5882 break;
5883 }
5884 let _ = self.advance(); if matches!(self.peek(), Token::Eq) {
5887 self.advance();
5888 let _ = self.advance(); }
5890 match self.peek() {
5891 Token::Comma => {
5892 self.advance();
5893 }
5894 Token::RParen => {
5895 self.advance();
5896 break;
5897 }
5898 other => {
5899 return Err(self.err(format!(
5900 "expected ',' or ')' in WITH (…) clause, got {other:?}"
5901 )));
5902 }
5903 }
5904 }
5905 }
5906 let partial_predicate = if matches!(self.peek(), Token::Where) {
5908 self.advance();
5909 Some(self.parse_expr(0)?)
5910 } else {
5911 None
5912 };
5913 if is_unique && !matches!(method, IndexMethod::BTree) {
5918 return Err(self.err(alloc::format!(
5919 "UNIQUE is only supported on BTree indexes, got USING {:?}",
5920 method
5921 )));
5922 }
5923 Ok(Statement::CreateIndex(CreateIndexStatement {
5924 name,
5925 table,
5926 column,
5927 method,
5928 if_not_exists,
5929 included_columns,
5930 partial_predicate,
5931 extra_columns: extra_columns.clone(),
5932 expression,
5933 is_unique,
5934 opclass,
5935 }))
5936 }
5937
5938 fn parse_column_def_with_fk(
5943 &mut self,
5944 ) -> Result<(ColumnDef, Option<ForeignKeyConstraint>), ParseError> {
5945 let col = self.parse_column_def()?;
5946 let inline_references = matches!(
5948 self.peek(),
5949 Token::Ident(s) if s.eq_ignore_ascii_case("references")
5950 );
5951 if !inline_references {
5952 return Ok((col, None));
5953 }
5954 let (parent_table, parent_columns, on_delete, on_update) = self.parse_references_tail(1)?;
5955 let fk = ForeignKeyConstraint {
5956 name: None,
5957 columns: vec![col.name.clone()],
5958 parent_table,
5959 parent_columns,
5960 on_delete,
5961 on_update,
5962 };
5963 Ok((col, Some(fk)))
5964 }
5965
5966 fn parse_column_type_name(&mut self) -> Result<ColumnTypeName, ParseError> {
5974 let (ty, _, _, _, _, _, _, _) = self.parse_type_with_implied_flags()?;
5975 Ok(ty)
5976 }
5977
5978 #[allow(clippy::type_complexity)]
5979 fn parse_type_with_implied_flags(
5980 &mut self,
5981 ) -> Result<
5982 (
5983 ColumnTypeName,
5984 bool,
5985 bool,
5986 Option<String>,
5987 Collation,
5988 bool,
5989 Option<Vec<String>>,
5993 Option<Vec<String>>,
5996 ),
5997 ParseError,
5998 > {
5999 let mut ty_ident = match self.advance() {
6000 Token::Ident(s) => s,
6001 other => {
6002 return Err(ParseError {
6003 message: format!("expected column type, got {other:?}"),
6004 token_pos: self.pos.saturating_sub(1),
6005 });
6006 }
6007 };
6008 while matches!(self.peek(), Token::Dot) {
6013 self.advance();
6014 ty_ident = self.expect_ident_like()?;
6015 }
6016 let mut implied_auto_increment = false;
6017 let mut implied_not_null = false;
6018 let mut user_type_ref: Option<String> = None;
6019 let mut inline_enum_variants: Option<Vec<String>> = None;
6024 let mut inline_set_variants: Option<Vec<String>> = None;
6026 let mut ty = match ty_ident.as_str() {
6027 "smallserial" | "serial2" => {
6029 implied_auto_increment = true;
6030 implied_not_null = true;
6031 ColumnTypeName::SmallInt
6032 }
6033 "serial" | "serial4" => {
6034 implied_auto_increment = true;
6035 implied_not_null = true;
6036 ColumnTypeName::Int
6037 }
6038 "bigserial" | "serial8" => {
6039 implied_auto_increment = true;
6040 implied_not_null = true;
6041 ColumnTypeName::BigInt
6042 }
6043 "smallint" => {
6049 self.consume_optional_paren_size();
6054 ColumnTypeName::SmallInt
6055 }
6056 "tinyint" => {
6067 let width = self.peek_optional_paren_size_value();
6068 self.consume_optional_paren_size();
6069 if width == Some(1) {
6070 ColumnTypeName::Bool
6071 } else {
6072 ColumnTypeName::SmallInt
6073 }
6074 }
6075 "int" | "integer" | "mediumint" => {
6076 self.consume_optional_paren_size();
6077 ColumnTypeName::Int
6078 }
6079 "bigint" => {
6080 self.consume_optional_paren_size();
6081 ColumnTypeName::BigInt
6082 }
6083 "float" | "double" | "real" => {
6088 if ty_ident.eq_ignore_ascii_case("double")
6089 && matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("precision"))
6090 {
6091 self.advance();
6092 }
6093 ColumnTypeName::Float
6094 }
6095 "float4" | "float8" => ColumnTypeName::Float,
6097 "text" => ColumnTypeName::Text,
6098 "bool" | "boolean" => ColumnTypeName::Bool,
6099 "varchar" => ColumnTypeName::Varchar(self.parse_paren_size("VARCHAR")?),
6100 "char" => ColumnTypeName::Char(self.parse_paren_size("CHAR")?),
6101 "vector" => {
6102 let dim = self.parse_paren_size("VECTOR")?;
6103 let encoding = self.parse_optional_vector_encoding()?;
6104 ColumnTypeName::Vector { dim, encoding }
6105 }
6106 "numeric" => {
6107 let (precision, scale) = self.parse_optional_numeric_params()?;
6108 ColumnTypeName::Numeric(precision, scale)
6109 }
6110 "date" => ColumnTypeName::Date,
6111 "timestamp" | "datetime" => {
6114 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with"))
6120 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("time"))
6121 && matches!(self.tokens.get(self.pos + 2), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("zone"))
6122 {
6123 self.advance(); self.advance(); self.advance(); ColumnTypeName::Timestamptz
6127 } else if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("without"))
6128 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("time"))
6129 && matches!(self.tokens.get(self.pos + 2), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("zone"))
6130 {
6131 self.advance(); self.advance(); self.advance(); ColumnTypeName::Timestamp
6135 } else {
6136 self.consume_optional_paren_size();
6140 ColumnTypeName::Timestamp
6141 }
6142 }
6143 "timestamptz" => ColumnTypeName::Timestamptz,
6147 "json" => ColumnTypeName::Json,
6152 "jsonb" => ColumnTypeName::Jsonb,
6153 "bytea" | "bytes" => ColumnTypeName::Bytes,
6159 "inet" | "cidr" | "macaddr" => ColumnTypeName::Text,
6169 "tsvector" => ColumnTypeName::TsVector,
6174 "tsquery" => ColumnTypeName::TsQuery,
6175 "uuid" => ColumnTypeName::Uuid,
6179 "time" => ColumnTypeName::Time,
6182 "year" => ColumnTypeName::Year,
6185 "timetz" => ColumnTypeName::TimeTz,
6188 "money" => ColumnTypeName::Money,
6191 "int4range" => ColumnTypeName::Range(RangeKindAst::Int4),
6193 "int8range" => ColumnTypeName::Range(RangeKindAst::Int8),
6194 "numrange" => ColumnTypeName::Range(RangeKindAst::Num),
6195 "tsrange" => ColumnTypeName::Range(RangeKindAst::Ts),
6196 "tstzrange" => ColumnTypeName::Range(RangeKindAst::TsTz),
6197 "daterange" => ColumnTypeName::Range(RangeKindAst::Date),
6198 "hstore" => ColumnTypeName::Hstore,
6200 "enum" => {
6206 if !matches!(self.peek(), Token::LParen) {
6208 return Err(self.err(alloc::format!(
6209 "expected '(' after ENUM, got {:?}",
6210 self.peek()
6211 )));
6212 }
6213 self.advance();
6214 let mut variants: Vec<String> = Vec::new();
6215 loop {
6216 match self.advance() {
6217 Token::String(s) => variants.push(s),
6218 other => {
6219 return Err(self.err(alloc::format!(
6220 "ENUM(...) expects string literal variants, got {other:?}"
6221 )));
6222 }
6223 }
6224 match self.peek() {
6225 Token::Comma => {
6226 self.advance();
6227 continue;
6228 }
6229 Token::RParen => {
6230 self.advance();
6231 break;
6232 }
6233 other => {
6234 return Err(self.err(alloc::format!(
6235 "expected ',' or ')' in ENUM(...), got {other:?}"
6236 )));
6237 }
6238 }
6239 }
6240 if variants.is_empty() {
6241 return Err(self.err("ENUM(...) must declare at least one variant".into()));
6242 }
6243 inline_enum_variants = Some(variants);
6244 ColumnTypeName::Text
6247 }
6248 "set" => {
6252 if !matches!(self.peek(), Token::LParen) {
6253 return Err(self.err(alloc::format!(
6254 "expected '(' after SET, got {:?}",
6255 self.peek()
6256 )));
6257 }
6258 self.advance();
6259 let mut variants: Vec<String> = Vec::new();
6260 loop {
6261 match self.advance() {
6262 Token::String(s) => variants.push(s),
6263 other => {
6264 return Err(self.err(alloc::format!(
6265 "SET(...) expects string literal variants, got {other:?}"
6266 )));
6267 }
6268 }
6269 match self.peek() {
6270 Token::Comma => {
6271 self.advance();
6272 continue;
6273 }
6274 Token::RParen => {
6275 self.advance();
6276 break;
6277 }
6278 other => {
6279 return Err(self.err(alloc::format!(
6280 "expected ',' or ')' in SET(...), got {other:?}"
6281 )));
6282 }
6283 }
6284 }
6285 if variants.is_empty() {
6286 return Err(self.err("SET(...) must declare at least one variant".into()));
6287 }
6288 inline_set_variants = Some(variants);
6289 ColumnTypeName::Text
6290 }
6291 _other => {
6292 user_type_ref = Some(ty_ident.clone());
6298 ColumnTypeName::Text
6299 }
6300 };
6301 let is_unsigned = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unsigned"))
6311 {
6312 self.advance();
6313 true
6314 } else {
6315 false
6316 };
6317 let mut collation = Collation::Binary;
6333 loop {
6334 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("character"))
6335 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("set"))
6336 {
6337 self.advance(); self.advance(); if matches!(
6340 self.peek(),
6341 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
6342 ) {
6343 self.advance();
6344 }
6345 continue;
6346 }
6347 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("collate")) {
6348 self.advance(); let read_collation_atom = |this: &mut Self| -> Option<alloc::string::String> {
6355 match this.peek().clone() {
6356 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => {
6357 this.advance();
6358 Some(s)
6359 }
6360 Token::Default => {
6361 this.advance();
6362 Some(alloc::string::String::from("default"))
6363 }
6364 _ => None,
6365 }
6366 };
6367 let raw = if let Some(head) = read_collation_atom(self) {
6368 if matches!(self.peek(), Token::Dot) {
6370 self.advance();
6371 let tail = read_collation_atom(self).unwrap_or_default();
6372 alloc::format!("{head}.{tail}")
6373 } else {
6374 head
6375 }
6376 } else {
6377 alloc::string::String::new()
6378 };
6379 if !raw.is_empty() {
6380 let parsed = Collation::from_collation_name(&raw);
6381 if parsed != Collation::Binary {
6387 collation = parsed;
6388 }
6389 }
6390 continue;
6391 }
6392 break;
6393 }
6394 if matches!(self.peek(), Token::LBracket) {
6399 self.advance();
6400 if !matches!(self.peek(), Token::RBracket) {
6401 return Err(self.err(alloc::format!(
6402 "TEXT[] takes no dimension; got {:?}",
6403 self.peek()
6404 )));
6405 }
6406 self.advance();
6407 ty = match ty {
6411 ColumnTypeName::Text => ColumnTypeName::TextArray,
6412 ColumnTypeName::Int => ColumnTypeName::IntArray,
6413 ColumnTypeName::BigInt => ColumnTypeName::BigIntArray,
6414 other => {
6415 return Err(self.err(alloc::format!(
6416 "v7.11 supports TEXT[] / INT[] / BIGINT[] only; got {other:?}[]"
6417 )));
6418 }
6419 };
6420 if matches!(self.peek(), Token::LBracket) {
6423 self.advance();
6424 if !matches!(self.peek(), Token::RBracket) {
6425 return Err(self.err(alloc::format!(
6426 "TYPE[][] second dimension takes no size; got {:?}",
6427 self.peek()
6428 )));
6429 }
6430 self.advance();
6431 ty = match ty {
6432 ColumnTypeName::IntArray => ColumnTypeName::IntArray2D,
6433 ColumnTypeName::BigIntArray => ColumnTypeName::BigIntArray2D,
6434 ColumnTypeName::TextArray => ColumnTypeName::TextArray2D,
6435 other => {
6436 return Err(self.err(alloc::format!(
6437 "v7.17 2D arrays support INT[][] / BIGINT[][] / \
6438 TEXT[][] only; got {other:?}"
6439 )));
6440 }
6441 };
6442 }
6443 }
6444 Ok((
6445 ty,
6446 implied_auto_increment,
6447 implied_not_null,
6448 user_type_ref,
6449 collation,
6450 is_unsigned,
6451 inline_enum_variants,
6452 inline_set_variants,
6453 ))
6454 }
6455
6456 fn parse_column_def(&mut self) -> Result<ColumnDef, ParseError> {
6457 if let Token::Ident(s) = self.peek()
6466 && [
6467 "unique",
6468 "primary",
6469 "foreign",
6470 "constraint",
6471 "check",
6472 "references",
6473 "exclude",
6474 ]
6475 .iter()
6476 .any(|kw| s.eq_ignore_ascii_case(kw))
6477 {
6478 return Err(self.err(alloc::format!(
6479 "unexpected reserved keyword '{s}' at start of column definition \
6480 (malformed table constraint?)"
6481 )));
6482 }
6483 let name = self.expect_ident_like()?;
6484 let (
6485 ty,
6486 implied_auto_increment,
6487 implied_not_null,
6488 user_type_ref,
6489 collation,
6490 is_unsigned,
6491 inline_enum_variants,
6492 inline_set_variants,
6493 ) = self.parse_type_with_implied_flags()?;
6494 let mut default: Option<Expr> = None;
6498 let mut nullable = !implied_not_null;
6499 let mut nullability_seen = implied_not_null;
6500 let mut auto_increment = implied_auto_increment;
6501 let mut is_primary_key = false;
6502 let mut is_unique = false;
6503 let mut check: Option<Expr> = None;
6504 let mut on_update_runtime: Option<Expr> = None;
6505 loop {
6506 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
6512 self.advance();
6513 let _name = self.expect_ident_like()?;
6514 continue;
6515 }
6516 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("generated")) {
6526 self.advance();
6527 match self.peek().clone() {
6528 Token::Ident(s) if s.eq_ignore_ascii_case("always") => {
6529 self.advance();
6530 }
6531 Token::By => {
6533 self.advance();
6534 if !matches!(self.peek(), Token::Default) {
6535 return Err(self.err(alloc::format!(
6536 "expected DEFAULT after GENERATED BY, got {:?}",
6537 self.peek()
6538 )));
6539 }
6540 self.advance();
6541 }
6542 other => {
6543 return Err(self.err(alloc::format!(
6544 "expected ALWAYS or BY DEFAULT after GENERATED, got {other:?}"
6545 )));
6546 }
6547 }
6548 if !matches!(self.peek(), Token::As) {
6549 return Err(self.err(alloc::format!(
6550 "expected AS after GENERATED ALWAYS/BY DEFAULT, got {:?}",
6551 self.peek()
6552 )));
6553 }
6554 self.advance();
6555 if matches!(self.peek(), Token::LParen) {
6556 return Err(self.err(
6557 "generated expression columns (GENERATED … AS (expr) STORED) \
6558 are not supported"
6559 .into(),
6560 ));
6561 }
6562 self.expect_keyword_ident("identity")?;
6563 if matches!(self.peek(), Token::LParen) {
6567 let mut depth = 0usize;
6568 loop {
6569 match self.advance() {
6570 Token::LParen => depth += 1,
6571 Token::RParen => {
6572 depth -= 1;
6573 if depth == 0 {
6574 break;
6575 }
6576 }
6577 Token::Eof => {
6578 return Err(self.err(
6579 "unterminated sequence-options parens after IDENTITY".into(),
6580 ));
6581 }
6582 _ => {}
6583 }
6584 }
6585 }
6586 auto_increment = true;
6587 nullable = false;
6589 continue;
6590 }
6591 if matches!(self.peek(), Token::On)
6596 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("update"))
6597 {
6598 self.advance(); self.advance(); let next = self.peek().clone();
6602 match next {
6603 Token::Ident(s) | Token::QuotedIdent(s)
6604 if s.eq_ignore_ascii_case("current_timestamp") =>
6605 {
6606 self.advance();
6607 if matches!(self.peek(), Token::LParen) {
6609 self.advance();
6610 if !matches!(self.peek(), Token::Integer(_)) {
6611 return Err(self.err(alloc::format!(
6612 "expected integer precision inside CURRENT_TIMESTAMP(…), got {:?}",
6613 self.peek()
6614 )));
6615 }
6616 self.advance();
6617 if !matches!(self.peek(), Token::RParen) {
6618 return Err(self.err(alloc::format!(
6619 "expected ')' after CURRENT_TIMESTAMP precision, got {:?}",
6620 self.peek()
6621 )));
6622 }
6623 self.advance();
6624 }
6625 on_update_runtime = Some(Expr::FunctionCall {
6626 name: "now".into(),
6627 args: Vec::new(),
6628 });
6629 continue;
6630 }
6631 other => {
6632 return Err(self.err(alloc::format!(
6633 "v7.17 only supports ON UPDATE CURRENT_TIMESTAMP, got {other:?}"
6634 )));
6635 }
6636 }
6637 }
6638 if matches!(self.peek(), Token::Default) {
6639 if default.is_some() {
6640 return Err(self.err("DEFAULT specified twice".into()));
6641 }
6642 self.advance();
6643 default = Some(self.parse_expr(0)?);
6644 continue;
6645 }
6646 if matches!(self.peek(), Token::Not) {
6647 if nullability_seen {
6648 return Err(self.err("NOT NULL specified twice".into()));
6649 }
6650 self.advance();
6651 if !matches!(self.peek(), Token::Null) {
6652 return Err(self.err(format!(
6653 "expected NULL after NOT in column def, got {:?}",
6654 self.peek()
6655 )));
6656 }
6657 self.advance();
6658 nullable = false;
6659 nullability_seen = true;
6660 continue;
6661 }
6662 if matches!(self.peek(), Token::Null) {
6668 if nullability_seen && !nullable {
6669 return Err(self.err("column declared NOT NULL then NULL — pick one".into()));
6670 }
6671 self.advance();
6672 nullable = true;
6673 nullability_seen = true;
6674 continue;
6675 }
6676 if let Token::Ident(s) = self.peek()
6679 && (s.eq_ignore_ascii_case("auto_increment")
6680 || s.eq_ignore_ascii_case("autoincrement"))
6681 {
6682 if auto_increment {
6683 return Err(self.err("AUTO_INCREMENT specified twice".into()));
6684 }
6685 self.advance();
6686 auto_increment = true;
6687 continue;
6688 }
6689 if let Token::Ident(s) = self.peek()
6694 && s.eq_ignore_ascii_case("primary")
6695 {
6696 if is_primary_key {
6697 return Err(self.err("PRIMARY KEY specified twice".into()));
6698 }
6699 let next = self.tokens.get(self.pos + 1);
6701 let next_is_key = matches!(
6702 next,
6703 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("key")
6704 );
6705 if !next_is_key {
6706 return Err(self.err(format!(
6707 "expected KEY after PRIMARY in column def, got {:?}",
6708 next
6709 )));
6710 }
6711 self.advance(); self.advance(); is_primary_key = true;
6714 if nullability_seen && nullable {
6715 return Err(self.err(
6716 "column declared NULL but inline PRIMARY KEY implies NOT NULL".into(),
6717 ));
6718 }
6719 nullable = false;
6720 nullability_seen = true;
6721 continue;
6722 }
6723 if let Token::Ident(s) = self.peek()
6727 && s.eq_ignore_ascii_case("unique")
6728 {
6729 if is_unique {
6730 return Err(self.err("UNIQUE specified twice".into()));
6731 }
6732 self.advance();
6733 is_unique = true;
6734 continue;
6735 }
6736 if let Token::Ident(s) = self.peek()
6741 && s.eq_ignore_ascii_case("check")
6742 {
6743 self.advance();
6744 if !matches!(self.peek(), Token::LParen) {
6745 return Err(self.err(alloc::format!(
6746 "expected '(' after CHECK in column def, got {:?}",
6747 self.peek()
6748 )));
6749 }
6750 self.advance();
6751 let pred = self.parse_expr(0)?;
6752 if !matches!(self.peek(), Token::RParen) {
6753 return Err(self.err(alloc::format!(
6754 "expected ')' to close CHECK predicate, got {:?}",
6755 self.peek()
6756 )));
6757 }
6758 self.advance();
6759 check = Some(match check.take() {
6760 Some(prev) => Expr::Binary {
6761 op: BinOp::And,
6762 lhs: Box::new(prev),
6763 rhs: Box::new(pred),
6764 },
6765 None => pred,
6766 });
6767 continue;
6768 }
6769 break;
6770 }
6771 Ok(ColumnDef {
6772 name,
6773 ty,
6774 nullable,
6775 default,
6776 auto_increment,
6777 is_primary_key,
6778 is_unique,
6779 check,
6780 user_type_ref,
6781 on_update_runtime,
6782 collation,
6783 is_unsigned,
6784 inline_enum_variants,
6785 inline_set_variants,
6786 })
6787 }
6788
6789 fn parse_optional_numeric_params(&mut self) -> Result<(u8, u8), ParseError> {
6793 if !matches!(self.peek(), Token::LParen) {
6794 return Ok((0, 0));
6798 }
6799 self.advance();
6800 let precision = match self.advance() {
6801 Token::Integer(n) if (1..=38).contains(&n) => u8::try_from(n).expect("range-checked"),
6802 other => {
6803 return Err(ParseError {
6804 message: format!(
6805 "NUMERIC precision must be an integer in 1..=38, got {other:?}"
6806 ),
6807 token_pos: self.pos.saturating_sub(1),
6808 });
6809 }
6810 };
6811 let scale = if matches!(self.peek(), Token::Comma) {
6812 self.advance();
6813 match self.advance() {
6814 Token::Integer(n) if (0..=i64::from(precision)).contains(&n) => {
6815 u8::try_from(n).expect("range-checked")
6816 }
6817 other => {
6818 return Err(ParseError {
6819 message: format!(
6820 "NUMERIC scale must be a non-negative integer ≤ precision, got {other:?}"
6821 ),
6822 token_pos: self.pos.saturating_sub(1),
6823 });
6824 }
6825 }
6826 } else {
6827 0
6828 };
6829 if !matches!(self.peek(), Token::RParen) {
6830 return Err(self.err(format!(
6831 "expected ')' to close NUMERIC params, got {:?}",
6832 self.peek()
6833 )));
6834 }
6835 self.advance();
6836 Ok((precision, scale))
6837 }
6838
6839 fn parse_optional_vector_encoding(&mut self) -> Result<VecEncoding, ParseError> {
6847 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
6848 return Ok(VecEncoding::F32);
6849 }
6850 let n1 = self.tokens.get(self.pos + 1);
6856 let next_is_encoding = matches!(
6857 n1,
6858 Some(Token::Ident(s))
6859 if s.eq_ignore_ascii_case("sq8") || s.eq_ignore_ascii_case("half")
6860 );
6861 if !next_is_encoding {
6862 return Ok(VecEncoding::F32);
6863 }
6864 self.advance();
6865 let enc_ident = match self.advance() {
6866 Token::Ident(s) => s,
6867 other => {
6868 return Err(self.err(format!(
6869 "expected vector encoding after USING, got {other:?}"
6870 )));
6871 }
6872 };
6873 match enc_ident.to_ascii_lowercase().as_str() {
6874 "sq8" => Ok(VecEncoding::Sq8),
6875 "half" => Ok(VecEncoding::F16),
6878 other => Err(self.err(format!(
6879 "unknown vector encoding {other:?}; supported: SQ8, HALF"
6880 ))),
6881 }
6882 }
6883
6884 fn peek_optional_paren_size_value(&self) -> Option<i64> {
6890 if !matches!(self.peek(), Token::LParen) {
6891 return None;
6892 }
6893 let next = self.tokens.get(self.pos + 1)?;
6894 let n = match next {
6895 Token::Integer(n) => *n,
6896 _ => return None,
6897 };
6898 if !matches!(self.tokens.get(self.pos + 2), Some(Token::RParen)) {
6899 return None;
6900 }
6901 Some(n)
6902 }
6903
6904 fn consume_optional_paren_size(&mut self) {
6908 if !matches!(self.peek(), Token::LParen) {
6909 return;
6910 }
6911 self.advance();
6912 let mut depth = 1usize;
6914 while depth > 0 {
6915 match self.peek() {
6916 Token::LParen => depth += 1,
6917 Token::RParen => depth -= 1,
6918 Token::Eof => return,
6919 _ => {}
6920 }
6921 self.advance();
6922 }
6923 }
6924
6925 fn parse_paren_size(&mut self, label: &str) -> Result<u32, ParseError> {
6926 if !matches!(self.peek(), Token::LParen) {
6927 return Err(self.err(format!("{label} type requires (N), got {:?}", self.peek())));
6928 }
6929 self.advance();
6930 let n = match self.advance() {
6931 Token::Integer(n) if n > 0 => u32::try_from(n).map_err(|_| ParseError {
6932 message: format!("{label} size too large: {n}"),
6933 token_pos: self.pos.saturating_sub(1),
6934 })?,
6935 other => {
6936 return Err(ParseError {
6937 message: format!("expected positive integer {label} size, got {other:?}"),
6938 token_pos: self.pos.saturating_sub(1),
6939 });
6940 }
6941 };
6942 if !matches!(self.peek(), Token::RParen) {
6943 return Err(self.err(format!(
6944 "expected ')' after {label} size, got {:?}",
6945 self.peek()
6946 )));
6947 }
6948 self.advance();
6949 Ok(n)
6950 }
6951
6952 fn parse_insert_stmt(&mut self) -> Result<Statement, ParseError> {
6953 debug_assert!(matches!(self.peek(), Token::Insert));
6954 self.advance();
6955 if !matches!(self.peek(), Token::Into) {
6956 return Err(self.err(format!("expected INTO after INSERT, got {:?}", self.peek())));
6957 }
6958 self.advance();
6959 let table = self.expect_ident_like()?;
6960 let columns = if matches!(self.peek(), Token::LParen) {
6962 self.advance();
6963 let mut names = Vec::new();
6964 loop {
6965 names.push(self.expect_ident_like()?);
6966 match self.peek() {
6967 Token::Comma => {
6968 self.advance();
6969 }
6970 Token::RParen => {
6971 self.advance();
6972 break;
6973 }
6974 other => {
6975 return Err(self.err(format!(
6976 "expected ',' or ')' in INSERT column list, got {other:?}"
6977 )));
6978 }
6979 }
6980 }
6981 Some(names)
6982 } else {
6983 None
6984 };
6985 if matches!(self.peek(), Token::Select) {
6988 let select_stmt = match self.parse_select_stmt()? {
6989 Statement::Select(s) => s,
6990 other => {
6991 return Err(self.err(alloc::format!(
6992 "expected SELECT after INSERT INTO ... target, got {other:?}"
6993 )));
6994 }
6995 };
6996 let on_conflict = self.parse_optional_on_conflict()?;
6997 let returning = self.parse_optional_returning()?;
6998 return Ok(Statement::Insert(InsertStatement {
6999 table,
7000 columns,
7001 rows: Vec::new(),
7002 select_source: Some(Box::new(select_stmt)),
7003 on_conflict,
7004 returning,
7005 }));
7006 }
7007 if !matches!(self.peek(), Token::Values) {
7008 return Err(self.err(format!(
7009 "expected VALUES or SELECT after table name, got {:?}",
7010 self.peek()
7011 )));
7012 }
7013 self.advance();
7014 if !matches!(self.peek(), Token::LParen) {
7015 return Err(self.err(format!("expected '(' after VALUES, got {:?}", self.peek())));
7016 }
7017 let mut rows = Vec::new();
7018 loop {
7019 if !matches!(self.peek(), Token::LParen) {
7021 return Err(self.err(format!(
7022 "expected '(' for next VALUES tuple, got {:?}",
7023 self.peek()
7024 )));
7025 }
7026 self.advance();
7027 let mut tuple = Vec::new();
7028 loop {
7029 tuple.push(self.parse_expr(0)?);
7030 match self.peek() {
7031 Token::Comma => {
7032 self.advance();
7033 }
7034 Token::RParen => {
7035 self.advance();
7036 break;
7037 }
7038 other => {
7039 return Err(self.err(format!(
7040 "expected ',' or ')' in VALUES tuple, got {other:?}"
7041 )));
7042 }
7043 }
7044 }
7045 if tuple.is_empty() {
7046 return Err(self.err("INSERT VALUES tuple requires at least one value".into()));
7047 }
7048 rows.push(tuple);
7049 if matches!(self.peek(), Token::Comma) {
7051 self.advance();
7052 } else {
7053 break;
7054 }
7055 }
7056 let on_conflict = self.parse_optional_on_conflict()?;
7057 let returning = self.parse_optional_returning()?;
7058 Ok(Statement::Insert(InsertStatement {
7059 table,
7060 columns,
7061 rows,
7062 select_source: None,
7063 on_conflict,
7064 returning,
7065 }))
7066 }
7067
7068 fn parse_optional_on_conflict(
7073 &mut self,
7074 ) -> Result<Option<crate::ast::OnConflictClause>, ParseError> {
7075 if !matches!(self.peek(), Token::On) {
7076 return Ok(None);
7077 }
7078 let next_is_conflict = matches!(
7081 self.tokens.get(self.pos + 1),
7082 Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("conflict")
7083 );
7084 if !next_is_conflict {
7085 return Ok(None);
7086 }
7087 self.advance(); self.advance(); let mut target_columns: Vec<String> = Vec::new();
7091 if matches!(self.peek(), Token::LParen) {
7092 self.advance();
7093 loop {
7094 target_columns.push(self.expect_ident_like()?);
7095 match self.peek() {
7096 Token::Comma => {
7097 self.advance();
7098 }
7099 Token::RParen => {
7100 self.advance();
7101 break;
7102 }
7103 other => {
7104 return Err(self.err(alloc::format!(
7105 "expected ',' or ')' in ON CONFLICT target list, got {other:?}"
7106 )));
7107 }
7108 }
7109 }
7110 }
7111 match self.advance() {
7113 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {}
7114 other => {
7115 return Err(self.err(alloc::format!(
7116 "expected DO after ON CONFLICT [(…)], got {other:?}"
7117 )));
7118 }
7119 }
7120 let action = match self.advance() {
7122 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing") => {
7123 crate::ast::OnConflictAction::Nothing
7124 }
7125 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
7126 self.parse_on_conflict_update_action()?
7127 }
7128 other => {
7129 return Err(self.err(alloc::format!(
7130 "expected NOTHING or UPDATE after ON CONFLICT DO, got {other:?}"
7131 )));
7132 }
7133 };
7134 Ok(Some(crate::ast::OnConflictClause {
7135 target_columns,
7136 action,
7137 }))
7138 }
7139
7140 fn parse_on_conflict_update_action(
7144 &mut self,
7145 ) -> Result<crate::ast::OnConflictAction, ParseError> {
7146 match self.advance() {
7148 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {}
7149 other => {
7150 return Err(self.err(alloc::format!(
7151 "expected SET after ON CONFLICT DO UPDATE, got {other:?}"
7152 )));
7153 }
7154 }
7155 let mut assignments: Vec<(String, Expr)> = Vec::new();
7156 loop {
7157 let col = self.expect_ident_like()?;
7158 if !matches!(self.peek(), Token::Eq) {
7159 return Err(self.err(alloc::format!(
7160 "expected `=` after column in ON CONFLICT DO UPDATE SET, got {:?}",
7161 self.peek()
7162 )));
7163 }
7164 self.advance();
7165 let value = self.parse_expr(0)?;
7166 assignments.push((col, value));
7167 if matches!(self.peek(), Token::Comma) {
7168 self.advance();
7169 continue;
7170 }
7171 break;
7172 }
7173 let where_ = if matches!(self.peek(), Token::Where) {
7174 self.advance();
7175 Some(self.parse_expr(0)?)
7176 } else {
7177 None
7178 };
7179 Ok(crate::ast::OnConflictAction::Update {
7180 assignments,
7181 where_,
7182 })
7183 }
7184
7185 fn parse_select_list(&mut self) -> Result<Vec<SelectItem>, ParseError> {
7186 let mut items = Vec::new();
7187 loop {
7188 items.push(self.parse_select_item()?);
7189 if matches!(self.peek(), Token::Comma) {
7190 self.advance();
7191 } else {
7192 break;
7193 }
7194 }
7195 Ok(items)
7196 }
7197
7198 fn parse_select_item(&mut self) -> Result<SelectItem, ParseError> {
7199 if matches!(self.peek(), Token::Star) {
7200 self.advance();
7201 return Ok(SelectItem::Wildcard);
7202 }
7203 let expr = self.parse_expr(0)?;
7204 let alias = self.parse_optional_alias();
7205 Ok(SelectItem::Expr { expr, alias })
7206 }
7207
7208 fn parse_table_ref(&mut self) -> Result<TableRef, ParseError> {
7209 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("lateral"))
7215 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
7216 {
7217 self.advance(); self.advance(); let inner = match self.parse_one_statement()? {
7221 Statement::Select(s) => s,
7222 other => {
7223 return Err(self.err(alloc::format!(
7224 "expected SELECT inside LATERAL ( … ), got {other:?}"
7225 )));
7226 }
7227 };
7228 if !matches!(self.peek(), Token::RParen) {
7229 return Err(self.err(alloc::format!(
7230 "expected ')' after LATERAL subquery, got {:?}",
7231 self.peek()
7232 )));
7233 }
7234 self.advance();
7235 let alias_ident = self.parse_optional_alias();
7236 let name = alias_ident.clone().unwrap_or_else(|| "lateral".to_string());
7237 return Ok(TableRef {
7238 name,
7239 alias: alias_ident,
7240 as_of_segment: None,
7241 unnest_expr: None,
7242 unnest_column_aliases: Vec::new(),
7243 generate_series_args: None,
7244 lateral_subquery: Some(Box::new(inner)),
7245 });
7246 }
7247 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unnest"))
7251 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
7252 {
7253 self.advance(); self.advance(); let expr = self.parse_expr(0)?;
7256 if !matches!(self.peek(), Token::RParen) {
7257 return Err(self.err(alloc::format!(
7258 "expected ')' after unnest() argument, got {:?}",
7259 self.peek()
7260 )));
7261 }
7262 self.advance();
7263 let (alias_ident, unnest_column_aliases) = self.parse_optional_alias_with_columns();
7264 let name = alias_ident.clone().unwrap_or_else(|| "unnest".to_string());
7265 return Ok(TableRef {
7266 name,
7267 alias: alias_ident,
7268 as_of_segment: None,
7269 unnest_expr: Some(Box::new(expr)),
7270 unnest_column_aliases,
7271 generate_series_args: None,
7272 lateral_subquery: None,
7273 });
7274 }
7275 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("generate_series"))
7285 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
7286 {
7287 self.advance(); self.advance(); let mut args: Vec<Expr> = Vec::new();
7290 loop {
7291 args.push(self.parse_expr(0)?);
7292 if matches!(self.peek(), Token::Comma) {
7293 self.advance();
7294 continue;
7295 }
7296 break;
7297 }
7298 if !matches!(self.peek(), Token::RParen) {
7299 return Err(self.err(alloc::format!(
7300 "expected ')' after generate_series() arguments, got {:?}",
7301 self.peek()
7302 )));
7303 }
7304 self.advance();
7305 if args.len() < 2 || args.len() > 3 {
7306 return Err(self.err(alloc::format!(
7307 "generate_series() expects 2 or 3 arguments (start, stop [, step]); got {}",
7308 args.len()
7309 )));
7310 }
7311 let (alias_ident, _column_aliases) = self.parse_optional_alias_with_columns();
7312 let name = alias_ident
7313 .clone()
7314 .unwrap_or_else(|| "generate_series".to_string());
7315 return Ok(TableRef {
7316 name,
7317 alias: alias_ident,
7318 as_of_segment: None,
7319 unnest_expr: None,
7320 unnest_column_aliases: Vec::new(),
7321 generate_series_args: Some(args),
7322 lateral_subquery: None,
7323 });
7324 }
7325 let name = if let Some(synth) = self.try_peek_meta_qualified() {
7334 synth
7335 } else if let Some(synth) = self.try_peek_meta_bare() {
7336 synth
7337 } else {
7338 self.expect_ident_like()?
7339 };
7340 let as_of_segment = if matches!(self.peek(), Token::As)
7346 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("of"))
7347 {
7348 self.advance(); self.advance(); let kw = match self.peek().clone() {
7351 Token::Ident(s) | Token::QuotedIdent(s) => s,
7352 other => {
7353 return Err(self.err(format!("expected SEGMENT after AS OF, got {other:?}")));
7354 }
7355 };
7356 if !kw.eq_ignore_ascii_case("segment") {
7357 return Err(self.err(format!(
7358 "expected SEGMENT after AS OF, got {kw:?}; v6.10.2 supports SEGMENT only"
7359 )));
7360 }
7361 self.advance();
7362 let id = match self.advance() {
7365 Token::String(s) => s
7366 .parse::<u32>()
7367 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
7368 Token::Integer(n) => u32::try_from(n)
7369 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
7370 other => {
7371 return Err(self.err(format!(
7372 "expected segment id literal after AS OF SEGMENT, got {other:?}"
7373 )));
7374 }
7375 };
7376 Some(id)
7377 } else {
7378 None
7379 };
7380 let alias = self.parse_optional_alias();
7381 Ok(TableRef {
7382 name,
7383 alias,
7384 as_of_segment,
7385 unnest_expr: None,
7386 unnest_column_aliases: Vec::new(),
7387 generate_series_args: None,
7388 lateral_subquery: None,
7389 })
7390 }
7391
7392 fn parse_optional_alias_with_columns(&mut self) -> (Option<String>, Vec<String>) {
7398 let alias = self.parse_optional_alias();
7399 if alias.is_none() {
7400 return (None, Vec::new());
7401 }
7402 let mut cols: Vec<String> = Vec::new();
7403 if matches!(self.peek(), Token::LParen) {
7404 self.advance();
7405 while let Token::Ident(s) | Token::QuotedIdent(s) = self.peek().clone() {
7406 self.advance();
7407 cols.push(s);
7408 if matches!(self.peek(), Token::Comma) {
7409 self.advance();
7410 continue;
7411 }
7412 break;
7413 }
7414 if matches!(self.peek(), Token::RParen) {
7415 self.advance();
7416 }
7417 }
7418 (alias, cols)
7419 }
7420
7421 fn parse_from_clause(&mut self) -> Result<FromClause, ParseError> {
7426 let primary = self.parse_table_ref()?;
7427 let mut joins = Vec::new();
7428 loop {
7429 if matches!(self.peek(), Token::Comma) {
7431 self.advance();
7432 let table = self.parse_table_ref()?;
7433 joins.push(FromJoin {
7434 kind: JoinKind::Cross,
7435 table,
7436 on: None,
7437 });
7438 continue;
7439 }
7440 let kind =
7443 match self.peek() {
7444 Token::Inner => {
7445 self.advance();
7446 if !matches!(self.peek(), Token::Join) {
7447 return Err(self
7448 .err(format!("expected JOIN after INNER, got {:?}", self.peek())));
7449 }
7450 self.advance();
7451 JoinKind::Inner
7452 }
7453 Token::Left => {
7454 self.advance();
7455 if matches!(self.peek(), Token::Outer) {
7456 self.advance();
7457 }
7458 if !matches!(self.peek(), Token::Join) {
7459 return Err(self.err(format!(
7460 "expected JOIN after LEFT [OUTER], got {:?}",
7461 self.peek()
7462 )));
7463 }
7464 self.advance();
7465 JoinKind::Left
7466 }
7467 Token::Cross => {
7468 self.advance();
7469 if !matches!(self.peek(), Token::Join) {
7470 return Err(self
7471 .err(format!("expected JOIN after CROSS, got {:?}", self.peek())));
7472 }
7473 self.advance();
7474 JoinKind::Cross
7475 }
7476 Token::Join => {
7477 self.advance();
7478 JoinKind::Inner
7479 }
7480 _ => break,
7481 };
7482 let table = self.parse_table_ref()?;
7483 let on = if matches!(self.peek(), Token::On) {
7484 self.advance();
7485 Some(self.parse_expr(0)?)
7486 } else if kind == JoinKind::Cross {
7487 None
7488 } else {
7489 return Err(self.err(format!(
7490 "expected ON after {:?} JOIN, got {:?}",
7491 kind,
7492 self.peek()
7493 )));
7494 };
7495 joins.push(FromJoin { kind, table, on });
7496 }
7497 Ok(FromClause { primary, joins })
7498 }
7499
7500 fn parse_optional_alias(&mut self) -> Option<String> {
7505 if matches!(self.peek(), Token::As) {
7506 self.advance();
7507 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
7512 return self.expect_ident_like().ok();
7513 }
7514 return None;
7515 }
7516 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
7524 if is_alias_stopword(s) {
7525 return None;
7526 }
7527 return self.expect_ident_like().ok();
7528 }
7529 None
7530 }
7531
7532 fn parse_expr(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
7534 self.enter_nested()?;
7538 let r = self.parse_expr_inner(min_prec);
7539 self.nest_depth -= 1;
7540 r
7541 }
7542
7543 fn parse_expr_inner(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
7544 let mut lhs = self.parse_unary()?;
7545 let mut chain_len = 0usize;
7546 while let Some((op, prec)) = binop_from(self.peek()) {
7547 if prec < min_prec {
7548 break;
7549 }
7550 chain_len += 1;
7554 if chain_len > MAX_BINARY_CHAIN {
7555 return Err(self.err(alloc::format!(
7556 "more than {MAX_BINARY_CHAIN} chained binary operators; rewrite long OR-equality chains as IN (…)"
7557 )));
7558 }
7559 self.advance();
7560 let any_kind = match self.peek() {
7565 Token::All if matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) => {
7566 Some(false)
7567 }
7568 Token::Ident(s) | Token::QuotedIdent(s)
7569 if (s.eq_ignore_ascii_case("any") || s.eq_ignore_ascii_case("all"))
7570 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) =>
7571 {
7572 Some(s.eq_ignore_ascii_case("any"))
7573 }
7574 _ => None,
7575 };
7576 if let Some(is_any) = any_kind {
7577 self.advance(); self.advance(); let arr = self.parse_expr(0)?;
7580 if !matches!(self.peek(), Token::RParen) {
7581 return Err(self.err(alloc::format!(
7582 "expected ')' after ANY/ALL argument, got {:?}",
7583 self.peek()
7584 )));
7585 }
7586 self.advance();
7587 lhs = Expr::AnyAll {
7588 expr: Box::new(lhs),
7589 op,
7590 array: Box::new(arr),
7591 is_any,
7592 };
7593 continue;
7594 }
7595 let rhs = self.parse_expr(prec + 1)?;
7596 lhs = Expr::Binary {
7597 lhs: Box::new(lhs),
7598 op,
7599 rhs: Box::new(rhs),
7600 };
7601 }
7602 Ok(lhs)
7603 }
7604
7605 fn parse_unary(&mut self) -> Result<Expr, ParseError> {
7606 match self.peek() {
7607 Token::Not => {
7608 self.advance();
7609 let e = self.parse_expr(3)?;
7612 Ok(Expr::Unary {
7613 op: UnOp::Not,
7614 expr: Box::new(e),
7615 })
7616 }
7617 Token::Minus => {
7618 self.advance();
7619 let e = self.parse_expr(8)?;
7622 Ok(Expr::Unary {
7623 op: UnOp::Neg,
7624 expr: Box::new(e),
7625 })
7626 }
7627 Token::Tilde => {
7628 self.advance();
7629 let e = self.parse_expr(8)?;
7631 Ok(Expr::Unary {
7632 op: UnOp::BitNot,
7633 expr: Box::new(e),
7634 })
7635 }
7636 _ => self.parse_atom(),
7637 }
7638 }
7639
7640 fn parse_atom(&mut self) -> Result<Expr, ParseError> {
7641 let tok_pos = self.pos;
7642 match self.advance() {
7643 Token::Integer(n) => Ok(Expr::Literal(Literal::Integer(n))),
7644 Token::Float(x) => Ok(Expr::Literal(Literal::Float(x))),
7645 Token::String(s) => Ok(Expr::Literal(Literal::String(s))),
7646 Token::True => Ok(Expr::Literal(Literal::Bool(true))),
7647 Token::False => Ok(Expr::Literal(Literal::Bool(false))),
7648 Token::Null => Ok(Expr::Literal(Literal::Null)),
7649 Token::Placeholder(n) => Ok(Expr::Placeholder(n)),
7653 Token::LParen => {
7654 if matches!(self.peek(), Token::Select) {
7658 let inner = self.parse_select_stmt()?;
7659 match self.advance() {
7660 Token::RParen => {
7661 let Statement::Select(s) = inner else {
7662 unreachable!("parse_select_stmt returns Select")
7663 };
7664 Ok(Expr::ScalarSubquery(Box::new(s)))
7665 }
7666 other => Err(ParseError {
7667 message: format!("expected ')' after scalar subquery, got {other:?}"),
7668 token_pos: self.pos.saturating_sub(1),
7669 }),
7670 }
7671 } else {
7672 let e = self.parse_expr(0)?;
7673 match self.advance() {
7674 Token::RParen => Ok(e),
7675 other => Err(ParseError {
7676 message: format!("expected ')', got {other:?}"),
7677 token_pos: self.pos.saturating_sub(1),
7678 }),
7679 }
7680 }
7681 }
7682 Token::LBracket => self.parse_vector_literal_body(),
7683 Token::Extract => self.parse_extract_atom(),
7684 Token::Interval => self.parse_interval_atom(),
7685 Token::Left if matches!(self.peek(), Token::LParen) => {
7692 self.advance(); let mut args = Vec::new();
7694 if !matches!(self.peek(), Token::RParen) {
7695 loop {
7696 args.push(self.parse_expr(0)?);
7697 match self.peek() {
7698 Token::Comma => {
7699 self.advance();
7700 }
7701 Token::RParen => break,
7702 other => {
7703 return Err(self.err(alloc::format!(
7704 "expected ',' or ')' in left() args, got {other:?}"
7705 )));
7706 }
7707 }
7708 }
7709 }
7710 self.advance(); Ok(Expr::FunctionCall {
7712 name: "left".into(),
7713 args,
7714 })
7715 }
7716 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists") => {
7721 self.parse_exists_atom(false)
7722 }
7723 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("case") => {
7727 self.parse_case_atom()
7728 }
7729 Token::Ident(s) | Token::QuotedIdent(s)
7733 if s.eq_ignore_ascii_case("array") && matches!(self.peek(), Token::LBracket) =>
7734 {
7735 self.advance(); let mut items: Vec<Expr> = Vec::new();
7737 if !matches!(self.peek(), Token::RBracket) {
7738 loop {
7739 items.push(self.parse_expr(0)?);
7740 match self.peek() {
7741 Token::Comma => {
7742 self.advance();
7743 }
7744 Token::RBracket => break,
7745 other => {
7746 return Err(self.err(alloc::format!(
7747 "expected ',' or ']' in ARRAY literal, got {other:?}"
7748 )));
7749 }
7750 }
7751 }
7752 }
7753 self.advance(); Ok(Expr::Array(items))
7755 }
7756 Token::Ident(s) | Token::QuotedIdent(s)
7771 if s.eq_ignore_ascii_case("match") && matches!(self.peek(), Token::LParen) =>
7772 {
7773 self.parse_match_against_atom()
7774 }
7775 Token::Ident(s) | Token::QuotedIdent(s) => self.finish_ident_atom(s),
7776 other => Err(ParseError {
7777 message: format!("unexpected token {other:?} in expression"),
7778 token_pos: tok_pos,
7779 }),
7780 }
7781 .and_then(|atom| self.finish_postfix_casts(atom))
7783 }
7784
7785 fn parse_cast_target(&mut self) -> Result<CastTarget, ParseError> {
7790 let target = match self.advance() {
7791 Token::Ident(s) => match s.to_ascii_lowercase().as_str() {
7792 "int" | "integer" | "int4" => {
7793 if matches!(self.peek(), Token::LBracket)
7794 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
7795 {
7796 self.advance();
7797 self.advance();
7798 CastTarget::IntArray
7799 } else {
7800 CastTarget::Int
7801 }
7802 }
7803 "bigint" | "int8" => {
7804 if matches!(self.peek(), Token::LBracket)
7805 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
7806 {
7807 self.advance();
7808 self.advance();
7809 CastTarget::BigIntArray
7810 } else {
7811 CastTarget::BigInt
7812 }
7813 }
7814 "float" | "double" | "real" => CastTarget::Float,
7815 "text" => {
7816 if matches!(self.peek(), Token::LBracket)
7818 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
7819 {
7820 self.advance();
7821 self.advance();
7822 CastTarget::TextArray
7823 } else {
7824 CastTarget::Text
7825 }
7826 }
7827 "bool" | "boolean" => CastTarget::Bool,
7828 "vector" => CastTarget::Vector,
7829 "date" => CastTarget::Date,
7830 "timestamp" | "datetime" => CastTarget::Timestamp,
7831 "timestamptz" => CastTarget::Timestamptz,
7832 "interval" => CastTarget::Interval,
7833 "json" => CastTarget::Json,
7834 "jsonb" => CastTarget::Jsonb,
7835 "regtype" => CastTarget::RegType,
7836 "regclass" => CastTarget::RegClass,
7837 "tsvector" => CastTarget::TsVector,
7841 "tsquery" => CastTarget::TsQuery,
7842 "uuid" => CastTarget::Uuid,
7845 "bytea" => CastTarget::Bytea,
7850 "inet" | "cidr" | "macaddr" => CastTarget::Text,
7855 other => {
7856 return Err(ParseError {
7857 message: format!("unsupported cast target `::{other}`"),
7858 token_pos: self.pos.saturating_sub(1),
7859 });
7860 }
7861 },
7862 Token::Interval => CastTarget::Interval,
7863 other => {
7864 return Err(ParseError {
7865 message: format!("expected type ident after `::`, got {other:?}"),
7866 token_pos: self.pos.saturating_sub(1),
7867 });
7868 }
7869 };
7870 Ok(target)
7871 }
7872
7873 fn finish_postfix_casts(&mut self, mut expr: Expr) -> Result<Expr, ParseError> {
7874 loop {
7875 if matches!(self.peek(), Token::DoubleColon) {
7876 self.advance();
7877 let target = self.parse_cast_target()?;
7882 expr = Expr::Cast {
7883 expr: Box::new(expr),
7884 target,
7885 };
7886 continue;
7887 }
7888 if matches!(self.peek(), Token::Is) {
7889 self.advance();
7890 let negated = if matches!(self.peek(), Token::Not) {
7891 self.advance();
7892 true
7893 } else {
7894 false
7895 };
7896 if matches!(self.peek(), Token::Distinct) {
7899 self.advance();
7900 if !matches!(self.peek(), Token::From) {
7901 return Err(self.err(format!(
7902 "expected FROM after IS{} DISTINCT, got {:?}",
7903 if negated { " NOT" } else { "" },
7904 self.peek()
7905 )));
7906 }
7907 self.advance();
7908 let rhs = self.parse_expr(20)?;
7912 let op = if negated {
7913 BinOp::IsNotDistinctFrom
7914 } else {
7915 BinOp::IsDistinctFrom
7916 };
7917 expr = Expr::Binary {
7918 op,
7919 lhs: Box::new(expr),
7920 rhs: Box::new(rhs),
7921 };
7922 continue;
7923 }
7924 if !matches!(self.peek(), Token::Null) {
7925 return Err(self.err(format!(
7926 "expected NULL or DISTINCT after IS{}, got {:?}",
7927 if negated { " NOT" } else { "" },
7928 self.peek()
7929 )));
7930 }
7931 self.advance();
7932 expr = Expr::IsNull {
7933 expr: Box::new(expr),
7934 negated,
7935 };
7936 continue;
7937 }
7938 let negated = if matches!(self.peek(), Token::Not) {
7942 let next = self.tokens.get(self.pos + 1);
7943 matches!(next, Some(Token::Between | Token::In | Token::Like))
7944 || matches!(next, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("ilike"))
7945 } else {
7946 false
7947 };
7948 if negated {
7949 self.advance();
7950 }
7951 if matches!(self.peek(), Token::Between) {
7952 expr = self.parse_between_tail(expr, negated)?;
7953 continue;
7954 }
7955 if matches!(self.peek(), Token::In) {
7956 expr = self.parse_in_tail(expr, negated)?;
7957 continue;
7958 }
7959 if matches!(self.peek(), Token::Like) {
7960 self.advance();
7961 let pattern = self.parse_expr(5)?;
7964 expr = Expr::Like {
7965 expr: Box::new(expr),
7966 pattern: Box::new(pattern),
7967 negated,
7968 case_insensitive: false,
7969 };
7970 continue;
7971 }
7972 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("ilike")) {
7975 self.advance();
7976 let pattern = self.parse_expr(5)?;
7977 expr = Expr::Like {
7978 expr: Box::new(expr),
7979 pattern: Box::new(pattern),
7980 negated,
7981 case_insensitive: true,
7982 };
7983 continue;
7984 }
7985 if matches!(self.peek(), Token::LBracket) {
7989 self.advance();
7990 let index = self.parse_expr(0)?;
7991 if !matches!(self.peek(), Token::RBracket) {
7992 return Err(self.err(alloc::format!(
7993 "expected ']' after array index, got {:?}",
7994 self.peek()
7995 )));
7996 }
7997 self.advance();
7998 expr = Expr::ArraySubscript {
7999 target: Box::new(expr),
8000 index: Box::new(index),
8001 };
8002 continue;
8003 }
8004 return Ok(expr);
8005 }
8006 }
8007
8008 fn parse_between_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
8012 self.advance(); let low = self.parse_expr(5)?;
8014 if !matches!(self.peek(), Token::And) {
8015 return Err(self.err(format!(
8016 "expected AND after BETWEEN low bound, got {:?}",
8017 self.peek()
8018 )));
8019 }
8020 self.advance();
8021 let high = self.parse_expr(5)?;
8022 let target = Box::new(expr);
8023 let combined = Expr::Binary {
8024 lhs: Box::new(Expr::Binary {
8025 lhs: target.clone(),
8026 op: BinOp::GtEq,
8027 rhs: Box::new(low),
8028 }),
8029 op: BinOp::And,
8030 rhs: Box::new(Expr::Binary {
8031 lhs: target,
8032 op: BinOp::LtEq,
8033 rhs: Box::new(high),
8034 }),
8035 };
8036 Ok(maybe_not(combined, negated))
8037 }
8038
8039 fn parse_with_cte_then_select(&mut self) -> Result<Statement, ParseError> {
8044 let mut recursive = false;
8049 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
8050 && s.eq_ignore_ascii_case("recursive")
8051 {
8052 self.advance();
8053 recursive = true;
8054 }
8055 let mut ctes = Vec::new();
8056 loop {
8057 let name = self.expect_ident_like()?;
8058 let column_overrides: Vec<String> = if matches!(self.peek(), Token::LParen) {
8062 self.advance();
8063 let mut names = Vec::new();
8064 loop {
8065 names.push(self.expect_ident_like()?);
8066 if matches!(self.peek(), Token::Comma) {
8067 self.advance();
8068 continue;
8069 }
8070 break;
8071 }
8072 if !matches!(self.peek(), Token::RParen) {
8073 return Err(self.err(format!(
8074 "expected ')' to close CTE column list, got {:?}",
8075 self.peek()
8076 )));
8077 }
8078 self.advance();
8079 names
8080 } else {
8081 Vec::new()
8082 };
8083 if !matches!(self.peek(), Token::As) {
8087 return Err(self.err(format!(
8088 "expected AS after CTE name {name:?}, got {:?}",
8089 self.peek()
8090 )));
8091 }
8092 self.advance();
8093 if !matches!(self.peek(), Token::LParen) {
8094 return Err(self.err(format!(
8095 "expected '(' after AS in WITH clause, got {:?}",
8096 self.peek()
8097 )));
8098 }
8099 self.advance();
8100 if !matches!(self.peek(), Token::Select) {
8101 return Err(self.err(format!("WITH body must be a SELECT, got {:?}", self.peek())));
8102 }
8103 let inner = self.parse_select_stmt()?;
8104 if !matches!(self.peek(), Token::RParen) {
8105 return Err(self.err(format!(
8106 "expected ')' after CTE body, got {:?}",
8107 self.peek()
8108 )));
8109 }
8110 self.advance();
8111 let Statement::Select(body) = inner else {
8112 unreachable!("parse_select_stmt returns Select")
8113 };
8114 ctes.push(crate::ast::Cte {
8115 name,
8116 body,
8117 recursive,
8118 column_overrides,
8119 });
8120 if matches!(self.peek(), Token::Comma) {
8121 self.advance();
8122 continue;
8123 }
8124 break;
8125 }
8126 if !matches!(self.peek(), Token::Select) {
8128 return Err(self.err(format!(
8129 "expected SELECT after WITH clause, got {:?}",
8130 self.peek()
8131 )));
8132 }
8133 let body_stmt = self.parse_select_stmt()?;
8134 let Statement::Select(mut body) = body_stmt else {
8135 unreachable!()
8136 };
8137 body.ctes = ctes;
8138 Ok(Statement::Select(body))
8139 }
8140
8141 fn parse_case_atom(&mut self) -> Result<Expr, ParseError> {
8150 let operand = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("when")) {
8154 None
8155 } else {
8156 Some(Box::new(self.parse_expr(0)?))
8157 };
8158 let mut branches: Vec<(Expr, Expr)> = Vec::new();
8159 loop {
8160 match self.peek() {
8161 Token::Ident(s) if s.eq_ignore_ascii_case("when") => {
8162 self.advance();
8163 let cond = self.parse_expr(0)?;
8164 match self.peek() {
8165 Token::Ident(t) if t.eq_ignore_ascii_case("then") => {
8166 self.advance();
8167 }
8168 other => {
8169 return Err(self.err(alloc::format!(
8170 "expected THEN after CASE WHEN <expr>, got {other:?}"
8171 )));
8172 }
8173 }
8174 let value = self.parse_expr(0)?;
8175 branches.push((cond, value));
8176 }
8177 _ => break,
8178 }
8179 }
8180 if branches.is_empty() {
8181 return Err(self.err("CASE requires at least one WHEN … THEN … branch".into()));
8182 }
8183 let else_branch = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("else"))
8184 {
8185 self.advance();
8186 Some(Box::new(self.parse_expr(0)?))
8187 } else {
8188 None
8189 };
8190 match self.peek() {
8191 Token::Ident(s) if s.eq_ignore_ascii_case("end") => {
8192 self.advance();
8193 }
8194 other => {
8195 return Err(self.err(alloc::format!(
8196 "expected END to close CASE expression, got {other:?}"
8197 )));
8198 }
8199 }
8200 Ok(Expr::Case {
8201 operand,
8202 branches,
8203 else_branch,
8204 })
8205 }
8206
8207 fn parse_exists_atom(&mut self, negated: bool) -> Result<Expr, ParseError> {
8208 if !matches!(self.peek(), Token::LParen) {
8209 return Err(self.err(format!("expected '(' after EXISTS, got {:?}", self.peek())));
8210 }
8211 self.advance();
8212 let inner = self.parse_select_stmt()?;
8213 if !matches!(self.peek(), Token::RParen) {
8214 return Err(self.err(format!(
8215 "expected ')' after EXISTS-subquery, got {:?}",
8216 self.peek()
8217 )));
8218 }
8219 self.advance();
8220 let Statement::Select(s) = inner else {
8221 unreachable!("parse_select_stmt returns Select")
8222 };
8223 Ok(Expr::Exists {
8224 subquery: Box::new(s),
8225 negated,
8226 })
8227 }
8228
8229 fn parse_in_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
8230 self.advance(); if !matches!(self.peek(), Token::LParen) {
8232 return Err(self.err(format!("expected '(' after IN, got {:?}", self.peek())));
8233 }
8234 self.advance();
8235 if matches!(self.peek(), Token::Select) {
8237 let inner = self.parse_select_stmt()?;
8238 if !matches!(self.peek(), Token::RParen) {
8239 return Err(self.err(format!(
8240 "expected ')' after IN-subquery, got {:?}",
8241 self.peek()
8242 )));
8243 }
8244 self.advance();
8245 let Statement::Select(s) = inner else {
8246 unreachable!("parse_select_stmt always returns Statement::Select")
8247 };
8248 return Ok(Expr::InSubquery {
8249 expr: Box::new(expr),
8250 subquery: Box::new(s),
8251 negated,
8252 });
8253 }
8254 let mut elements = Vec::new();
8255 if !matches!(self.peek(), Token::RParen) {
8256 loop {
8257 elements.push(self.parse_expr(0)?);
8258 match self.peek() {
8259 Token::Comma => {
8260 self.advance();
8261 }
8262 Token::RParen => break,
8263 other => {
8264 return Err(
8265 self.err(format!("expected ',' or ')' in IN list, got {other:?}"))
8266 );
8267 }
8268 }
8269 }
8270 }
8271 self.advance(); if elements.is_empty() {
8276 return Ok(maybe_not(Expr::Literal(Literal::Bool(false)), negated));
8277 }
8278 Ok(Expr::InList {
8279 expr: Box::new(expr),
8280 list: elements,
8281 negated,
8282 })
8283 }
8284
8285 fn parse_match_against_atom(&mut self) -> Result<Expr, ParseError> {
8306 if !matches!(self.peek(), Token::LParen) {
8309 return Err(self.err(alloc::format!(
8310 "expected '(' after MATCH, got {:?}",
8311 self.peek()
8312 )));
8313 }
8314 self.advance();
8315 let mut cols: Vec<Expr> = Vec::new();
8316 loop {
8317 cols.push(self.parse_expr(0)?);
8318 match self.peek() {
8319 Token::Comma => {
8320 self.advance();
8321 }
8322 Token::RParen => break,
8323 other => {
8324 return Err(self.err(alloc::format!(
8325 "expected ',' or ')' in MATCH column list, got {other:?}"
8326 )));
8327 }
8328 }
8329 }
8330 self.advance(); match self.peek() {
8333 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("against") => {
8334 self.advance();
8335 }
8336 other => {
8337 return Err(self.err(alloc::format!(
8338 "expected AGAINST after MATCH column list, got {other:?}"
8339 )));
8340 }
8341 }
8342 if !matches!(self.peek(), Token::LParen) {
8343 return Err(self.err(alloc::format!(
8344 "expected '(' after AGAINST, got {:?}",
8345 self.peek()
8346 )));
8347 }
8348 self.advance();
8349 let term = match self.advance() {
8359 Token::String(s) => Expr::Literal(crate::ast::Literal::String(s)),
8360 Token::Placeholder(n) => Expr::Placeholder(n),
8361 Token::Ident(s) | Token::QuotedIdent(s) => Expr::Column(crate::ast::ColumnName {
8362 qualifier: None,
8363 name: s,
8364 }),
8365 other => {
8366 return Err(self.err(alloc::format!(
8367 "MATCH ... AGAINST(<term>) expects a string literal, \
8368 bound parameter, or column ref, got {other:?}"
8369 )));
8370 }
8371 };
8372 loop {
8377 match self.peek() {
8378 Token::In => {
8381 self.advance();
8382 }
8383 Token::Ident(s) | Token::QuotedIdent(s)
8384 if s.eq_ignore_ascii_case("natural")
8385 || s.eq_ignore_ascii_case("language")
8386 || s.eq_ignore_ascii_case("boolean")
8387 || s.eq_ignore_ascii_case("mode")
8388 || s.eq_ignore_ascii_case("with")
8389 || s.eq_ignore_ascii_case("query")
8390 || s.eq_ignore_ascii_case("expansion") =>
8391 {
8392 self.advance();
8393 }
8394 _ => break,
8395 }
8396 }
8397 if !matches!(self.peek(), Token::RParen) {
8398 return Err(self.err(alloc::format!(
8399 "expected ')' to close AGAINST, got {:?}",
8400 self.peek()
8401 )));
8402 }
8403 self.advance();
8404 let simple_lit = || Expr::Literal(crate::ast::Literal::String(String::from("simple")));
8407 let plainto = Expr::FunctionCall {
8408 name: String::from("plainto_tsquery"),
8409 args: alloc::vec![simple_lit(), term.clone()],
8410 };
8411 let mut folded: Option<Expr> = None;
8412 for col in cols {
8413 let to_tsv = Expr::FunctionCall {
8414 name: String::from("to_tsvector"),
8415 args: alloc::vec![simple_lit(), col],
8416 };
8417 let leaf = Expr::Binary {
8418 lhs: Box::new(to_tsv),
8419 op: crate::ast::BinOp::TsMatch,
8420 rhs: Box::new(plainto.clone()),
8421 };
8422 folded = Some(match folded {
8423 None => leaf,
8424 Some(prev) => Expr::Binary {
8425 lhs: Box::new(prev),
8426 op: crate::ast::BinOp::Or,
8427 rhs: Box::new(leaf),
8428 },
8429 });
8430 }
8431 match folded {
8432 Some(e) => Ok(e),
8433 None => Err(self.err(String::from(
8434 "MATCH(...) AGAINST(...) requires at least one column",
8435 ))),
8436 }
8437 }
8438
8439 fn parse_extract_atom(&mut self) -> Result<Expr, ParseError> {
8440 if !matches!(self.peek(), Token::LParen) {
8441 return Err(self.err(format!("expected '(' after EXTRACT, got {:?}", self.peek())));
8442 }
8443 self.advance();
8444 let field_name = self.expect_ident_like()?;
8445 let field = match field_name.to_ascii_lowercase().as_str() {
8446 "year" => ExtractField::Year,
8447 "month" => ExtractField::Month,
8448 "day" => ExtractField::Day,
8449 "hour" => ExtractField::Hour,
8450 "minute" => ExtractField::Minute,
8451 "second" => ExtractField::Second,
8452 "microsecond" | "microseconds" => ExtractField::Microsecond,
8453 "epoch" => ExtractField::Epoch,
8454 other => {
8455 return Err(self.err(format!(
8456 "unknown EXTRACT field {other:?}; \
8457 supported: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MICROSECOND, EPOCH"
8458 )));
8459 }
8460 };
8461 if !matches!(self.peek(), Token::From) {
8462 return Err(self.err(format!(
8463 "expected FROM after EXTRACT field, got {:?}",
8464 self.peek()
8465 )));
8466 }
8467 self.advance();
8468 let source = self.parse_expr(0)?;
8469 if !matches!(self.peek(), Token::RParen) {
8470 return Err(self.err(format!(
8471 "expected ')' to close EXTRACT, got {:?}",
8472 self.peek()
8473 )));
8474 }
8475 self.advance();
8476 Ok(Expr::Extract {
8477 field,
8478 source: Box::new(source),
8479 })
8480 }
8481
8482 fn parse_interval_atom(&mut self) -> Result<Expr, ParseError> {
8487 let tok = self.advance();
8488 let Token::String(text) = tok else {
8489 return Err(self.err(format!(
8490 "expected string literal after INTERVAL, got {tok:?}"
8491 )));
8492 };
8493 let (months, micros) = parse_interval_text(&text).ok_or_else(|| ParseError {
8494 message: format!(
8495 "cannot parse INTERVAL {text:?}; \
8496 expected `<n> <unit> [<n> <unit> ...]` with units \
8497 microsecond[s], millisecond[s], second[s], minute[s], \
8498 hour[s], day[s], week[s], month[s], year[s]"
8499 ),
8500 token_pos: self.pos.saturating_sub(1),
8501 })?;
8502 Ok(Expr::Literal(Literal::Interval {
8503 months,
8504 micros,
8505 text,
8506 }))
8507 }
8508
8509 fn parse_vector_literal_body(&mut self) -> Result<Expr, ParseError> {
8510 let mut elems = Vec::new();
8511 if matches!(self.peek(), Token::RBracket) {
8512 self.advance();
8513 return Ok(Expr::Literal(Literal::Vector(elems)));
8514 }
8515 loop {
8516 let e = self.parse_expr(0)?;
8517 let x = extract_numeric_literal(&e).ok_or_else(|| ParseError {
8518 message: format!("vector element must be a numeric literal, got {e:?}"),
8519 token_pos: self.pos,
8520 })?;
8521 elems.push(x);
8522 match self.peek() {
8523 Token::Comma => {
8524 self.advance();
8525 }
8526 Token::RBracket => {
8527 self.advance();
8528 break;
8529 }
8530 other => {
8531 return Err(self.err(format!("expected ',' or ']' in vector, got {other:?}")));
8532 }
8533 }
8534 }
8535 Ok(Expr::Literal(Literal::Vector(elems)))
8536 }
8537
8538 fn parse_null_treatment_modifier(&mut self) -> NullTreatment {
8547 let Token::Ident(s) = self.peek().clone() else {
8548 return NullTreatment::Respect;
8549 };
8550 let is_ignore = s.eq_ignore_ascii_case("ignore");
8551 let is_respect = s.eq_ignore_ascii_case("respect");
8552 if !is_ignore && !is_respect {
8553 return NullTreatment::Respect;
8554 }
8555 if self.pos + 1 < self.tokens.len()
8558 && let Token::Ident(s2) = &self.tokens[self.pos + 1]
8559 && s2.eq_ignore_ascii_case("nulls")
8560 {
8561 self.advance();
8562 self.advance();
8563 return if is_ignore {
8564 NullTreatment::Ignore
8565 } else {
8566 NullTreatment::Respect
8567 };
8568 }
8569 NullTreatment::Respect
8570 }
8571
8572 #[allow(clippy::type_complexity)] fn parse_over_clause(
8575 &mut self,
8576 ) -> Result<
8577 (
8578 Vec<Expr>,
8579 Vec<(Expr, bool, Option<bool>)>,
8580 Option<WindowFrame>,
8581 ),
8582 ParseError,
8583 > {
8584 if !matches!(self.peek(), Token::LParen) {
8585 return Err(self.err(format!("expected '(' after OVER, got {:?}", self.peek())));
8586 }
8587 self.advance();
8588 let mut partition_by = Vec::new();
8589 let mut order_by = Vec::new();
8590 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
8592 && s.eq_ignore_ascii_case("partition")
8593 {
8594 self.advance();
8595 if !matches!(self.peek(), Token::By) {
8596 return Err(self.err(format!(
8597 "expected BY after PARTITION, got {:?}",
8598 self.peek()
8599 )));
8600 }
8601 self.advance();
8602 loop {
8603 partition_by.push(self.parse_expr(0)?);
8604 if matches!(self.peek(), Token::Comma) {
8605 self.advance();
8606 continue;
8607 }
8608 break;
8609 }
8610 }
8611 if matches!(self.peek(), Token::Order) {
8613 self.advance();
8614 if !matches!(self.peek(), Token::By) {
8615 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
8616 }
8617 self.advance();
8618 loop {
8619 let e = self.parse_expr(0)?;
8620 let desc = if matches!(self.peek(), Token::Desc) {
8621 self.advance();
8622 true
8623 } else if matches!(self.peek(), Token::Asc) {
8624 self.advance();
8625 false
8626 } else {
8627 false
8628 };
8629 let nulls_first = self.parse_optional_nulls_placement()?;
8631 order_by.push((e, desc, nulls_first));
8632 if matches!(self.peek(), Token::Comma) {
8633 self.advance();
8634 continue;
8635 }
8636 break;
8637 }
8638 }
8639 let mut frame: Option<WindowFrame> = None;
8643 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
8644 let kind = if s.eq_ignore_ascii_case("rows") {
8645 Some(FrameKind::Rows)
8646 } else if s.eq_ignore_ascii_case("range") {
8647 Some(FrameKind::Range)
8648 } else {
8649 None
8650 };
8651 if let Some(kind) = kind {
8652 self.advance();
8653 frame = Some(self.parse_frame_tail(kind)?);
8654 }
8655 }
8656 if !matches!(self.peek(), Token::RParen) {
8657 return Err(self.err(format!(
8658 "expected ')' to close OVER clause, got {:?}",
8659 self.peek()
8660 )));
8661 }
8662 self.advance();
8663 Ok((partition_by, order_by, frame))
8664 }
8665
8666 fn parse_frame_tail(&mut self, kind: FrameKind) -> Result<WindowFrame, ParseError> {
8672 if matches!(self.peek(), Token::Between) {
8673 self.advance();
8674 let start = self.parse_frame_bound()?;
8675 if !matches!(self.peek(), Token::And) {
8676 return Err(self.err(format!("expected AND in frame spec, got {:?}", self.peek())));
8677 }
8678 self.advance();
8679 let end = self.parse_frame_bound()?;
8680 Ok(WindowFrame {
8681 kind,
8682 start,
8683 end: Some(end),
8684 })
8685 } else {
8686 let start = self.parse_frame_bound()?;
8687 Ok(WindowFrame {
8688 kind,
8689 start,
8690 end: None,
8691 })
8692 }
8693 }
8694
8695 fn parse_frame_bound(&mut self) -> Result<FrameBound, ParseError> {
8698 if let Token::Integer(n) = *self.peek() {
8700 self.advance();
8701 let n: u64 = u64::try_from(n).map_err(|_| {
8702 self.err(format!(
8703 "invalid frame offset {n} — expected non-negative integer"
8704 ))
8705 })?;
8706 let dir = self.expect_ident_like()?;
8707 return if dir.eq_ignore_ascii_case("preceding") {
8708 Ok(FrameBound::OffsetPreceding(n))
8709 } else if dir.eq_ignore_ascii_case("following") {
8710 Ok(FrameBound::OffsetFollowing(n))
8711 } else {
8712 Err(self.err(format!(
8713 "expected PRECEDING or FOLLOWING after offset, got {dir:?}"
8714 )))
8715 };
8716 }
8717 let first = self.expect_ident_like()?;
8718 if first.eq_ignore_ascii_case("unbounded") {
8719 let dir = self.expect_ident_like()?;
8720 return if dir.eq_ignore_ascii_case("preceding") {
8721 Ok(FrameBound::UnboundedPreceding)
8722 } else if dir.eq_ignore_ascii_case("following") {
8723 Ok(FrameBound::UnboundedFollowing)
8724 } else {
8725 Err(self.err(format!(
8726 "expected PRECEDING or FOLLOWING after UNBOUNDED, got {dir:?}"
8727 )))
8728 };
8729 }
8730 if first.eq_ignore_ascii_case("current") {
8731 let row = self.expect_ident_like()?;
8732 if !row.eq_ignore_ascii_case("row") {
8733 return Err(self.err(format!("expected ROW after CURRENT, got {row:?}")));
8734 }
8735 return Ok(FrameBound::CurrentRow);
8736 }
8737 Err(self.err(format!(
8738 "expected frame bound (UNBOUNDED/CURRENT/<n>), got {first:?}"
8739 )))
8740 }
8741
8742 fn finish_ident_atom(&mut self, first: String) -> Result<Expr, ParseError> {
8743 if matches!(self.peek(), Token::Dot) {
8744 self.advance();
8745 let name = self.expect_ident_like()?;
8746 if matches!(self.peek(), Token::LParen) {
8752 return self.finish_ident_atom(name);
8753 }
8754 return Ok(Expr::Column(ColumnName {
8755 qualifier: Some(first),
8756 name,
8757 }));
8758 }
8759 if matches!(self.peek(), Token::LParen) {
8760 self.advance();
8761 if first.eq_ignore_ascii_case("count") && matches!(self.peek(), Token::Star) {
8765 self.advance();
8766 if !matches!(self.peek(), Token::RParen) {
8767 return Err(self.err(format!(
8768 "expected ')' after COUNT(*), got {:?}",
8769 self.peek()
8770 )));
8771 }
8772 self.advance();
8773 let null_treatment = self.parse_null_treatment_modifier();
8775 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
8776 && s.eq_ignore_ascii_case("over")
8777 {
8778 self.advance();
8779 let (partition_by, order_by, frame) = self.parse_over_clause()?;
8780 return Ok(Expr::WindowFunction {
8781 name: "count_star".into(),
8782 args: Vec::new(),
8783 partition_by,
8784 order_by,
8785 frame,
8786 null_treatment,
8787 });
8788 }
8789 return Ok(Expr::FunctionCall {
8790 name: "count_star".into(),
8791 args: Vec::new(),
8792 });
8793 }
8794 let mut args = Vec::new();
8796 let mut agg_order_by: Vec<OrderBy> = Vec::new();
8797 let agg_distinct = if matches!(self.peek(), Token::Distinct) {
8799 self.advance();
8800 true
8801 } else {
8802 false
8803 };
8804 if !matches!(self.peek(), Token::RParen) {
8805 loop {
8806 args.push(self.parse_expr(0)?);
8807 if first.eq_ignore_ascii_case("cast")
8811 && args.len() == 1
8812 && matches!(self.peek(), Token::As)
8813 {
8814 self.advance();
8815 let target = self.parse_cast_target()?;
8816 if !matches!(self.peek(), Token::RParen) {
8817 return Err(self.err(format!(
8818 "expected ')' to close CAST, got {:?}",
8819 self.peek()
8820 )));
8821 }
8822 self.advance();
8823 return Ok(Expr::Cast {
8824 expr: Box::new(args.pop().expect("one arg")),
8825 target,
8826 });
8827 }
8828 if matches!(self.peek(), Token::Order) {
8832 self.advance();
8833 if !matches!(self.peek(), Token::By) {
8834 return Err(self.err(format!(
8835 "expected BY after ORDER in aggregate args, got {:?}",
8836 self.peek()
8837 )));
8838 }
8839 self.advance();
8840 loop {
8841 let expr = self.parse_expr(0)?;
8842 let desc = if matches!(self.peek(), Token::Desc) {
8843 self.advance();
8844 true
8845 } else if matches!(self.peek(), Token::Asc) {
8846 self.advance();
8847 false
8848 } else {
8849 false
8850 };
8851 let nulls_first = self.parse_optional_nulls_placement()?;
8852 agg_order_by.push(OrderBy {
8853 expr,
8854 desc,
8855 nulls_first,
8856 });
8857 if matches!(self.peek(), Token::Comma) {
8858 self.advance();
8859 } else {
8860 break;
8861 }
8862 }
8863 if !matches!(self.peek(), Token::RParen) {
8864 return Err(self.err(format!(
8865 "expected ')' after aggregate ORDER BY, got {:?}",
8866 self.peek()
8867 )));
8868 }
8869 break;
8870 }
8871 match self.peek() {
8872 Token::Comma => {
8873 self.advance();
8874 }
8875 Token::RParen => break,
8876 other => {
8877 return Err(self.err(format!(
8878 "expected ',' or ')' in function args, got {other:?}"
8879 )));
8880 }
8881 }
8882 }
8883 }
8884 self.advance(); let null_treatment = self.parse_null_treatment_modifier();
8892 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
8893 && s.eq_ignore_ascii_case("over")
8894 {
8895 self.advance();
8896 let (partition_by, order_by, frame) = self.parse_over_clause()?;
8897 return Ok(Expr::WindowFunction {
8898 name: first,
8899 args,
8900 partition_by,
8901 order_by,
8902 frame,
8903 null_treatment,
8904 });
8905 }
8906 if !agg_order_by.is_empty() || agg_distinct {
8907 return Ok(Expr::AggregateOrdered {
8908 call: Box::new(Expr::FunctionCall { name: first, args }),
8909 order_by: agg_order_by,
8910 distinct: agg_distinct,
8911 });
8912 }
8913 return Ok(Expr::FunctionCall { name: first, args });
8914 }
8915 let lc = first.to_ascii_lowercase();
8921 if matches!(
8922 lc.as_str(),
8923 "current_date" | "current_time" | "current_timestamp" | "localtimestamp" | "localtime"
8924 ) {
8925 return Ok(Expr::FunctionCall {
8926 name: lc,
8927 args: Vec::new(),
8928 });
8929 }
8930 Ok(Expr::Column(ColumnName {
8931 qualifier: None,
8932 name: first,
8933 }))
8934 }
8935}
8936
8937fn extract_first_column(expr: &Expr) -> Option<String> {
8945 match expr {
8946 Expr::Column(cn) => Some(cn.name.clone()),
8947 Expr::FunctionCall { args, .. } => args.iter().find_map(extract_first_column),
8948 Expr::Binary { lhs, rhs, .. } => {
8949 extract_first_column(lhs).or_else(|| extract_first_column(rhs))
8950 }
8951 Expr::Unary { expr: e, .. } => extract_first_column(e),
8952 _ => None,
8953 }
8954}
8955
8956fn maybe_not(expr: Expr, negated: bool) -> Expr {
8957 if negated {
8958 Expr::Unary {
8959 op: UnOp::Not,
8960 expr: Box::new(expr),
8961 }
8962 } else {
8963 expr
8964 }
8965}
8966
8967fn binop_from(tok: &Token) -> Option<(BinOp, u8)> {
8968 let pair = match tok {
8969 Token::Or => (BinOp::Or, 1),
8970 Token::And => (BinOp::And, 2),
8971 Token::Eq => (BinOp::Eq, 4),
8972 Token::NotEq => (BinOp::NotEq, 4),
8973 Token::Lt => (BinOp::Lt, 4),
8974 Token::LtEq => (BinOp::LtEq, 4),
8975 Token::Gt => (BinOp::Gt, 4),
8976 Token::GtEq => (BinOp::GtEq, 4),
8977 Token::L2Distance => (BinOp::L2Distance, 5),
8980 Token::InnerProduct => (BinOp::InnerProduct, 5),
8981 Token::CosineDistance => (BinOp::CosineDistance, 5),
8982 Token::Plus => (BinOp::Add, 6),
8983 Token::Minus => (BinOp::Sub, 6),
8984 Token::Concat => (BinOp::Concat, 6),
8987 Token::Pipe => (BinOp::BitOr, 6),
9000 Token::Amp => (BinOp::BitAnd, 6),
9001 Token::Star => (BinOp::Mul, 7),
9002 Token::Slash => (BinOp::Div, 7),
9003 Token::JsonGet => (BinOp::JsonGet, 7),
9007 Token::JsonGetText => (BinOp::JsonGetText, 7),
9008 Token::JsonGetPath => (BinOp::JsonGetPath, 7),
9009 Token::JsonGetPathText => (BinOp::JsonGetPathText, 7),
9010 Token::JsonContains => (BinOp::JsonContains, 7),
9011 Token::TsMatch => (BinOp::TsMatch, 4),
9015 Token::InetContainedBy => (BinOp::InetContainedBy, 4),
9019 Token::InetContainedByEq => (BinOp::InetContainedByEq, 4),
9020 Token::InetContains => (BinOp::InetContains, 4),
9021 Token::InetContainsEq => (BinOp::InetContainsEq, 4),
9022 Token::InetOverlap => (BinOp::InetOverlap, 4),
9023 _ => return None,
9024 };
9025 Some(pair)
9026}
9027
9028#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
9029fn is_alias_stopword(s: &str) -> bool {
9041 matches!(
9042 s.to_ascii_lowercase().as_str(),
9043 "with"
9044 | "on"
9045 | "where"
9046 | "having"
9047 | "group"
9048 | "order"
9049 | "limit"
9050 | "offset"
9051 | "union"
9052 | "except"
9053 | "intersect"
9054 | "returning"
9055 | "set"
9056 | "values"
9057 | "for"
9058 | "lateral"
9059 | "left"
9060 | "right"
9061 | "inner"
9062 | "outer"
9063 | "full"
9064 | "cross"
9065 | "join"
9066 | "natural"
9067 | "using"
9068 | "fetch"
9069 )
9070}
9071
9072fn extract_numeric_literal(e: &Expr) -> Option<f32> {
9073 match e {
9074 Expr::Literal(Literal::Integer(n)) => Some(*n as f32),
9075 Expr::Literal(Literal::Float(x)) => Some(*x as f32),
9076 Expr::Unary {
9077 op: UnOp::Neg,
9078 expr,
9079 } => extract_numeric_literal(expr).map(|x| -x),
9080 _ => None,
9081 }
9082}
9083
9084pub fn parse_interval_text(s: &str) -> Option<(i32, i64)> {
9092 let parts: Vec<&str> = s.split_whitespace().collect();
9093 if parts.is_empty() || !parts.len().is_multiple_of(2) {
9094 return None;
9095 }
9096 let mut months: i32 = 0;
9097 let mut micros: i64 = 0;
9098 let mut i = 0;
9099 while i < parts.len() {
9100 let n: i64 = parts[i].parse().ok()?;
9101 let unit = parts[i + 1].to_ascii_lowercase();
9102 let unit_stripped = unit.strip_suffix('s').unwrap_or(&unit);
9103 match unit_stripped {
9104 "microsecond" => micros = micros.checked_add(n)?,
9105 "millisecond" => micros = micros.checked_add(n.checked_mul(1_000)?)?,
9106 "second" => micros = micros.checked_add(n.checked_mul(1_000_000)?)?,
9107 "minute" => micros = micros.checked_add(n.checked_mul(60_000_000)?)?,
9108 "hour" => micros = micros.checked_add(n.checked_mul(3_600_000_000)?)?,
9109 "day" => micros = micros.checked_add(n.checked_mul(86_400_000_000)?)?,
9110 "week" => micros = micros.checked_add(n.checked_mul(604_800_000_000)?)?,
9111 "month" => {
9112 let n32 = i32::try_from(n).ok()?;
9113 months = months.checked_add(n32)?;
9114 }
9115 "year" => {
9116 let n32 = i32::try_from(n).ok()?;
9117 months = months.checked_add(n32.checked_mul(12)?)?;
9118 }
9119 _ => return None,
9120 }
9121 i += 2;
9122 }
9123 Some((months, micros))
9124}
9125
9126fn map_type_ident_to_column_type_name(ident: &str) -> Option<ColumnTypeName> {
9137 Some(match ident.to_ascii_lowercase().as_str() {
9138 "smallint" | "tinyint" => ColumnTypeName::SmallInt,
9139 "int" | "integer" | "mediumint" => ColumnTypeName::Int,
9140 "bigint" => ColumnTypeName::BigInt,
9141 "float" | "double" | "real" => ColumnTypeName::Float,
9142 "text" => ColumnTypeName::Text,
9143 "bool" | "boolean" => ColumnTypeName::Bool,
9144 "date" => ColumnTypeName::Date,
9145 "timestamp" | "datetime" => ColumnTypeName::Timestamp,
9146 "timestamptz" => ColumnTypeName::Timestamptz,
9147 "json" => ColumnTypeName::Json,
9148 "jsonb" => ColumnTypeName::Jsonb,
9149 "bytea" | "bytes" => ColumnTypeName::Bytes,
9150 "tsvector" => ColumnTypeName::TsVector,
9151 "tsquery" => ColumnTypeName::TsQuery,
9152 "uuid" => ColumnTypeName::Uuid,
9153 "time" => ColumnTypeName::Time,
9154 "year" => ColumnTypeName::Year,
9155 "timetz" => ColumnTypeName::TimeTz,
9156 "money" => ColumnTypeName::Money,
9157 _ => return None,
9158 })
9159}
9160
9161pub fn parse_function_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
9188 parse_plpgsql_body(body)
9189}
9190
9191fn parse_plpgsql_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
9192 let tokens = lexer::tokenize(body).map_err(|e| ParseError {
9196 message: alloc::format!("plpgsql body lex error: {e}"),
9197 token_pos: 0,
9198 })?;
9199 let mut parser = Parser::new(tokens);
9200 parser.parse_plpgsql_block()
9201}
9202
9203#[cfg(test)]
9204mod tests {
9205 use super::*;
9206 use alloc::string::ToString;
9207
9208 fn parse(s: &str) -> Statement {
9209 parse_statement(s).expect("parse ok")
9210 }
9211
9212 #[test]
9216 fn nesting_budget_errors_cleanly() {
9217 let depth = MAX_NEST_DEPTH + 50;
9218 let sql = format!("SELECT {}1{}", "(".repeat(depth), ")".repeat(depth));
9219 let err = parse_statement(&sql).expect_err("must reject");
9220 assert!(err.message.contains("nests deeper"), "{err:?}");
9221 let sql = format!("SELECT {}1{}", "(".repeat(48), ")".repeat(48));
9223 parse(&sql);
9224 }
9225
9226 #[test]
9227 fn binary_chain_budget_errors_cleanly() {
9228 let sql = format!("SELECT 1{}", " + 1".repeat(MAX_BINARY_CHAIN + 50));
9229 let err = parse_statement(&sql).expect_err("must reject");
9230 assert!(err.message.contains("chained binary"), "{err:?}");
9231 let sql = format!("SELECT 1{}", " + 1".repeat(200));
9234 parse(&sql);
9235 }
9236
9237 #[test]
9238 fn in_list_unaffected_by_chain_budget() {
9239 let items: alloc::vec::Vec<String> = (0..20_000).map(|k| k.to_string()).collect();
9241 let sql = format!("SELECT 1 WHERE 5 IN ({})", items.join(","));
9242 let Statement::Select(s) = parse(&sql) else {
9243 panic!("expected select")
9244 };
9245 let Some(Expr::InList { list, negated, .. }) = s.where_ else {
9246 panic!("expected flat InList, got {:?}", s.where_)
9247 };
9248 assert_eq!(list.len(), 20_000);
9249 assert!(!negated);
9250 }
9251
9252 fn lit_int(n: i64) -> Expr {
9253 Expr::Literal(Literal::Integer(n))
9254 }
9255
9256 fn col(name: &str) -> Expr {
9257 Expr::Column(ColumnName {
9258 qualifier: None,
9259 name: name.into(),
9260 })
9261 }
9262
9263 #[test]
9264 fn select_single_integer() {
9265 let s = parse("SELECT 1");
9266 let Statement::Select(s) = s else {
9267 panic!("expected SELECT")
9268 };
9269 assert_eq!(s.items.len(), 1);
9270 assert!(s.from.is_none());
9271 assert!(s.where_.is_none());
9272 }
9273
9274 #[test]
9275 fn select_multiple_literal_kinds() {
9276 let s = parse("SELECT 1, 'hi', NULL, TRUE, 1.5");
9277 let Statement::Select(s) = s else {
9278 panic!("expected SELECT")
9279 };
9280 assert_eq!(s.items.len(), 5);
9281 }
9282
9283 #[test]
9284 fn select_wildcard_from_table() {
9285 let s = parse("SELECT * FROM users");
9286 let Statement::Select(s) = s else {
9287 panic!("expected SELECT")
9288 };
9289 assert!(matches!(s.items[..], [SelectItem::Wildcard]));
9290 assert_eq!(s.from.as_ref().unwrap().primary.name, "users");
9291 }
9292
9293 #[test]
9294 fn select_with_table_alias() {
9295 let s = parse("SELECT * FROM users AS u");
9296 let Statement::Select(s) = s else {
9297 panic!("expected SELECT")
9298 };
9299 let t = &s.from.as_ref().unwrap().primary;
9300 assert_eq!(t.name, "users");
9301 assert_eq!(t.alias.as_deref(), Some("u"));
9302 }
9303
9304 #[test]
9305 fn select_with_where_eq() {
9306 let s = parse("SELECT a FROM t WHERE a = 1");
9307 let Statement::Select(s) = s else {
9308 panic!("expected SELECT")
9309 };
9310 let w = s.where_.unwrap();
9311 assert_eq!(
9312 w,
9313 Expr::Binary {
9314 lhs: Box::new(col("a")),
9315 op: BinOp::Eq,
9316 rhs: Box::new(lit_int(1)),
9317 }
9318 );
9319 }
9320
9321 #[test]
9322 fn arithmetic_precedence() {
9323 let s = parse("SELECT 1 + 2 * 3");
9324 let Statement::Select(s) = s else {
9325 panic!("expected SELECT")
9326 };
9327 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9328 panic!("wildcard?")
9329 };
9330 assert_eq!(
9331 expr,
9332 &Expr::Binary {
9333 lhs: Box::new(lit_int(1)),
9334 op: BinOp::Add,
9335 rhs: Box::new(Expr::Binary {
9336 lhs: Box::new(lit_int(2)),
9337 op: BinOp::Mul,
9338 rhs: Box::new(lit_int(3)),
9339 }),
9340 }
9341 );
9342 }
9343
9344 #[test]
9345 fn parentheses_override_precedence() {
9346 let s = parse("SELECT (1 + 2) * 3");
9347 let Statement::Select(s) = s else {
9348 panic!("expected SELECT")
9349 };
9350 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9351 panic!()
9352 };
9353 assert_eq!(
9354 expr,
9355 &Expr::Binary {
9356 lhs: Box::new(Expr::Binary {
9357 lhs: Box::new(lit_int(1)),
9358 op: BinOp::Add,
9359 rhs: Box::new(lit_int(2)),
9360 }),
9361 op: BinOp::Mul,
9362 rhs: Box::new(lit_int(3)),
9363 }
9364 );
9365 }
9366
9367 #[test]
9368 fn not_binds_below_comparison() {
9369 let s = parse("SELECT NOT a = 1 FROM t");
9371 let Statement::Select(s) = s else {
9372 panic!("expected SELECT")
9373 };
9374 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9375 panic!()
9376 };
9377 assert_eq!(
9378 expr,
9379 &Expr::Unary {
9380 op: UnOp::Not,
9381 expr: Box::new(Expr::Binary {
9382 lhs: Box::new(col("a")),
9383 op: BinOp::Eq,
9384 rhs: Box::new(lit_int(1)),
9385 }),
9386 }
9387 );
9388 }
9389
9390 #[test]
9391 fn unary_minus_binds_above_multiplication() {
9392 let s = parse("SELECT -a * 2 FROM t");
9394 let Statement::Select(s) = s else {
9395 panic!("expected SELECT")
9396 };
9397 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9398 panic!()
9399 };
9400 assert_eq!(
9401 expr,
9402 &Expr::Binary {
9403 lhs: Box::new(Expr::Unary {
9404 op: UnOp::Neg,
9405 expr: Box::new(col("a")),
9406 }),
9407 op: BinOp::Mul,
9408 rhs: Box::new(lit_int(2)),
9409 }
9410 );
9411 }
9412
9413 #[test]
9414 fn qualified_column() {
9415 let s = parse("SELECT t.col FROM t");
9416 let Statement::Select(s) = s else {
9417 panic!("expected SELECT")
9418 };
9419 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9420 panic!()
9421 };
9422 assert_eq!(
9423 expr,
9424 &Expr::Column(ColumnName {
9425 qualifier: Some("t".into()),
9426 name: "col".into()
9427 })
9428 );
9429 }
9430
9431 #[test]
9432 fn select_item_alias_with_as() {
9433 let s = parse("SELECT a AS y FROM t");
9434 let Statement::Select(s) = s else {
9435 panic!("expected SELECT")
9436 };
9437 let SelectItem::Expr { alias, .. } = &s.items[0] else {
9438 panic!()
9439 };
9440 assert_eq!(alias.as_deref(), Some("y"));
9441 }
9442
9443 #[test]
9444 fn trailing_semicolon_accepted() {
9445 let s = parse("SELECT 1;");
9446 let Statement::Select(s) = s else {
9447 panic!("expected SELECT")
9448 };
9449 assert_eq!(s.items.len(), 1);
9450 }
9451
9452 #[test]
9453 fn boolean_chain_with_and_or_not() {
9454 let s = parse("SELECT NOT a OR b AND NOT c FROM t");
9456 let Statement::Select(s) = s else {
9457 panic!("expected SELECT")
9458 };
9459 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9460 panic!()
9461 };
9462 let expected = Expr::Binary {
9463 lhs: Box::new(Expr::Unary {
9464 op: UnOp::Not,
9465 expr: Box::new(col("a")),
9466 }),
9467 op: BinOp::Or,
9468 rhs: Box::new(Expr::Binary {
9469 lhs: Box::new(col("b")),
9470 op: BinOp::And,
9471 rhs: Box::new(Expr::Unary {
9472 op: UnOp::Not,
9473 expr: Box::new(col("c")),
9474 }),
9475 }),
9476 };
9477 assert_eq!(expr, &expected);
9478 }
9479
9480 #[test]
9481 fn empty_input_errors() {
9482 assert!(matches!(parse_statement("").unwrap(), Statement::Empty));
9489 assert!(matches!(
9490 parse_statement(" \n\t ").unwrap(),
9491 Statement::Empty
9492 ));
9493 assert!(parse_statement("SELECT FROM WHERE").is_err());
9495 }
9496
9497 #[test]
9498 fn unmatched_paren_errors() {
9499 assert!(parse_statement("SELECT (1 + 2").is_err());
9500 }
9501
9502 #[test]
9503 fn display_round_trip_simple_select() {
9504 let original = parse("SELECT a + 1 FROM t WHERE a > 0");
9505 let text = original.to_string();
9506 let again = parse_statement(&text).expect("re-parse");
9507 assert_eq!(original, again);
9508 }
9509
9510 #[test]
9513 fn create_table_single_column() {
9514 let s = parse("CREATE TABLE foo (a INT)");
9515 let Statement::CreateTable(c) = s else {
9516 panic!("expected CreateTable")
9517 };
9518 assert_eq!(c.name, "foo");
9519 assert_eq!(c.columns.len(), 1);
9520 assert_eq!(c.columns[0].name, "a");
9521 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
9522 assert!(c.columns[0].nullable);
9523 }
9524
9525 #[test]
9526 fn create_table_multi_column_with_not_null_mix() {
9527 let s = parse("CREATE TABLE u (id INT NOT NULL, name TEXT, score FLOAT NOT NULL, ok BOOL)");
9528 let Statement::CreateTable(c) = s else {
9529 panic!()
9530 };
9531 assert_eq!(c.columns.len(), 4);
9532 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
9533 assert!(!c.columns[0].nullable);
9534 assert_eq!(c.columns[1].ty, ColumnTypeName::Text);
9535 assert!(c.columns[1].nullable);
9536 assert_eq!(c.columns[2].ty, ColumnTypeName::Float);
9537 assert!(!c.columns[2].nullable);
9538 assert_eq!(c.columns[3].ty, ColumnTypeName::Bool);
9539 }
9540
9541 #[test]
9542 fn create_table_bigint_supported() {
9543 let s = parse("CREATE TABLE accounts (id BIGINT NOT NULL)");
9544 let Statement::CreateTable(c) = s else {
9545 panic!()
9546 };
9547 assert_eq!(c.columns[0].ty, ColumnTypeName::BigInt);
9548 }
9549
9550 #[test]
9551 fn create_table_vector_default_is_f32() {
9552 let s = parse("CREATE TABLE t (v VECTOR(128))");
9553 let Statement::CreateTable(c) = s else {
9554 panic!()
9555 };
9556 assert_eq!(
9557 c.columns[0].ty,
9558 ColumnTypeName::Vector {
9559 dim: 128,
9560 encoding: VecEncoding::F32,
9561 },
9562 );
9563 }
9564
9565 #[test]
9566 fn create_table_vector_using_sq8() {
9567 for sql in [
9570 "CREATE TABLE t (v VECTOR(128) USING SQ8)",
9571 "CREATE TABLE t (v VECTOR(128) using sq8)",
9572 ] {
9573 let s = parse(sql);
9574 let Statement::CreateTable(c) = s else {
9575 panic!()
9576 };
9577 assert_eq!(
9578 c.columns[0].ty,
9579 ColumnTypeName::Vector {
9580 dim: 128,
9581 encoding: VecEncoding::Sq8,
9582 },
9583 "{sql}",
9584 );
9585 }
9586 }
9587
9588 #[test]
9589 fn create_table_vector_using_unknown_errors() {
9590 let err = parse_statement("CREATE TABLE t (v VECTOR(8) USING PQ8)").unwrap_err();
9599 assert!(
9600 err.message.contains("USING")
9601 || err.message.contains("using")
9602 || err.message.contains("')'")
9603 || err.message.contains("','"),
9604 "expected USING/column-list rejection, got: {}",
9605 err.message
9606 );
9607 }
9608
9609 #[test]
9610 fn vector_using_sq8_display_roundtrips() {
9611 let s = parse("CREATE TABLE t (v VECTOR(64) USING SQ8)");
9614 let Statement::CreateTable(c) = s else {
9615 panic!()
9616 };
9617 assert_eq!(c.columns[0].ty.to_string(), "VECTOR(64) USING SQ8");
9618 }
9619
9620 #[test]
9621 fn parser_recognises_placeholders() {
9622 use crate::ast::{Expr, SelectItem, Statement};
9623 let s = parse("SELECT $1, $2 + 1 FROM t WHERE x = $3");
9625 let Statement::Select(sel) = s else { panic!() };
9626 assert!(matches!(
9627 sel.items[0],
9628 SelectItem::Expr {
9629 expr: Expr::Placeholder(1),
9630 alias: None
9631 }
9632 ));
9633 let SelectItem::Expr {
9635 expr: Expr::Binary { lhs, rhs, .. },
9636 ..
9637 } = &sel.items[1]
9638 else {
9639 panic!()
9640 };
9641 assert!(matches!(**lhs, Expr::Placeholder(2)));
9642 assert!(matches!(**rhs, Expr::Literal(Literal::Integer(1))));
9643 let Some(Expr::Binary { rhs, .. }) = sel.where_.as_ref() else {
9645 panic!()
9646 };
9647 assert!(matches!(**rhs, Expr::Placeholder(3)));
9648 }
9649
9650 #[test]
9651 fn parser_rejects_dollar_zero() {
9652 assert!(parse_statement("SELECT $0").is_err());
9654 }
9655
9656 #[test]
9657 fn placeholder_display_roundtrips() {
9658 let s = parse("SELECT $42 FROM t");
9661 let printed = s.to_string();
9662 assert!(printed.contains("$42"));
9663 let again = parse(&printed);
9664 assert_eq!(s, again);
9665 }
9666
9667 #[test]
9668 fn alter_index_rebuild_bare() {
9669 use crate::ast::{AlterIndexTarget, Statement};
9670 let s = parse("ALTER INDEX my_idx REBUILD");
9671 let Statement::AlterIndex(a) = s else {
9672 panic!("expected AlterIndex, got {s:?}")
9673 };
9674 assert_eq!(a.name, "my_idx");
9675 assert_eq!(a.target, AlterIndexTarget::Rebuild { encoding: None });
9676 }
9677
9678 #[test]
9679 fn alter_index_rebuild_with_encoding() {
9680 use crate::ast::{AlterIndexTarget, Statement};
9681 for (sql, want) in [
9682 (
9683 "ALTER INDEX my_idx REBUILD WITH (encoding = F32)",
9684 VecEncoding::F32,
9685 ),
9686 (
9687 "ALTER INDEX my_idx REBUILD WITH (encoding = sq8)",
9688 VecEncoding::Sq8,
9689 ),
9690 (
9691 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
9692 VecEncoding::F16,
9693 ),
9694 ] {
9695 let s = parse(sql);
9696 let Statement::AlterIndex(a) = s else {
9697 panic!("{sql}: expected AlterIndex")
9698 };
9699 assert_eq!(a.name, "my_idx");
9700 assert_eq!(
9701 a.target,
9702 AlterIndexTarget::Rebuild {
9703 encoding: Some(want)
9704 },
9705 "{sql}"
9706 );
9707 }
9708 }
9709
9710 #[test]
9711 fn alter_index_rebuild_unknown_encoding_errors() {
9712 let err = parse_statement("ALTER INDEX my_idx REBUILD WITH (encoding = PQ8)").unwrap_err();
9713 assert!(
9714 err.message.contains("unknown vector encoding"),
9715 "got: {}",
9716 err.message
9717 );
9718 }
9719
9720 #[test]
9721 fn alter_index_rebuild_display_roundtrips() {
9722 for (input, want) in [
9723 ("ALTER INDEX my_idx REBUILD", "ALTER INDEX my_idx REBUILD"),
9724 (
9725 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
9726 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
9727 ),
9728 (
9729 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
9730 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
9731 ),
9732 ] {
9733 let s = parse(input);
9734 assert_eq!(s.to_string(), want);
9735 }
9736 }
9737
9738 #[test]
9739 fn create_table_unknown_type_defers_to_engine() {
9740 let stmt = parse_statement("CREATE TABLE x (a xml)").unwrap();
9747 let Statement::CreateTable(t) = stmt else {
9748 panic!("expected CreateTable");
9749 };
9750 assert_eq!(t.columns[0].user_type_ref.as_deref(), Some("xml"));
9751 }
9752
9753 #[test]
9754 fn create_table_missing_table_keyword_errors() {
9755 assert!(parse_statement("CREATE x (a INT)").is_err());
9756 }
9757
9758 #[test]
9759 fn insert_single_value() {
9760 let s = parse("INSERT INTO foo VALUES (42)");
9761 let Statement::Insert(i) = s else {
9762 panic!("expected Insert")
9763 };
9764 assert_eq!(i.table, "foo");
9765 assert_eq!(i.rows.len(), 1);
9766 assert_eq!(i.rows[0].len(), 1);
9767 assert!(matches!(i.rows[0][0], Expr::Literal(Literal::Integer(42))));
9768 }
9769
9770 #[test]
9771 fn insert_multi_value_with_mixed_literals() {
9772 let s = parse("INSERT INTO foo VALUES (1, 'hi', 3.14, TRUE, NULL)");
9773 let Statement::Insert(i) = s else { panic!() };
9774 assert_eq!(i.rows.len(), 1);
9775 assert_eq!(i.rows[0].len(), 5);
9776 }
9777
9778 #[test]
9779 fn insert_missing_into_errors() {
9780 assert!(parse_statement("INSERT foo VALUES (1)").is_err());
9781 }
9782
9783 #[test]
9784 fn create_table_round_trip() {
9785 let original =
9786 parse("CREATE TABLE foo (id BIGINT NOT NULL, label TEXT, score FLOAT NOT NULL)");
9787 let text = original.to_string();
9788 let again = parse_statement(&text).expect("re-parse");
9789 assert_eq!(original, again);
9790 }
9791
9792 #[test]
9793 fn insert_round_trip_with_negation_and_string() {
9794 let original = parse("INSERT INTO t VALUES (-1, 'it''s', NULL)");
9795 let text = original.to_string();
9796 let again = parse_statement(&text).expect("re-parse");
9797 assert_eq!(original, again);
9798 }
9799
9800 #[test]
9801 fn unknown_keyword_at_statement_start_errors() {
9802 let err = parse_statement("FROBNICATE foo SET x = 1").unwrap_err();
9805 assert!(err.message.contains("expected SELECT"));
9806 }
9807
9808 #[test]
9811 fn create_index_basic() {
9812 let s = parse("CREATE INDEX idx_id ON users (id)");
9813 let Statement::CreateIndex(c) = s else {
9814 panic!("expected CreateIndex")
9815 };
9816 assert_eq!(c.name, "idx_id");
9817 assert_eq!(c.table, "users");
9818 assert_eq!(c.column, "id");
9819 }
9820
9821 #[test]
9822 fn create_index_missing_on_errors() {
9823 assert!(parse_statement("CREATE INDEX foo users (id)").is_err());
9824 }
9825
9826 #[test]
9827 fn create_index_missing_paren_errors() {
9828 assert!(parse_statement("CREATE INDEX foo ON users id").is_err());
9829 }
9830
9831 #[test]
9832 fn create_index_round_trip() {
9833 let original = parse("CREATE INDEX by_name ON users (name)");
9834 let again = parse_statement(&original.to_string()).unwrap();
9835 assert_eq!(original, again);
9836 }
9837
9838 #[test]
9841 fn create_unique_index_basic() {
9842 let s = parse("CREATE UNIQUE INDEX uq_x ON t (a)");
9843 let Statement::CreateIndex(c) = s else {
9844 panic!("expected CreateIndex");
9845 };
9846 assert!(c.is_unique);
9847 assert_eq!(c.column, "a");
9848 assert!(c.partial_predicate.is_none());
9849 }
9850
9851 #[test]
9852 fn create_unique_index_partial() {
9853 let s = parse(
9855 "CREATE UNIQUE INDEX idx_email_templates_user_default \
9856 ON email_templates (user_address) WHERE is_default = true",
9857 );
9858 let Statement::CreateIndex(c) = s else {
9859 panic!("expected CreateIndex");
9860 };
9861 assert!(c.is_unique);
9862 assert_eq!(c.table, "email_templates");
9863 assert_eq!(c.column, "user_address");
9864 assert!(c.partial_predicate.is_some());
9865 }
9866
9867 #[test]
9868 fn create_unique_index_composite_with_predicate() {
9869 let s = parse(
9871 "CREATE UNIQUE INDEX uq_calendar_events_instance \
9872 ON calendar_events (calendar_id, uid, recurrence_id) \
9873 WHERE recurrence_id IS NOT NULL",
9874 );
9875 let Statement::CreateIndex(c) = s else {
9876 panic!("expected CreateIndex");
9877 };
9878 assert!(c.is_unique);
9879 assert_eq!(c.column, "calendar_id");
9880 assert_eq!(
9881 c.extra_columns,
9882 vec!["uid".to_string(), "recurrence_id".to_string()]
9883 );
9884 assert!(c.partial_predicate.is_some());
9885 }
9886
9887 #[test]
9888 fn create_unique_index_using_btree_ok() {
9889 let s = parse("CREATE UNIQUE INDEX uq_x ON t USING btree (a)");
9890 assert!(matches!(s, Statement::CreateIndex(ref c) if c.is_unique));
9891 }
9892
9893 #[test]
9894 fn create_unique_index_using_hnsw_rejected() {
9895 let err =
9896 parse_statement("CREATE UNIQUE INDEX uq_v ON t USING hnsw (embedding)").unwrap_err();
9897 assert!(err.message.contains("UNIQUE"), "{}", err.message);
9898 }
9899
9900 #[test]
9901 fn create_unique_index_round_trip() {
9902 let original = parse(
9903 "CREATE UNIQUE INDEX uq_calendar_events_master \
9904 ON calendar_events (calendar_id, uid) WHERE recurrence_id IS NULL",
9905 );
9906 let again = parse_statement(&original.to_string()).unwrap();
9907 assert_eq!(original, again);
9908 }
9909
9910 #[test]
9911 fn create_unique_without_index_errors() {
9912 let err = parse_statement("CREATE UNIQUE TABLE t (a INT)").unwrap_err();
9913 assert!(err.message.contains("INDEX"), "{}", err.message);
9914 }
9915
9916 #[test]
9919 fn create_table_bytea_column() {
9920 let s = parse("CREATE TABLE t (id INT NOT NULL, payload BYTEA NOT NULL)");
9921 let Statement::CreateTable(c) = s else {
9922 panic!("expected CreateTable");
9923 };
9924 assert_eq!(c.columns.len(), 2);
9925 assert_eq!(c.columns[1].ty, ColumnTypeName::Bytes);
9926 assert!(!c.columns[1].nullable);
9927 }
9928
9929 #[test]
9930 fn create_table_bytes_alias_column() {
9931 let s = parse("CREATE TABLE t (blob BYTES)");
9932 let Statement::CreateTable(c) = s else {
9933 panic!("expected CreateTable");
9934 };
9935 assert_eq!(c.columns[0].ty, ColumnTypeName::Bytes);
9936 }
9937
9938 #[test]
9939 fn bytea_round_trip_display() {
9940 let original = parse("CREATE TABLE t (a BYTEA NOT NULL)");
9941 let again = parse_statement(&original.to_string()).unwrap();
9942 assert_eq!(original, again);
9943 }
9944
9945 #[test]
9948 fn begin_commit_rollback_parse_as_unit_variants() {
9949 assert_eq!(parse("BEGIN"), Statement::Begin);
9950 assert_eq!(parse("COMMIT"), Statement::Commit);
9951 assert_eq!(parse("ROLLBACK"), Statement::Rollback);
9952 assert_eq!(parse("BEGIN;"), Statement::Begin);
9954 }
9955
9956 #[test]
9959 fn inner_product_binop_parses() {
9960 let s = parse("SELECT v <#> [1.0, 2.0] FROM t");
9961 let Statement::Select(s) = s else { panic!() };
9962 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9963 panic!()
9964 };
9965 assert!(matches!(
9966 expr,
9967 Expr::Binary {
9968 op: BinOp::InnerProduct,
9969 ..
9970 }
9971 ));
9972 }
9973
9974 #[test]
9975 fn cosine_distance_binop_parses() {
9976 let s = parse("SELECT v <=> [1.0, 2.0] FROM t");
9977 let Statement::Select(s) = s else { panic!() };
9978 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9979 panic!()
9980 };
9981 assert!(matches!(
9982 expr,
9983 Expr::Binary {
9984 op: BinOp::CosineDistance,
9985 ..
9986 }
9987 ));
9988 }
9989
9990 #[test]
9991 fn vector_cast_postfix_wraps_string_literal() {
9992 let s = parse("SELECT '[1,2,3]'::vector FROM t");
9993 let Statement::Select(s) = s else { panic!() };
9994 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9995 panic!()
9996 };
9997 assert!(matches!(
9998 expr,
9999 Expr::Cast {
10000 target: CastTarget::Vector,
10001 ..
10002 }
10003 ));
10004 }
10005
10006 #[test]
10007 fn unsupported_cast_target_errors() {
10008 let err = parse_statement("SELECT 1::numeric FROM t").unwrap_err();
10010 assert!(err.message.contains("unsupported cast target"));
10011 }
10012
10013 #[test]
10014 fn tx_statements_round_trip() {
10015 for q in ["BEGIN", "COMMIT", "ROLLBACK"] {
10016 let original = parse(q);
10017 let again = parse_statement(&original.to_string()).unwrap();
10018 assert_eq!(original, again);
10019 }
10020 }
10021
10022 #[test]
10023 fn interval_text_parsing_units() {
10024 assert_eq!(parse_interval_text("1 day"), Some((0, 86_400_000_000)));
10026 assert_eq!(parse_interval_text("1 second"), Some((0, 1_000_000)));
10027 assert_eq!(parse_interval_text("1 month"), Some((1, 0)));
10028 assert_eq!(parse_interval_text("2 years"), Some((24, 0)));
10029 assert_eq!(parse_interval_text("1 year 6 months"), Some((18, 0)));
10031 assert_eq!(
10032 parse_interval_text("1 day 2 hours"),
10033 Some((0, 86_400_000_000 + 7_200_000_000))
10034 );
10035 assert_eq!(parse_interval_text("-1 day"), Some((0, -86_400_000_000)));
10037 assert_eq!(parse_interval_text(""), None);
10039 assert_eq!(parse_interval_text("garbage"), None);
10040 assert_eq!(parse_interval_text("1 fortnight"), None);
10041 assert_eq!(parse_interval_text("1"), None);
10042 }
10043
10044 #[test]
10045 fn interval_literal_roundtrips_via_display() {
10046 let parsed = parse("SELECT INTERVAL '1 day 2 hours'");
10047 let s = parsed.to_string();
10048 assert!(s.contains("INTERVAL '1 day 2 hours'"), "got: {s}");
10050 let again = parse_statement(&s).unwrap();
10052 assert_eq!(parsed, again);
10053 }
10054
10055 #[test]
10058 fn parser_recognises_create_publication_bare() {
10059 let s = parse("CREATE PUBLICATION pub_a");
10060 let Statement::CreatePublication(p) = s else {
10061 panic!("expected CreatePublication, got {s:?}")
10062 };
10063 assert_eq!(p.name, "pub_a");
10064 assert_eq!(p.scope, PublicationScope::AllTables);
10065 }
10066
10067 #[test]
10068 fn parser_recognises_create_publication_for_all_tables() {
10069 let s = parse("CREATE PUBLICATION pub_a FOR ALL TABLES");
10070 let Statement::CreatePublication(p) = s else {
10071 panic!("expected CreatePublication, got {s:?}")
10072 };
10073 assert_eq!(p.name, "pub_a");
10074 assert_eq!(p.scope, PublicationScope::AllTables);
10075 }
10076
10077 #[test]
10078 fn parser_recognises_drop_publication() {
10079 let s = parse("DROP PUBLICATION pub_a");
10080 let Statement::DropPublication(name) = s else {
10081 panic!("expected DropPublication, got {s:?}")
10082 };
10083 assert_eq!(name, "pub_a");
10084 }
10085
10086 #[test]
10087 fn parser_recognises_for_table_list() {
10088 let s = parse("CREATE PUBLICATION pub_a FOR TABLE t1, t2, t3");
10089 let Statement::CreatePublication(p) = s else {
10090 panic!("expected CreatePublication, got {s:?}")
10091 };
10092 assert_eq!(p.name, "pub_a");
10093 let PublicationScope::ForTables(ts) = p.scope else {
10094 panic!("expected ForTables scope")
10095 };
10096 assert_eq!(ts, alloc::vec!["t1", "t2", "t3"]);
10097 }
10098
10099 #[test]
10100 fn parser_recognises_for_tables_plural() {
10101 let s = parse("CREATE PUBLICATION pub_a FOR TABLES t1, t2");
10103 let Statement::CreatePublication(p) = s else {
10104 panic!("expected CreatePublication, got {s:?}")
10105 };
10106 let PublicationScope::ForTables(ts) = p.scope else {
10107 panic!("expected ForTables")
10108 };
10109 assert_eq!(ts, alloc::vec!["t1", "t2"]);
10110 }
10111
10112 #[test]
10113 fn parser_recognises_for_all_tables_except_list() {
10114 let s = parse("CREATE PUBLICATION p FOR ALL TABLES EXCEPT t1, t2");
10115 let Statement::CreatePublication(p) = s else {
10116 panic!()
10117 };
10118 let PublicationScope::AllTablesExcept(ts) = p.scope else {
10119 panic!("expected AllTablesExcept")
10120 };
10121 assert_eq!(ts, alloc::vec!["t1", "t2"]);
10122 }
10123
10124 #[test]
10125 fn parser_rejects_for_table_with_empty_list() {
10126 let err = parse_statement("CREATE PUBLICATION p FOR TABLE")
10128 .expect_err("must error on empty list");
10129 assert!(!err.message.is_empty());
10132 }
10133
10134 #[test]
10135 fn parser_recognises_show_publications() {
10136 let s = parse("SHOW PUBLICATIONS");
10139 assert!(matches!(s, Statement::ShowPublications));
10140 }
10141
10142 #[test]
10145 fn parser_recognises_create_subscription_single_publication() {
10146 let s = parse(
10147 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=127.0.0.1 port=20002' PUBLICATION pub_a",
10148 );
10149 let Statement::CreateSubscription(c) = s else {
10150 panic!("expected CreateSubscription, got {s:?}")
10151 };
10152 assert_eq!(c.name, "sub_a");
10153 assert_eq!(c.conn_str, "host=127.0.0.1 port=20002");
10154 assert_eq!(c.publications, alloc::vec!["pub_a"]);
10155 }
10156
10157 #[test]
10158 fn parser_recognises_create_subscription_multi_publication() {
10159 let s = parse("CREATE SUBSCRIPTION sub_a CONNECTION 'host=h' PUBLICATION p1, p2, p3");
10160 let Statement::CreateSubscription(c) = s else {
10161 panic!()
10162 };
10163 assert_eq!(c.publications, alloc::vec!["p1", "p2", "p3"]);
10164 }
10165
10166 #[test]
10167 fn parser_rejects_create_subscription_missing_connection() {
10168 let err = parse_statement("CREATE SUBSCRIPTION s PUBLICATION p")
10169 .expect_err("must error on missing CONNECTION");
10170 assert!(err.message.contains("CONNECTION"), "got: {}", err.message);
10171 }
10172
10173 #[test]
10174 fn parser_rejects_create_subscription_missing_publication() {
10175 let err = parse_statement("CREATE SUBSCRIPTION s CONNECTION 'host=x'")
10176 .expect_err("must error on missing PUBLICATION");
10177 assert!(err.message.contains("PUBLICATION"), "got: {}", err.message);
10178 }
10179
10180 #[test]
10181 fn parser_recognises_drop_subscription() {
10182 let s = parse("DROP SUBSCRIPTION sub_a");
10183 let Statement::DropSubscription(name) = s else {
10184 panic!("expected DropSubscription, got {s:?}")
10185 };
10186 assert_eq!(name, "sub_a");
10187 }
10188
10189 #[test]
10190 fn parser_recognises_show_subscriptions() {
10191 let s = parse("SHOW SUBSCRIPTIONS");
10192 assert!(matches!(s, Statement::ShowSubscriptions));
10193 }
10194
10195 #[test]
10196 fn parser_recognises_wait_for_wal_position_no_timeout() {
10197 let s = parse("WAIT FOR WAL POSITION 12345");
10198 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
10199 panic!("expected WaitForWalPosition, got {s:?}")
10200 };
10201 assert_eq!(pos, 12345);
10202 assert!(timeout_ms.is_none());
10203 }
10204
10205 #[test]
10206 fn parser_recognises_wait_for_wal_position_with_timeout() {
10207 let s = parse("WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000");
10208 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
10209 panic!()
10210 };
10211 assert_eq!(pos, 67890);
10212 assert_eq!(timeout_ms, Some(5000));
10213 }
10214
10215 #[test]
10216 fn parser_rejects_wait_with_negative_position() {
10217 let err = parse_statement("WAIT FOR WAL POSITION -1").unwrap_err();
10223 assert!(!err.message.is_empty());
10224 }
10225
10226 #[test]
10227 fn parser_recognises_bare_analyze() {
10228 let s = parse("ANALYZE");
10229 assert!(matches!(s, Statement::Analyze(None)));
10230 }
10231
10232 #[test]
10233 fn parser_recognises_analyze_with_table() {
10234 let s = parse("ANALYZE users");
10235 let Statement::Analyze(Some(name)) = s else {
10236 panic!("expected Analyze, got {s:?}")
10237 };
10238 assert_eq!(name, "users");
10239 }
10240
10241 #[test]
10242 fn parser_recognises_analyze_with_quoted_table() {
10243 let s = parse("ANALYZE \"Mixed Case\"");
10244 let Statement::Analyze(Some(name)) = s else {
10245 panic!()
10246 };
10247 assert_eq!(name, "Mixed Case");
10248 }
10249
10250 #[test]
10251 fn parser_rejects_analyze_with_garbage_token() {
10252 let err = parse_statement("ANALYZE 42").expect_err("must error");
10253 assert!(!err.message.is_empty());
10254 }
10255
10256 #[test]
10257 fn analyze_display_roundtrips() {
10258 for sql in ["ANALYZE", "ANALYZE users"] {
10259 let s = parse(sql);
10260 let printed = s.to_string();
10261 let again = parse_statement(&printed)
10262 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
10263 assert_eq!(s, again);
10264 }
10265 }
10266
10267 #[test]
10268 fn wait_for_display_roundtrips() {
10269 for sql in [
10270 "WAIT FOR WAL POSITION 12345",
10271 "WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000",
10272 ] {
10273 let s = parse(sql);
10274 let printed = s.to_string();
10275 let again = parse_statement(&printed)
10276 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
10277 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
10278 }
10279 }
10280
10281 #[test]
10282 fn subscription_ddl_display_roundtrips() {
10283 for sql in [
10284 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=h port=20002' PUBLICATION pub_a",
10285 "CREATE SUBSCRIPTION sub_b CONNECTION 'host=h' PUBLICATION p1, p2",
10286 "DROP SUBSCRIPTION sub_a",
10287 "SHOW SUBSCRIPTIONS",
10288 ] {
10289 let s = parse(sql);
10290 let printed = s.to_string();
10291 let again = parse_statement(&printed)
10292 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
10293 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
10294 }
10295 }
10296
10297 #[test]
10298 fn parser_drop_dispatches_user_vs_publication() {
10299 let s = parse("DROP USER 'alice'");
10302 let Statement::DropUser(name) = s else {
10303 panic!("expected DropUser, got {s:?}")
10304 };
10305 assert_eq!(name, "alice");
10306 let s = parse("DROP PUBLICATION p1");
10308 assert!(matches!(s, Statement::DropPublication(_)));
10309 }
10310
10311 #[test]
10312 fn publication_ddl_display_roundtrips() {
10313 for sql in [
10316 "CREATE PUBLICATION pub_a",
10317 "CREATE PUBLICATION pub_a FOR ALL TABLES",
10318 "CREATE PUBLICATION pub_a FOR TABLE t1, t2",
10319 "CREATE PUBLICATION pub_a FOR ALL TABLES EXCEPT t1",
10320 "DROP PUBLICATION pub_a",
10321 "SHOW PUBLICATIONS",
10322 ] {
10323 let s = parse(sql);
10324 let printed = s.to_string();
10325 let again = parse_statement(&printed)
10326 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
10327 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
10328 }
10329 }
10330
10331 #[test]
10334 fn create_function_returns_trigger_plpgsql_minimal() {
10335 let sql = "CREATE FUNCTION noop() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RETURN NEW; END; $$";
10336 let s = parse(sql);
10337 let Statement::CreateFunction(f) = s else {
10338 panic!("expected CreateFunction");
10339 };
10340 assert_eq!(f.name, "noop");
10341 assert!(!f.or_replace);
10342 assert!(f.args.is_empty());
10343 assert!(matches!(f.returns, FunctionReturn::Trigger));
10344 assert_eq!(f.language, "plpgsql");
10345 let FunctionBody::PlPgSql(block) = f.body else {
10346 panic!("expected PlPgSql body");
10347 };
10348 assert_eq!(block.statements.len(), 1);
10349 assert!(matches!(
10350 block.statements[0],
10351 PlPgSqlStmt::Return(ReturnTarget::New)
10352 ));
10353 }
10354
10355 #[test]
10356 fn create_function_or_replace_with_assignment() {
10357 let sql = "CREATE OR REPLACE FUNCTION update_sv() RETURNS TRIGGER LANGUAGE plpgsql AS $$
10360BEGIN
10361 NEW.search_vector := to_tsvector('english', NEW.subject);
10362 RETURN NEW;
10363END;
10364$$";
10365 let s = parse(sql);
10366 let Statement::CreateFunction(f) = s else {
10367 panic!("expected CreateFunction");
10368 };
10369 assert!(f.or_replace);
10370 let FunctionBody::PlPgSql(block) = &f.body else {
10371 panic!("expected PlPgSql body");
10372 };
10373 assert_eq!(block.statements.len(), 2);
10374 let PlPgSqlStmt::Assign { target, .. } = &block.statements[0] else {
10376 panic!("expected Assign as first stmt");
10377 };
10378 match target {
10379 AssignTarget::NewColumn(c) => assert_eq!(c, "search_vector"),
10380 other => panic!("expected NEW.col, got {other:?}"),
10381 }
10382 assert!(matches!(
10384 block.statements[1],
10385 PlPgSqlStmt::Return(ReturnTarget::New)
10386 ));
10387 }
10388
10389 #[test]
10390 fn create_trigger_after_insert_or_update() {
10391 let sql = "CREATE TRIGGER tg AFTER INSERT OR UPDATE ON messages FOR EACH ROW EXECUTE FUNCTION update_sv()";
10392 let s = parse(sql);
10393 let Statement::CreateTrigger(t) = s else {
10394 panic!("expected CreateTrigger");
10395 };
10396 assert_eq!(t.name, "tg");
10397 assert_eq!(t.table, "messages");
10398 assert_eq!(t.timing, TriggerTiming::After);
10399 assert_eq!(t.events, vec![TriggerEvent::Insert, TriggerEvent::Update]);
10400 assert_eq!(t.for_each, TriggerForEach::Row);
10401 assert_eq!(t.function, "update_sv");
10402 }
10403
10404 #[test]
10405 fn create_trigger_before_delete_execute_procedure_alias() {
10406 let sql =
10408 "CREATE TRIGGER guard BEFORE DELETE ON t FOR EACH ROW EXECUTE PROCEDURE block_delete()";
10409 let s = parse(sql);
10410 let Statement::CreateTrigger(t) = s else {
10411 panic!("expected CreateTrigger");
10412 };
10413 assert_eq!(t.timing, TriggerTiming::Before);
10414 assert_eq!(t.events, vec![TriggerEvent::Delete]);
10415 }
10416
10417 #[test]
10418 fn drop_trigger_if_exists_round_trips() {
10419 let s = Statement::DropTrigger {
10424 name: "tg".into(),
10425 table: "messages".into(),
10426 if_exists: true,
10427 };
10428 assert_eq!(s.to_string(), "DROP TRIGGER IF EXISTS tg ON messages");
10429 }
10430
10431 #[test]
10432 fn trigger_ddl_display_roundtrips_through_parser() {
10433 for sql in [
10437 "CREATE TRIGGER tg AFTER INSERT ON t FOR EACH ROW EXECUTE FUNCTION f()",
10438 "CREATE TRIGGER tg2 BEFORE UPDATE OR DELETE ON t FOR EACH ROW EXECUTE FUNCTION g()",
10439 ] {
10440 let s = parse(sql);
10441 let printed = s.to_string();
10442 let again = parse_statement(&printed)
10443 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
10444 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
10445 }
10446 }
10447}