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 IsolationLevel, JoinKind, Literal, NullTreatment, OrderBy, PlPgSqlBlock, PlPgSqlDeclare,
27 PlPgSqlStmt, PublicationScope, RaiseLevel, RangeKindAst, ReturnTarget, SelectItem,
28 SelectStatement, Statement, TableRef, TriggerEvent, TriggerForEach, TriggerTiming, UnOp,
29 UnionKind, VecEncoding, 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 unreserved_keyword_text(tok: &Token) -> Option<String> {
96 let s = match tok {
97 Token::Release => "release",
99 Token::Savepoint => "savepoint",
100 Token::Show => "show",
101 Token::Index => "index",
102 Token::Begin => "begin",
103 Token::Commit => "commit",
104 Token::Rollback => "rollback",
105 Token::Drop => "drop",
106 Token::Insert => "insert",
107 Token::Values => "values",
108 Token::Limit => "limit",
109 Token::Partition => "partition",
110 Token::Tables => "tables",
111 Token::Connection => "connection",
112 Token::Publication => "publication",
113 Token::Subscription => "subscription",
114 Token::Interval => "interval",
115 Token::Extract => "extract",
119 Token::Offset => "offset",
120 _ => return None,
129 };
130 Some(s.to_string())
131}
132
133fn is_vector_opclass_name(name: &str) -> bool {
141 let lc = name.to_ascii_lowercase();
142 matches!(
143 lc.as_str(),
144 "vector_cosine_ops"
145 | "vector_l2_ops"
146 | "vector_ip_ops"
147 | "halfvec_cosine_ops"
148 | "halfvec_l2_ops"
149 | "halfvec_ip_ops"
150 | "sq8_cosine_ops"
151 | "sq8_l2_ops"
152 | "sq8_ip_ops"
153 | "gin_trgm_ops"
159 | "gist_trgm_ops"
160 | "text_pattern_ops"
165 | "varchar_pattern_ops"
166 | "bpchar_pattern_ops"
167 | "int4_ops"
168 | "int8_ops"
169 | "text_ops"
170 )
171}
172
173#[derive(Debug, Clone, PartialEq, Eq)]
174pub struct ParseError {
175 pub message: String,
176 pub token_pos: usize,
178}
179
180impl fmt::Display for ParseError {
181 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182 write!(
183 f,
184 "parse error at token #{}: {}",
185 self.token_pos, self.message
186 )
187 }
188}
189
190impl From<LexError> for ParseError {
191 fn from(e: LexError) -> Self {
192 Self {
193 message: format!("lex: {e}"),
194 token_pos: 0,
195 }
196 }
197}
198
199pub fn parse_expression(input: &str) -> Result<Expr, ParseError> {
205 let tokens = lexer::tokenize(input)?;
206 let mut p = Parser::new(tokens);
207 let expr = p.parse_expr(0)?;
208 p.expect_eof()?;
209 Ok(expr)
210}
211
212pub fn parse_statement(input: &str) -> Result<Statement, ParseError> {
215 parse_statement_with(input, false)
216}
217
218pub fn parse_statement_with(input: &str, backslash_escapes: bool) -> Result<Statement, ParseError> {
222 let tokens = lexer::tokenize_with(input, backslash_escapes)?;
223 let mut p = Parser::new(tokens);
224 let stmt = p.parse_one_statement()?;
225 if matches!(p.peek(), Token::Semicolon) {
226 p.advance();
227 }
228 p.expect_eof()?;
229 Ok(stmt)
230}
231
232struct Parser {
233 tokens: Vec<Token>,
234 pos: usize,
235 nest_depth: usize,
241}
242
243const MAX_NEST_DEPTH: usize = 64;
250
251const MAX_BINARY_CHAIN: usize = 256;
257
258enum NamedTableConstraintKind {
262 Check,
263 Unique,
264 PrimaryKey,
265}
266
267impl Parser {
268 fn new(tokens: Vec<Token>) -> Self {
269 Self {
270 tokens,
271 pos: 0,
272 nest_depth: 0,
273 }
274 }
275
276 fn enter_nested(&mut self) -> Result<(), ParseError> {
279 self.nest_depth += 1;
280 if self.nest_depth > MAX_NEST_DEPTH {
281 self.nest_depth -= 1;
282 return Err(self.err(alloc::format!(
283 "statement nests deeper than {MAX_NEST_DEPTH} levels"
284 )));
285 }
286 Ok(())
287 }
288
289 fn peek(&self) -> &Token {
290 &self.tokens[self.pos]
292 }
293
294 fn advance(&mut self) -> Token {
295 let t = mem::replace(&mut self.tokens[self.pos], Token::Eof);
296 if self.pos + 1 < self.tokens.len() {
297 self.pos += 1;
298 }
299 t
300 }
301
302 fn err(&self, message: String) -> ParseError {
303 ParseError {
304 message,
305 token_pos: self.pos,
306 }
307 }
308
309 fn expect_eof(&self) -> Result<(), ParseError> {
310 if matches!(self.peek(), Token::Eof) {
311 Ok(())
312 } else {
313 Err(self.err(format!("expected end of input, got {:?}", self.peek())))
314 }
315 }
316
317 fn consume_until_statement_boundary(&mut self) {
322 loop {
323 match self.peek() {
324 Token::Semicolon | Token::Eof => return,
325 _ => self.advance(),
326 };
327 }
328 }
329
330 fn scan_sequence_name_until_boundary(&mut self) -> Option<String> {
336 let mut seq: Option<String> = None;
337 let mut after_sequence_kw = false;
338 let mut after_name_kw = false;
339 loop {
340 match self.peek().clone() {
341 Token::Semicolon | Token::Eof => break,
342 Token::Ident(s) | Token::QuotedIdent(s) => {
343 if after_name_kw && seq.is_none() {
344 self.advance();
345 let mut name = s;
346 while matches!(self.peek(), Token::Dot) {
349 self.advance();
350 if let Token::Ident(n) | Token::QuotedIdent(n) = self.advance() {
351 name = n;
352 }
353 }
354 seq = Some(name);
355 after_name_kw = false;
356 continue;
357 }
358 if after_sequence_kw && s.eq_ignore_ascii_case("name") {
359 after_name_kw = true;
360 after_sequence_kw = false;
361 } else {
362 after_sequence_kw = s.eq_ignore_ascii_case("sequence");
363 }
364 self.advance();
365 }
366 Token::String(s) => {
367 if seq.is_none() {
368 let bare = s
370 .rsplit_once('.')
371 .map_or_else(|| s.clone(), |(_, b)| b.to_string());
372 seq = Some(bare);
373 }
374 self.advance();
375 }
376 _ => {
377 after_sequence_kw = false;
378 after_name_kw = false;
379 self.advance();
380 }
381 }
382 }
383 seq
384 }
385
386 fn expect_ident_like(&mut self) -> Result<String, ParseError> {
387 let first = match self.advance() {
388 Token::Ident(s) | Token::QuotedIdent(s) => s,
389 other if unreserved_keyword_text(&other).is_some() => {
401 unreserved_keyword_text(&other).unwrap()
402 }
403 other => {
404 return Err(ParseError {
405 message: format!("expected identifier, got {other:?}"),
406 token_pos: self.pos.saturating_sub(1),
407 });
408 }
409 };
410 if matches!(self.peek(), Token::Dot) {
417 self.advance();
418 match self.advance() {
419 Token::Ident(s) | Token::QuotedIdent(s) => return Ok(s),
420 other if unreserved_keyword_text(&other).is_some() => {
421 return Ok(unreserved_keyword_text(&other).unwrap());
422 }
423 other => {
424 return Err(ParseError {
425 message: format!("expected identifier after '{first}.', got {other:?}"),
426 token_pos: self.pos.saturating_sub(1),
427 });
428 }
429 }
430 }
431 Ok(first)
432 }
433
434 #[allow(clippy::too_many_lines)]
435 fn parse_one_statement(&mut self) -> Result<Statement, ParseError> {
436 if matches!(self.peek(), Token::Eof | Token::Semicolon) {
444 return Ok(Statement::Empty);
445 }
446 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
456 let lc = s.to_ascii_lowercase();
457 if is_dump_noise_statement(&lc) {
458 self.consume_until_statement_boundary();
459 return Ok(Statement::Empty);
460 }
461 }
462 match self.peek() {
463 Token::Select => self.parse_select_stmt(),
464 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {
480 self.advance();
481 let body_text = match self.advance() {
482 Token::String(s) => s,
483 other => {
484 return Err(self.err(alloc::format!(
485 "expected dollar-quoted body after DO, got {other:?}"
486 )));
487 }
488 };
489 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("language")) {
491 self.advance();
492 let _ = self.expect_ident_like()?;
493 }
494 let block = parse_plpgsql_body(&body_text)?;
499 Ok(Statement::DoBlock(block))
500 }
501 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with") => {
505 self.advance();
506 self.parse_with_cte_then_select()
507 }
508 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("explain") => {
511 self.advance();
512 let mut analyze = false;
513 let mut suggest = false;
514 let mut costs_off = false;
515 if matches!(self.peek(), Token::LParen) {
520 self.advance();
521 loop {
522 let opt = match self.peek().clone() {
523 Token::Ident(s) | Token::QuotedIdent(s) => s,
524 other => {
525 return Err(self.err(format!(
526 "expected option keyword inside EXPLAIN (…), got {other:?}"
527 )));
528 }
529 };
530 self.advance();
531 if opt.eq_ignore_ascii_case("suggest") {
532 suggest = true;
533 } else if opt.eq_ignore_ascii_case("costs") {
535 let value = match self.peek().clone() {
543 Token::On => {
544 self.advance();
545 true
546 }
547 Token::Ident(v) | Token::QuotedIdent(v)
548 if v.eq_ignore_ascii_case("off") =>
549 {
550 self.advance();
551 false
552 }
553 Token::Ident(v) | Token::QuotedIdent(v)
554 if v.eq_ignore_ascii_case("true") =>
555 {
556 self.advance();
557 true
558 }
559 _ => true,
560 };
561 costs_off = !value;
562 } else {
563 return Err(self.err(format!(
564 "unknown EXPLAIN option {opt:?}; v7.37.7 supports SUGGEST, COSTS"
565 )));
566 }
567 if matches!(self.peek(), Token::Comma) {
568 self.advance();
569 continue;
570 }
571 break;
572 }
573 if !matches!(self.peek(), Token::RParen) {
574 return Err(self.err(format!(
575 "expected ')' after EXPLAIN options, got {:?}",
576 self.peek()
577 )));
578 }
579 self.advance();
580 } else if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
581 && (s.eq_ignore_ascii_case("analyze") || s.eq_ignore_ascii_case("analyse"))
582 {
583 self.advance();
584 analyze = true;
585 }
586 let inner = self.parse_select_stmt()?;
587 let Statement::Select(s) = inner else {
588 return Err(self.err(format!("EXPLAIN body must be a SELECT, got {inner:?}")));
589 };
590 Ok(Statement::Explain(crate::ast::ExplainStatement {
591 analyze,
592 inner: Box::new(s),
593 suggest,
594 costs_off,
595 }))
596 }
597 Token::Create => self.parse_create_stmt(),
598 Token::Insert => self.parse_insert_stmt(),
599 Token::Begin => {
600 self.advance();
601 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("work") || s.eq_ignore_ascii_case("transaction"))
611 {
612 self.advance();
613 let _ = self.parse_isolation_level_clauses()?;
615 }
616 Ok(Statement::Begin)
617 }
618 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("start") => {
623 self.advance();
624 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("transaction"))
625 {
626 return Err(self.err(alloc::format!(
627 "expected TRANSACTION after START, got {:?}",
628 self.peek()
629 )));
630 }
631 self.advance();
632 let _ = self.parse_isolation_level_clauses()?;
633 Ok(Statement::Begin)
634 }
635 Token::Commit => {
636 self.advance();
637 Ok(Statement::Commit)
638 }
639 Token::Rollback => {
640 self.advance();
641 if matches!(self.peek(), Token::To) {
645 self.advance();
646 if matches!(self.peek(), Token::Savepoint) {
647 self.advance();
648 }
649 let name = self.expect_ident_like()?;
650 Ok(Statement::RollbackToSavepoint(name))
651 } else {
652 Ok(Statement::Rollback)
653 }
654 }
655 Token::Savepoint => {
656 self.advance();
657 let name = self.expect_ident_like()?;
658 Ok(Statement::Savepoint(name))
659 }
660 Token::Release => {
661 self.advance();
662 if matches!(self.peek(), Token::Savepoint) {
665 self.advance();
666 }
667 let name = self.expect_ident_like()?;
668 Ok(Statement::ReleaseSavepoint(name))
669 }
670 Token::Show => {
671 self.advance();
672 let target = match self.advance() {
678 Token::Tables => "tables".to_string(),
679 Token::Create => "create".to_string(),
683 Token::Index => "index".to_string(),
686 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
687 other => {
688 return Err(self.err(format!(
689 "expected SHOW target, got {other:?}"
690 )));
691 }
692 };
693 match target.as_str() {
694 "tables" => Ok(Statement::ShowTables),
695 "users" => Ok(Statement::ShowUsers),
696 "transaction_isolation" => Ok(Statement::ShowParameter(
699 "transaction_isolation".to_string(),
700 )),
701 "indexes" | "index" | "keys" => {
711 if !matches!(self.peek(), Token::From) {
712 return Err(self.err(format!(
713 "expected FROM after SHOW INDEXES, got {:?}",
714 self.peek()
715 )));
716 }
717 self.advance();
718 let table = self.expect_ident_like()?;
719 Ok(Statement::ShowIndexes(table))
720 }
721 "status" => Ok(Statement::ShowStatus),
726 "variables" => Ok(Statement::ShowVariables),
727 "processlist" => Ok(Statement::ShowProcesslist),
729 "create" => {
730 let kind = match self.advance() {
733 Token::Ident(s) | Token::QuotedIdent(s) => s,
734 Token::Table => "table".to_string(),
735 other => {
736 return Err(self.err(format!(
737 "expected TABLE after SHOW CREATE, got {other:?}"
738 )));
739 }
740 };
741 if !kind.eq_ignore_ascii_case("table") {
742 return Err(self.err(format!(
743 "unsupported SHOW CREATE {kind:?}; v7.17 supports TABLE only"
744 )));
745 }
746 let name = self.expect_ident_like()?;
747 Ok(Statement::ShowCreateTable(name))
748 }
749 "databases" | "schemas" => Ok(Statement::ShowDatabases),
755 "publications" => Ok(Statement::ShowPublications),
760 "subscriptions" => Ok(Statement::ShowSubscriptions),
762 "columns" => {
763 if !matches!(self.peek(), Token::From) {
764 return Err(self.err(format!(
765 "expected FROM after SHOW COLUMNS, got {:?}",
766 self.peek()
767 )));
768 }
769 self.advance();
770 let table = self.expect_ident_like()?;
771 Ok(Statement::ShowColumns(table))
772 }
773 other => Ok(Statement::ShowParameter(other.to_string())),
783 }
784 }
785 Token::Drop => {
791 self.advance();
792 match self.peek() {
793 Token::Publication => {
794 self.advance();
795 let name = self.expect_ident_or_string()?;
796 Ok(Statement::DropPublication(name))
797 }
798 Token::Subscription => {
799 self.advance();
800 let name = self.expect_ident_or_string()?;
801 Ok(Statement::DropSubscription(name))
802 }
803 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("user") => {
804 self.advance();
805 let name = self.expect_ident_or_string()?;
806 Ok(Statement::DropUser(name))
807 }
808 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("trigger") => {
810 self.advance();
811 let if_exists = self.consume_if_exists();
812 let name = self.expect_ident_like()?;
813 if !matches!(self.peek(), Token::On) {
815 return Err(self.err(alloc::format!(
816 "expected ON <table> after DROP TRIGGER {name:?}, got {:?}",
817 self.peek()
818 )));
819 }
820 self.advance();
821 let table = self.expect_ident_like()?;
822 Ok(Statement::DropTrigger {
823 name,
824 table,
825 if_exists,
826 })
827 }
828 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("function") => {
832 self.advance();
833 let if_exists = self.consume_if_exists();
834 let name = self.expect_ident_like()?;
835 if matches!(self.peek(), Token::LParen) {
837 self.advance();
838 let mut depth = 1usize;
840 while depth > 0 {
841 match self.peek() {
842 Token::LParen => depth += 1,
843 Token::RParen => depth -= 1,
844 Token::Eof => {
845 return Err(self.err(alloc::format!(
846 "unterminated arg list in DROP FUNCTION {name:?}"
847 )));
848 }
849 _ => {}
850 }
851 self.advance();
852 }
853 }
854 Ok(Statement::DropFunction { name, if_exists })
855 }
856 Token::Table => {
864 self.advance();
865 let if_exists = self.consume_if_exists();
866 let mut names: Vec<String> = Vec::new();
867 loop {
868 names.push(self.expect_ident_like()?);
869 if matches!(self.peek(), Token::Comma) {
870 self.advance();
871 continue;
872 }
873 break;
874 }
875 if matches!(
876 self.peek(),
877 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
878 || s.eq_ignore_ascii_case("restrict")
879 ) {
880 self.advance();
881 }
882 Ok(Statement::DropTable { names, if_exists })
883 }
884 Token::Index => {
890 self.advance();
891 let if_exists = self.consume_if_exists();
892 let name = self.expect_ident_like()?;
893 if matches!(
894 self.peek(),
895 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
896 || s.eq_ignore_ascii_case("restrict")
897 ) {
898 self.advance();
899 }
900 Ok(Statement::DropIndex { name, if_exists })
901 }
902 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("schema") => {
908 self.advance();
909 let if_exists = self.consume_if_exists();
910 let mut names = vec![self.expect_ident_like()?];
911 while matches!(self.peek(), Token::Comma) {
912 self.advance();
913 names.push(self.expect_ident_like()?);
914 }
915 if matches!(
916 self.peek(),
917 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
918 || s.eq_ignore_ascii_case("restrict")
919 ) {
920 self.advance();
921 }
922 Ok(Statement::DropSchema { names, if_exists })
923 }
924 Token::Ident(s) | Token::QuotedIdent(s)
927 if s.eq_ignore_ascii_case("type") =>
928 {
929 self.advance();
930 let if_exists = self.consume_if_exists();
931 let mut names = vec![self.expect_ident_like()?];
932 while matches!(self.peek(), Token::Comma) {
933 self.advance();
934 names.push(self.expect_ident_like()?);
935 }
936 if matches!(
937 self.peek(),
938 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
939 || s.eq_ignore_ascii_case("restrict")
940 ) {
941 self.advance();
942 }
943 Ok(Statement::DropType { names, if_exists })
944 }
945 Token::Ident(s) | Token::QuotedIdent(s)
948 if s.eq_ignore_ascii_case("domain") =>
949 {
950 self.advance();
951 let if_exists = self.consume_if_exists();
952 let mut names = vec![self.expect_ident_like()?];
953 while matches!(self.peek(), Token::Comma) {
954 self.advance();
955 names.push(self.expect_ident_like()?);
956 }
957 if matches!(
958 self.peek(),
959 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
960 || s.eq_ignore_ascii_case("restrict")
961 ) {
962 self.advance();
963 }
964 Ok(Statement::DropDomain { names, if_exists })
965 }
966 Token::Ident(s) | Token::QuotedIdent(s)
969 if s.eq_ignore_ascii_case("materialized") =>
970 {
971 self.advance();
972 let nxt = self.peek().clone();
973 if !matches!(&nxt, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("view"))
974 {
975 return Err(self.err(alloc::format!(
976 "expected VIEW after DROP MATERIALIZED, got {nxt:?}"
977 )));
978 }
979 self.advance();
980 let if_exists = self.consume_if_exists();
981 let mut names = vec![self.expect_ident_like()?];
982 while matches!(self.peek(), Token::Comma) {
983 self.advance();
984 names.push(self.expect_ident_like()?);
985 }
986 if matches!(
987 self.peek(),
988 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
989 || s.eq_ignore_ascii_case("restrict")
990 ) {
991 self.advance();
992 }
993 Ok(Statement::DropMaterializedView { names, if_exists })
994 }
995 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("view") => {
998 self.advance();
999 let if_exists = self.consume_if_exists();
1000 let mut names = vec![self.expect_ident_like()?];
1001 while matches!(self.peek(), Token::Comma) {
1002 self.advance();
1003 names.push(self.expect_ident_like()?);
1004 }
1005 if matches!(
1006 self.peek(),
1007 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
1008 || s.eq_ignore_ascii_case("restrict")
1009 ) {
1010 self.advance();
1011 }
1012 Ok(Statement::DropView { names, if_exists })
1013 }
1014 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sequence") => {
1018 self.advance();
1019 let if_exists = self.consume_if_exists();
1020 let mut names = vec![self.expect_ident_like()?];
1021 while matches!(self.peek(), Token::Comma) {
1022 self.advance();
1023 names.push(self.expect_ident_like()?);
1024 }
1025 if matches!(
1026 self.peek(),
1027 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
1028 || s.eq_ignore_ascii_case("restrict")
1029 ) {
1030 self.advance();
1031 }
1032 Ok(Statement::DropSequence { names, if_exists })
1033 }
1034 other => Err(self.err(format!(
1035 "expected TABLE / INDEX / SCHEMA / SEQUENCE / USER / PUBLICATION / \
1036 SUBSCRIPTION / TRIGGER / FUNCTION after DROP, got {other:?}"
1037 ))),
1038 }
1039 }
1040 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("refresh") => {
1042 self.advance();
1043 let nxt = self.peek().clone();
1044 if !matches!(&nxt, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("materialized"))
1045 {
1046 return Err(self.err(alloc::format!(
1047 "expected MATERIALIZED after REFRESH, got {nxt:?}"
1048 )));
1049 }
1050 self.advance();
1051 let nxt2 = self.peek().clone();
1052 if !matches!(&nxt2, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("view"))
1053 {
1054 return Err(self.err(alloc::format!(
1055 "expected VIEW after REFRESH MATERIALIZED, got {nxt2:?}"
1056 )));
1057 }
1058 self.advance();
1059 let name = self.expect_ident_like()?;
1060 let with_data = self.parse_optional_with_data(true)?;
1061 Ok(Statement::RefreshMaterializedView { name, with_data })
1062 }
1063 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
1064 self.advance();
1065 self.parse_update_after_keyword()
1066 }
1067 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
1068 self.advance();
1069 self.parse_delete_after_keyword()
1070 }
1071 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("alter") => {
1075 self.advance();
1076 self.parse_alter_after_keyword()
1077 }
1078 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("wait") => {
1082 self.advance();
1083 self.parse_wait_after_keyword()
1084 }
1085 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("compact") => {
1096 self.advance();
1097 let next = self.peek().clone();
1098 let cold = match next {
1099 Token::Ident(s) | Token::QuotedIdent(s) => s,
1100 _ => {
1101 return Err(
1102 self.err(format!("expected COLD after COMPACT, got {:?}", self.peek()))
1103 );
1104 }
1105 };
1106 if !cold.eq_ignore_ascii_case("cold") {
1107 return Err(self.err(format!("expected COLD after COMPACT, got {cold:?}")));
1108 }
1109 self.advance();
1110 let next = self.peek().clone();
1111 let segments = match next {
1112 Token::Ident(s) | Token::QuotedIdent(s) => s,
1113 _ => {
1114 return Err(self.err(format!(
1115 "expected SEGMENTS after COMPACT COLD, got {:?}",
1116 self.peek()
1117 )));
1118 }
1119 };
1120 if !segments.eq_ignore_ascii_case("segments") {
1121 return Err(self.err(format!(
1122 "expected SEGMENTS after COMPACT COLD, got {segments:?}"
1123 )));
1124 }
1125 self.advance();
1126 Ok(Statement::CompactColdSegments)
1127 }
1128 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("merge") => {
1135 self.advance();
1136 self.parse_merge_after_keyword()
1137 }
1138 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("analyze") => {
1139 self.advance();
1140 let target = match self.peek() {
1141 Token::Eof | Token::Semicolon => None,
1142 Token::Ident(_) | Token::QuotedIdent(_) => {
1143 Some(self.expect_ident_like()?)
1144 }
1145 other => {
1146 return Err(self.err(format!(
1147 "expected table name or end of statement after ANALYZE, got {other:?}"
1148 )));
1149 }
1150 };
1151 Ok(Statement::Analyze(target))
1152 }
1153 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {
1159 self.advance();
1160 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"))
1165 {
1166 self.advance();
1167 }
1168 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("names"))
1173 {
1174 self.advance();
1175 if matches!(
1177 self.peek(),
1178 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
1179 ) {
1180 self.advance();
1181 }
1182 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("collate"))
1184 {
1185 self.advance();
1186 if matches!(
1187 self.peek(),
1188 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
1189 ) {
1190 self.advance();
1191 }
1192 }
1193 return Ok(Statement::Empty);
1194 }
1195 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("authorization"))
1205 {
1206 self.advance(); match self.peek().clone() {
1208 Token::Default => {
1209 self.advance();
1210 }
1211 Token::String(_)
1212 | Token::Ident(_)
1213 | Token::QuotedIdent(_) => {
1214 self.advance();
1215 }
1216 other => {
1217 return Err(self.err(alloc::format!(
1218 "expected DEFAULT / '<role>' / <ident> after SET SESSION AUTHORIZATION, got {other:?}"
1219 )));
1220 }
1221 }
1222 return Ok(Statement::Empty);
1223 }
1224 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("transaction"))
1237 {
1238 self.advance(); let level = self.parse_isolation_level_clauses()?;
1240 return Ok(Statement::SetTransaction { isolation: level });
1241 }
1242 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("character"))
1245 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("set"))
1246 {
1247 self.advance(); self.advance(); if matches!(
1250 self.peek(),
1251 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
1252 ) {
1253 self.advance();
1254 }
1255 return Ok(Statement::Empty);
1256 }
1257 let mut pairs: Vec<(String, crate::ast::SetValue)> = Vec::new();
1262 loop {
1263 let lhs = match self.peek().clone() {
1264 Token::SessionVar(s) => {
1265 self.advance();
1266 s
1267 }
1268 Token::Ident(_) | Token::QuotedIdent(_) => self.parse_set_param_name()?,
1269 other => {
1270 return Err(self.err(format!(
1271 "expected parameter name after SET, got {other:?}"
1272 )));
1273 }
1274 };
1275 match self.peek() {
1277 Token::Eq => {
1278 self.advance();
1279 }
1280 Token::To => {
1281 self.advance();
1282 }
1283 other => {
1284 return Err(self.err(format!(
1285 "expected `=` or TO after SET {lhs}, got {other:?}"
1286 )));
1287 }
1288 }
1289 let value = self.parse_set_value()?;
1290 pairs.push((lhs, value));
1291 if matches!(self.peek(), Token::Comma) {
1292 self.advance();
1293 continue;
1294 }
1295 break;
1296 }
1297 if pairs.len() == 1 {
1298 let (name, value) = pairs.into_iter().next().unwrap();
1299 Ok(Statement::SetParameter { name, value })
1300 } else {
1301 Ok(Statement::SetParameterList(pairs))
1302 }
1303 }
1304 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("reset") => {
1306 self.advance();
1307 match self.peek().clone() {
1308 Token::All => {
1309 self.advance();
1310 Ok(Statement::ResetParameter(None))
1311 }
1312 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("all") => {
1313 self.advance();
1314 Ok(Statement::ResetParameter(None))
1315 }
1316 _ => {
1317 let name = self.parse_set_param_name()?;
1318 Ok(Statement::ResetParameter(Some(name)))
1319 }
1320 }
1321 }
1322 other => Err(self.err(format!(
1323 "expected SELECT / CREATE / DROP / INSERT / UPDATE / DELETE / ALTER / BEGIN / COMMIT / \
1324 ROLLBACK / SAVEPOINT / RELEASE / SHOW at start of statement, got {other:?}"
1325 ))),
1326 }
1327 }
1328
1329 fn parse_create_stmt(&mut self) -> Result<Statement, ParseError> {
1330 debug_assert!(matches!(self.peek(), Token::Create));
1331 self.advance();
1332 match self.peek() {
1333 Token::Table => self.parse_create_table_stmt_after_create(),
1334 Token::Index => self.parse_create_index_stmt_after_create(false),
1335 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unique") => {
1342 self.advance();
1343 if !matches!(self.peek(), Token::Index) {
1344 return Err(self.err(alloc::format!(
1345 "expected INDEX after CREATE UNIQUE, got {:?}",
1346 self.peek()
1347 )));
1348 }
1349 self.parse_create_index_stmt_after_create(true)
1350 }
1351 Token::Publication => {
1352 self.advance();
1353 self.parse_create_publication_after_keyword()
1354 }
1355 Token::Subscription => {
1356 self.advance();
1357 self.parse_create_subscription_after_keyword()
1358 }
1359 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("user") => {
1363 self.advance();
1364 self.parse_create_user_after_keyword()
1365 }
1366 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("extension") => {
1370 self.advance();
1371 self.parse_create_extension_after_keyword()
1372 }
1373 Token::Or => {
1379 self.advance();
1380 let next = self.peek();
1381 let (Token::Ident(s2) | Token::QuotedIdent(s2)) = next else {
1382 return Err(self.err(alloc::format!(
1383 "expected REPLACE after CREATE OR, got {next:?}"
1384 )));
1385 };
1386 if !s2.eq_ignore_ascii_case("replace") {
1387 return Err(self.err(alloc::format!(
1388 "expected REPLACE after CREATE OR, got {s2:?}"
1389 )));
1390 }
1391 self.advance();
1392 self.parse_create_function_or_trigger_after_or_replace(true)
1393 }
1394 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("function") => {
1395 self.advance();
1396 self.parse_create_function_after_keyword(false)
1397 }
1398 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("trigger") => {
1399 self.advance();
1400 self.parse_create_trigger_after_keyword(false)
1401 }
1402 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sequence") => {
1404 self.advance();
1405 self.parse_create_sequence_after_keyword(false)
1406 }
1407 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("view") => {
1409 self.advance();
1410 self.parse_create_view_after_keyword(false, false, false)
1411 }
1412 Token::Ident(s) | Token::QuotedIdent(s)
1426 if s.eq_ignore_ascii_case("algorithm")
1427 || s.eq_ignore_ascii_case("definer")
1428 || s.eq_ignore_ascii_case("sql") =>
1429 {
1430 self.consume_mysql_view_prefix()?;
1431 let next = self.peek().clone();
1436 if matches!(&next, Token::Ident(s2) | Token::QuotedIdent(s2)
1437 if s2.eq_ignore_ascii_case("view"))
1438 {
1439 self.advance();
1440 self.parse_create_view_after_keyword(false, false, false)
1441 } else {
1442 Err(self.err(alloc::format!(
1443 "expected VIEW after MySQL view prefix (ALGORITHM/DEFINER/SQL SECURITY), got {next:?}"
1444 )))
1445 }
1446 }
1447 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("type") => {
1449 self.advance();
1450 self.parse_create_type_after_keyword()
1451 }
1452 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("domain") => {
1455 self.advance();
1456 self.parse_create_domain_after_keyword()
1457 }
1458 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("schema") => {
1462 self.advance();
1463 let if_not_exists = self.parse_if_not_exists();
1464 let name = self.expect_ident_like()?;
1465 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
1468 if s.eq_ignore_ascii_case("authorization"))
1469 {
1470 self.advance();
1471 let _ = self.expect_ident_like()?;
1472 }
1473 Ok(Statement::CreateSchema { name, if_not_exists })
1474 }
1475 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("materialized") => {
1477 self.advance();
1478 let next = self.peek().clone();
1479 if matches!(&next, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("view"))
1480 {
1481 self.advance();
1482 self.parse_create_materialized_view_after_keyword()
1483 } else {
1484 Err(self.err(alloc::format!(
1485 "expected VIEW after CREATE MATERIALIZED, got {next:?}"
1486 )))
1487 }
1488 }
1489 Token::Ident(s) | Token::QuotedIdent(s)
1490 if s.eq_ignore_ascii_case("temporary") || s.eq_ignore_ascii_case("temp") =>
1491 {
1492 self.advance();
1493 let next = self.peek().clone();
1495 if matches!(&next, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("sequence"))
1496 {
1497 self.advance();
1498 self.parse_create_sequence_after_keyword(true)
1499 } else if matches!(&next, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("view"))
1500 {
1501 self.advance();
1502 self.parse_create_view_after_keyword(false, false, true)
1503 } else {
1504 self.consume_until_statement_boundary();
1506 Ok(Statement::Empty)
1507 }
1508 }
1509 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("procedure") => {
1519 self.consume_mysql_routine_body();
1520 Ok(Statement::Empty)
1521 }
1522 Token::Ident(s) | Token::QuotedIdent(s)
1533 if matches!(
1534 s.to_ascii_lowercase().as_str(),
1535 "database"
1536 | "role"
1537 | "policy"
1538 | "operator"
1539 | "cast"
1540 | "rule"
1541 | "aggregate"
1542 | "language"
1543 | "collation"
1544 | "conversion"
1545 | "statistics"
1553 | "event"
1554 | "foreign"
1555 ) =>
1556 {
1557 self.consume_until_statement_boundary();
1558 Ok(Statement::Empty)
1559 }
1560 other => Err(self.err(format!(
1561 "expected TABLE / INDEX / USER / EXTENSION / PUBLICATION / SUBSCRIPTION / FUNCTION / TRIGGER / SEQUENCE / SCHEMA / VIEW / TYPE / DOMAIN [OR REPLACE …] after CREATE, got {other:?}"
1562 ))),
1563 }
1564 }
1565
1566 fn parse_create_function_or_trigger_after_or_replace(
1571 &mut self,
1572 or_replace: bool,
1573 ) -> Result<Statement, ParseError> {
1574 let tok = self.peek();
1575 let (Token::Ident(s) | Token::QuotedIdent(s)) = tok else {
1576 return Err(self.err(alloc::format!(
1577 "expected FUNCTION / TRIGGER / VIEW after CREATE OR REPLACE, got {tok:?}"
1578 )));
1579 };
1580 if s.eq_ignore_ascii_case("function") {
1581 self.advance();
1582 self.parse_create_function_after_keyword(or_replace)
1583 } else if s.eq_ignore_ascii_case("trigger") {
1584 self.advance();
1585 self.parse_create_trigger_after_keyword(or_replace)
1586 } else if s.eq_ignore_ascii_case("view") {
1587 self.advance();
1589 self.parse_create_view_after_keyword(or_replace, false, false)
1590 } else if s.eq_ignore_ascii_case("temporary") || s.eq_ignore_ascii_case("temp") {
1591 self.advance();
1593 let nxt = self.peek().clone();
1594 if matches!(&nxt, Token::Ident(n) | Token::QuotedIdent(n) if n.eq_ignore_ascii_case("view"))
1595 {
1596 self.advance();
1597 self.parse_create_view_after_keyword(or_replace, false, true)
1598 } else {
1599 Err(self.err(alloc::format!(
1600 "expected VIEW after CREATE OR REPLACE TEMPORARY, got {nxt:?}"
1601 )))
1602 }
1603 } else {
1604 Err(self.err(alloc::format!(
1605 "expected FUNCTION / TRIGGER / VIEW after CREATE OR REPLACE, got {s:?}"
1606 )))
1607 }
1608 }
1609
1610 fn parse_create_extension_after_keyword(&mut self) -> Result<Statement, ParseError> {
1615 self.consume_if_not_exists();
1617 let name = self.expect_ident_like()?;
1618 loop {
1621 match self.peek() {
1622 Token::Ident(s) if s.eq_ignore_ascii_case("with") => {
1623 self.advance();
1624 continue;
1625 }
1626 Token::Ident(s) if s.eq_ignore_ascii_case("schema") => {
1627 self.advance();
1628 let _ = self.expect_ident_like()?;
1629 continue;
1630 }
1631 Token::Ident(s) if s.eq_ignore_ascii_case("version") => {
1632 self.advance();
1633 let _ = self.advance();
1635 continue;
1636 }
1637 Token::Ident(s) if s.eq_ignore_ascii_case("from") => {
1638 self.advance();
1639 let _ = self.advance();
1640 continue;
1641 }
1642 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => {
1643 self.advance();
1644 continue;
1645 }
1646 _ => break,
1647 }
1648 }
1649 Ok(Statement::CreateExtension(name))
1650 }
1651
1652 fn parse_create_function_after_keyword(
1664 &mut self,
1665 or_replace: bool,
1666 ) -> Result<Statement, ParseError> {
1667 let name = self.expect_ident_like()?;
1668 if !matches!(self.peek(), Token::LParen) {
1672 return Err(self.err(alloc::format!(
1673 "expected '(' after function name {name:?}, got {:?}",
1674 self.peek()
1675 )));
1676 }
1677 self.advance();
1678 let args = self.parse_function_arg_list()?;
1679 let tok = self.peek();
1681 let (Token::Ident(s) | Token::QuotedIdent(s)) = tok else {
1682 return Err(self.err(alloc::format!(
1683 "expected RETURNS after function arg list, got {tok:?}"
1684 )));
1685 };
1686 if !s.eq_ignore_ascii_case("returns") {
1687 return Err(self.err(alloc::format!(
1688 "expected RETURNS after function arg list, got {s:?}"
1689 )));
1690 }
1691 self.advance();
1692 let returns = self.parse_function_return()?;
1693 let mut language: Option<String> = self.parse_optional_language()?;
1696 if !matches!(self.peek(), Token::As) {
1700 return Err(self.err(alloc::format!(
1701 "expected AS before function body, got {:?}",
1702 self.peek()
1703 )));
1704 }
1705 self.advance();
1706 let body_text = match self.peek() {
1707 Token::String(s) => {
1708 let body = s.clone();
1709 self.advance();
1710 body
1711 }
1712 other => {
1713 return Err(self.err(alloc::format!(
1714 "expected $$-quoted function body after AS, got {other:?}"
1715 )));
1716 }
1717 };
1718 if language.is_none() {
1720 language = self.parse_optional_language()?;
1721 }
1722 let language = language.unwrap_or_else(|| String::from("sql"));
1723 let body = if language.eq_ignore_ascii_case("plpgsql") {
1728 match parse_plpgsql_body(&body_text) {
1729 Ok(block) => FunctionBody::PlPgSql(block),
1730 Err(_) => FunctionBody::Raw(body_text),
1736 }
1737 } else {
1738 FunctionBody::Raw(body_text)
1739 };
1740 Ok(Statement::CreateFunction(CreateFunctionStatement {
1741 name,
1742 or_replace,
1743 args,
1744 returns,
1745 language,
1746 body,
1747 }))
1748 }
1749
1750 fn parse_function_arg_list(&mut self) -> Result<Vec<FunctionArg>, ParseError> {
1754 let mut args: Vec<FunctionArg> = Vec::new();
1755 if matches!(self.peek(), Token::RParen) {
1756 self.advance();
1757 return Ok(args);
1758 }
1759 loop {
1760 let mode = if matches!(self.peek(), Token::In) {
1763 self.advance();
1764 FunctionArgMode::In
1765 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("out"))
1766 {
1767 self.advance();
1768 FunctionArgMode::Out
1769 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("inout"))
1770 {
1771 self.advance();
1772 FunctionArgMode::InOut
1773 } else {
1774 FunctionArgMode::In
1775 };
1776 let (name, ty_token) = {
1782 let first = self.expect_ident_like()?;
1783 match self.peek() {
1786 Token::Ident(_) | Token::QuotedIdent(_) => {
1787 let ty = self.expect_ident_like()?;
1788 (Some(first), ty)
1789 }
1790 _ => (None, first),
1791 }
1792 };
1793 let ty = match map_type_ident_to_column_type_name(&ty_token) {
1795 Some(t) => FunctionArgType::Typed(t),
1796 None => FunctionArgType::Raw(ty_token),
1797 };
1798 args.push(FunctionArg { mode, name, ty });
1799 match self.peek() {
1800 Token::Comma => {
1801 self.advance();
1802 continue;
1803 }
1804 Token::RParen => {
1805 self.advance();
1806 return Ok(args);
1807 }
1808 other => {
1809 return Err(self.err(alloc::format!(
1810 "expected , or ) in function arg list, got {other:?}"
1811 )));
1812 }
1813 }
1814 }
1815 }
1816
1817 fn parse_function_return(&mut self) -> Result<FunctionReturn, ParseError> {
1818 let ident = self.expect_ident_like()?;
1819 if ident.eq_ignore_ascii_case("trigger") {
1820 return Ok(FunctionReturn::Trigger);
1821 }
1822 if ident.eq_ignore_ascii_case("void") {
1823 return Ok(FunctionReturn::Void);
1824 }
1825 match map_type_ident_to_column_type_name(&ident) {
1826 Some(t) => Ok(FunctionReturn::Type(t)),
1827 None => Ok(FunctionReturn::Other(ident)),
1828 }
1829 }
1830
1831 fn parse_optional_language(&mut self) -> Result<Option<String>, ParseError> {
1832 match self.peek() {
1833 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("language") => {
1834 self.advance();
1835 let lang = self.expect_ident_like()?;
1836 Ok(Some(lang.to_ascii_lowercase()))
1837 }
1838 _ => Ok(None),
1839 }
1840 }
1841
1842 fn parse_create_domain_after_keyword(&mut self) -> Result<Statement, ParseError> {
1848 let name = self.expect_ident_like()?;
1849 if matches!(self.peek(), Token::As) {
1851 self.advance();
1852 }
1853 let base_type = self.parse_column_type_name()?;
1854 let mut default: Option<Expr> = None;
1855 let mut not_null = false;
1856 let mut checks: Vec<Expr> = Vec::new();
1857 loop {
1858 match self.peek() {
1859 Token::Default => {
1860 if default.is_some() {
1861 return Err(self.err("DOMAIN DEFAULT specified twice".into()));
1862 }
1863 self.advance();
1864 default = Some(self.parse_expr(0)?);
1865 }
1866 Token::Not => {
1867 self.advance();
1868 if !matches!(self.peek(), Token::Null) {
1869 return Err(self.err(alloc::format!(
1870 "expected NULL after NOT in DOMAIN, got {:?}",
1871 self.peek()
1872 )));
1873 }
1874 self.advance();
1875 not_null = true;
1876 }
1877 Token::Null => {
1878 self.advance();
1879 }
1883 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("check") => {
1884 self.advance();
1885 if !matches!(self.peek(), Token::LParen) {
1886 return Err(self.err(alloc::format!(
1887 "expected '(' after CHECK in DOMAIN, got {:?}",
1888 self.peek()
1889 )));
1890 }
1891 self.advance();
1892 let expr = self.parse_expr(0)?;
1893 if !matches!(self.peek(), Token::RParen) {
1894 return Err(self.err(alloc::format!(
1895 "expected ')' after CHECK expr, got {:?}",
1896 self.peek()
1897 )));
1898 }
1899 self.advance();
1900 checks.push(expr);
1901 }
1902 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("constraint") => {
1906 self.advance();
1907 let _ = self.expect_ident_like()?;
1908 }
1909 _ => break,
1910 }
1911 }
1912 Ok(Statement::CreateDomain(crate::ast::CreateDomainStatement {
1913 name,
1914 base_type,
1915 default,
1916 not_null,
1917 checks,
1918 }))
1919 }
1920
1921 fn parse_create_type_after_keyword(&mut self) -> Result<Statement, ParseError> {
1925 let name = self.expect_ident_like()?;
1926 if !matches!(self.peek(), Token::As) {
1928 return Err(self.err(alloc::format!(
1929 "expected AS after CREATE TYPE {name:?}, got {:?}",
1930 self.peek()
1931 )));
1932 }
1933 self.advance();
1934 if matches!(self.peek(), Token::LParen) {
1938 self.advance();
1939 let mut fields: Vec<(String, ColumnTypeName)> = Vec::new();
1940 loop {
1941 let field_name = self.expect_ident_like()?;
1942 let field_type = self.parse_column_type_name()?;
1943 fields.push((field_name, field_type));
1944 if matches!(self.peek(), Token::Comma) {
1945 self.advance();
1946 continue;
1947 }
1948 if matches!(self.peek(), Token::RParen) {
1949 self.advance();
1950 break;
1951 }
1952 return Err(self.err(alloc::format!(
1953 "expected , or ) in composite field list, got {:?}",
1954 self.peek()
1955 )));
1956 }
1957 if fields.is_empty() {
1958 return Err(self.err("CREATE TYPE … AS (…) must declare at least one field".into()));
1959 }
1960 return Ok(Statement::CreateType(crate::ast::CreateTypeStatement {
1961 name,
1962 kind: crate::ast::TypeKind::Composite { fields },
1963 }));
1964 }
1965 let kind_ident = match self.peek().clone() {
1967 Token::Ident(s) | Token::QuotedIdent(s) => s,
1968 other => {
1969 return Err(self.err(alloc::format!(
1970 "expected ENUM or '(' after CREATE TYPE {name:?} AS, got {other:?}"
1971 )));
1972 }
1973 };
1974 if !kind_ident.eq_ignore_ascii_case("enum") {
1975 return Err(self.err(alloc::format!(
1976 "Phase 1.4 only supports ENUM or composite '(…)'; got {kind_ident:?}"
1977 )));
1978 }
1979 self.advance();
1980 if !matches!(self.peek(), Token::LParen) {
1981 return Err(self.err(alloc::format!(
1982 "expected '(' after ENUM, got {:?}",
1983 self.peek()
1984 )));
1985 }
1986 self.advance();
1987 let mut labels: Vec<String> = Vec::new();
1988 loop {
1989 match self.peek().clone() {
1990 Token::String(s) => {
1991 self.advance();
1992 labels.push(s);
1993 }
1994 other => {
1995 return Err(
1996 self.err(alloc::format!("expected enum label string, got {other:?}"))
1997 );
1998 }
1999 }
2000 if matches!(self.peek(), Token::Comma) {
2001 self.advance();
2002 continue;
2003 }
2004 if matches!(self.peek(), Token::RParen) {
2005 self.advance();
2006 break;
2007 }
2008 return Err(self.err(alloc::format!(
2009 "expected , or ) in ENUM label list, got {:?}",
2010 self.peek()
2011 )));
2012 }
2013 if labels.is_empty() {
2014 return Err(self.err("CREATE TYPE … AS ENUM must declare at least one label".into()));
2015 }
2016 Ok(Statement::CreateType(crate::ast::CreateTypeStatement {
2017 name,
2018 kind: crate::ast::TypeKind::Enum { labels },
2019 }))
2020 }
2021
2022 fn parse_create_materialized_view_after_keyword(&mut self) -> Result<Statement, ParseError> {
2027 let if_not_exists = self.parse_if_not_exists();
2028 let name = self.expect_ident_like()?;
2029 let mut columns: Vec<String> = Vec::new();
2030 if matches!(self.peek(), Token::LParen) {
2031 self.advance();
2032 loop {
2033 let c = self.expect_ident_like()?;
2034 columns.push(c);
2035 if matches!(self.peek(), Token::Comma) {
2036 self.advance();
2037 continue;
2038 }
2039 if matches!(self.peek(), Token::RParen) {
2040 self.advance();
2041 break;
2042 }
2043 return Err(self.err(alloc::format!(
2044 "expected , or ) in MATERIALIZED VIEW column list, got {:?}",
2045 self.peek()
2046 )));
2047 }
2048 }
2049 if !matches!(self.peek(), Token::As) {
2050 return Err(self.err(alloc::format!(
2051 "expected AS <SELECT …> after CREATE MATERIALIZED VIEW {name:?}, got {:?}",
2052 self.peek()
2053 )));
2054 }
2055 self.advance();
2056 let body_stmt = self.parse_select_stmt()?;
2057 let Statement::Select(body) = body_stmt else {
2058 return Err(self.err(alloc::format!(
2059 "CREATE MATERIALIZED VIEW body must be a SELECT, got {body_stmt:?}"
2060 )));
2061 };
2062 let with_data = self.parse_optional_with_data(true)?;
2064 Ok(Statement::CreateMaterializedView(
2065 crate::ast::CreateMaterializedViewStatement {
2066 name,
2067 if_not_exists,
2068 columns,
2069 body,
2070 with_data,
2071 },
2072 ))
2073 }
2074
2075 fn parse_optional_with_data(&mut self, default_when_absent: bool) -> Result<bool, ParseError> {
2080 let save = self.pos;
2081 let is_with = match self.peek() {
2083 Token::Ident(s) | Token::QuotedIdent(s) => s.eq_ignore_ascii_case("with"),
2084 _ => false,
2085 };
2086 if !is_with {
2087 return Ok(default_when_absent);
2088 }
2089 self.advance();
2090 let mut with_data = true;
2092 let is_no = match self.peek() {
2093 Token::Ident(s) | Token::QuotedIdent(s) => s.eq_ignore_ascii_case("no"),
2094 _ => false,
2095 };
2096 if is_no {
2097 self.advance();
2098 with_data = false;
2099 }
2100 let is_data = match self.peek() {
2102 Token::Ident(s) | Token::QuotedIdent(s) => s.eq_ignore_ascii_case("data"),
2103 _ => false,
2104 };
2105 if is_data {
2106 self.advance();
2107 Ok(with_data)
2108 } else {
2109 self.pos = save;
2112 Ok(default_when_absent)
2113 }
2114 }
2115
2116 fn parse_create_view_after_keyword(
2121 &mut self,
2122 or_replace: bool,
2123 _materialized_unused: bool,
2124 temporary: bool,
2125 ) -> Result<Statement, ParseError> {
2126 let if_not_exists = self.parse_if_not_exists();
2127 let name = self.expect_ident_like()?;
2128 let mut columns: Vec<String> = Vec::new();
2130 if matches!(self.peek(), Token::LParen) {
2131 self.advance();
2132 loop {
2133 let c = self.expect_ident_like()?;
2134 columns.push(c);
2135 if matches!(self.peek(), Token::Comma) {
2136 self.advance();
2137 continue;
2138 }
2139 if matches!(self.peek(), Token::RParen) {
2140 self.advance();
2141 break;
2142 }
2143 return Err(self.err(alloc::format!(
2144 "expected , or ) in VIEW column list, got {:?}",
2145 self.peek()
2146 )));
2147 }
2148 }
2149 if !matches!(self.peek(), Token::As) {
2151 return Err(self.err(alloc::format!(
2152 "expected AS <SELECT …> after CREATE VIEW {name:?}, got {:?}",
2153 self.peek()
2154 )));
2155 }
2156 self.advance();
2157 let body_stmt = self.parse_select_stmt()?;
2159 let Statement::Select(body) = body_stmt else {
2160 return Err(self.err(alloc::format!(
2161 "CREATE VIEW body must be a SELECT statement, got {body_stmt:?}"
2162 )));
2163 };
2164 Ok(Statement::CreateView(crate::ast::CreateViewStatement {
2165 name,
2166 or_replace,
2167 if_not_exists,
2168 temporary,
2169 columns,
2170 body,
2171 }))
2172 }
2173
2174 fn parse_create_sequence_after_keyword(
2178 &mut self,
2179 temporary: bool,
2180 ) -> Result<Statement, ParseError> {
2181 let if_not_exists = self.parse_if_not_exists();
2182 let name = self.expect_ident_like()?;
2183 let data_type = if matches!(self.peek(), Token::As) {
2185 self.advance();
2186 Some(self.parse_sequence_data_type()?)
2187 } else {
2188 None
2189 };
2190 let options = self.parse_sequence_options(false)?;
2191 Ok(Statement::CreateSequence(
2192 crate::ast::CreateSequenceStatement {
2193 name,
2194 if_not_exists,
2195 temporary,
2196 data_type,
2197 options,
2198 },
2199 ))
2200 }
2201
2202 fn parse_alter_sequence_after_keyword(&mut self) -> Result<Statement, ParseError> {
2205 let if_exists = self.parse_if_exists();
2206 let name = self.expect_ident_like()?;
2207 let options = self.parse_sequence_options(true)?;
2208 Ok(Statement::AlterSequence(
2209 crate::ast::AlterSequenceStatement {
2210 name,
2211 if_exists,
2212 options,
2213 },
2214 ))
2215 }
2216
2217 fn parse_sequence_data_type(&mut self) -> Result<crate::ast::SequenceDataType, ParseError> {
2218 let kw = self.expect_ident_like()?;
2219 match kw.to_ascii_lowercase().as_str() {
2220 "smallint" | "int2" => Ok(crate::ast::SequenceDataType::SmallInt),
2221 "integer" | "int" | "int4" => Ok(crate::ast::SequenceDataType::Int),
2222 "bigint" | "int8" => Ok(crate::ast::SequenceDataType::BigInt),
2223 other => Err(self.err(alloc::format!(
2224 "expected SMALLINT / INTEGER / BIGINT after SEQUENCE AS, got {other:?}"
2225 ))),
2226 }
2227 }
2228
2229 fn parse_sequence_options(
2230 &mut self,
2231 allow_restart: bool,
2232 ) -> Result<crate::ast::SequenceOptions, ParseError> {
2233 use crate::ast::{SeqBound, SequenceOptions, SequenceOwnedBy};
2234 let mut opts = SequenceOptions::default();
2235 #[allow(clippy::while_let_loop)]
2236 loop {
2237 let kw_lc = match self.peek() {
2240 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
2241 _ => break,
2242 };
2243 match kw_lc.as_str() {
2244 "increment" => {
2245 self.advance();
2246 if matches!(self.peek(), Token::By) {
2248 self.advance();
2249 }
2250 opts.increment = Some(self.expect_signed_int()?);
2251 }
2252 "minvalue" => {
2253 self.advance();
2254 opts.min_value = Some(SeqBound::Value(self.expect_signed_int()?));
2255 }
2256 "maxvalue" => {
2257 self.advance();
2258 opts.max_value = Some(SeqBound::Value(self.expect_signed_int()?));
2259 }
2260 "no" => {
2261 self.advance();
2262 let what = self.expect_ident_like()?;
2263 match what.to_ascii_lowercase().as_str() {
2264 "minvalue" => opts.min_value = Some(SeqBound::NoBound),
2265 "maxvalue" => opts.max_value = Some(SeqBound::NoBound),
2266 "cycle" => opts.cycle = Some(false),
2267 other => {
2268 return Err(self.err(alloc::format!(
2269 "expected MINVALUE / MAXVALUE / CYCLE after NO, got {other:?}"
2270 )));
2271 }
2272 }
2273 }
2274 "start" => {
2275 self.advance();
2276 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
2278 if s.eq_ignore_ascii_case("with"))
2279 {
2280 self.advance();
2281 }
2282 opts.start = Some(self.expect_signed_int()?);
2283 }
2284 "restart" if allow_restart => {
2285 self.advance();
2286 let mut with_val: Option<i64> = None;
2288 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
2289 if s.eq_ignore_ascii_case("with"))
2290 {
2291 self.advance();
2292 with_val = Some(self.expect_signed_int()?);
2293 } else if matches!(self.peek(), Token::Integer(_) | Token::Minus) {
2294 with_val = Some(self.expect_signed_int()?);
2295 }
2296 opts.restart = Some(with_val);
2297 }
2298 "cache" => {
2299 self.advance();
2300 opts.cache = Some(self.expect_signed_int()?);
2301 }
2302 "cycle" => {
2303 self.advance();
2304 opts.cycle = Some(true);
2305 }
2306 "owned" => {
2307 self.advance();
2308 match self.peek() {
2310 Token::By => {
2311 self.advance();
2312 }
2313 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("by") => {
2314 self.advance();
2315 }
2316 other => {
2317 return Err(
2318 self.err(alloc::format!("expected BY after OWNED, got {other:?}"))
2319 );
2320 }
2321 }
2322 let first = match self.advance() {
2326 Token::Ident(s) | Token::QuotedIdent(s) => s,
2327 other => {
2328 return Err(self.err(alloc::format!(
2329 "expected identifier or NONE after OWNED BY, got {other:?}"
2330 )));
2331 }
2332 };
2333 if first.eq_ignore_ascii_case("none") {
2334 opts.owned_by = Some(SequenceOwnedBy::None);
2335 } else if matches!(self.peek(), Token::Dot) {
2336 self.advance();
2337 let second = match self.advance() {
2338 Token::Ident(s) | Token::QuotedIdent(s) => s,
2339 other => {
2340 return Err(self.err(alloc::format!(
2341 "expected column name after OWNED BY {first}., got {other:?}"
2342 )));
2343 }
2344 };
2345 if matches!(self.peek(), Token::Dot) {
2354 self.advance();
2355 let third = match self.advance() {
2356 Token::Ident(s) | Token::QuotedIdent(s) => s,
2357 other => {
2358 return Err(self.err(alloc::format!(
2359 "expected column name after OWNED BY {first}.{second}., got {other:?}"
2360 )));
2361 }
2362 };
2363 let _ = first; opts.owned_by = Some(SequenceOwnedBy::Column {
2365 table: second,
2366 column: third,
2367 });
2368 } else {
2369 opts.owned_by = Some(SequenceOwnedBy::Column {
2370 table: first,
2371 column: second,
2372 });
2373 }
2374 } else {
2375 return Err(self.err(alloc::format!(
2376 "expected table.column or NONE after OWNED BY, got {first:?}"
2377 )));
2378 }
2379 }
2380 _ => break,
2381 }
2382 }
2383 Ok(opts)
2384 }
2385
2386 fn expect_signed_int(&mut self) -> Result<i64, ParseError> {
2387 let neg = if matches!(self.peek(), Token::Minus) {
2388 self.advance();
2389 true
2390 } else {
2391 false
2392 };
2393 match self.peek() {
2394 Token::Integer(n) => {
2395 let v = *n;
2396 self.advance();
2397 Ok(if neg { -v } else { v })
2398 }
2399 other => Err(self.err(alloc::format!("expected signed integer, got {other:?}"))),
2400 }
2401 }
2402
2403 fn consume_optional_deferrable_clauses(&mut self) -> Result<(), ParseError> {
2413 loop {
2414 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("deferrable")) {
2416 self.advance();
2417 self.consume_optional_initially_clause()?;
2418 continue;
2419 }
2420 if matches!(self.peek(), Token::Not) {
2422 let look = self.tokens.get(self.pos + 1);
2423 if matches!(look, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("deferrable")) {
2424 self.advance(); self.advance(); self.consume_optional_initially_clause()?;
2427 continue;
2428 }
2429 break;
2430 }
2431 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("initially")) {
2436 self.consume_optional_initially_clause()?;
2437 continue;
2438 }
2439 break;
2440 }
2441 Ok(())
2442 }
2443
2444 fn consume_optional_initially_clause(&mut self) -> Result<(), ParseError> {
2448 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("initially")) {
2449 return Ok(());
2450 }
2451 self.advance(); match self.advance() {
2453 Token::Ident(s)
2454 if s.eq_ignore_ascii_case("deferred") || s.eq_ignore_ascii_case("immediate") =>
2455 {
2456 Ok(())
2457 }
2458 other => Err(self.err(alloc::format!(
2459 "expected DEFERRED or IMMEDIATE after INITIALLY, got {other:?}"
2460 ))),
2461 }
2462 }
2463
2464 fn consume_mysql_routine_body(&mut self) {
2482 let mut depth: i32 = 0;
2487 let mut started = false;
2488 loop {
2489 match self.peek().clone() {
2490 Token::Begin => {
2491 self.advance();
2492 depth += 1;
2493 started = true;
2494 }
2495 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("end") => {
2496 self.advance();
2497 if started {
2498 depth -= 1;
2499 if depth <= 0 {
2500 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
2507 let is_inner_close = matches!(
2512 self.peek(),
2513 Token::Ident(s) | Token::QuotedIdent(s)
2514 if matches!(
2515 s.to_ascii_lowercase().as_str(),
2516 "if" | "loop" | "while" | "case" | "repeat"
2517 )
2518 );
2519 if is_inner_close {
2520 self.advance();
2521 depth += 1;
2522 continue;
2523 }
2524 }
2525 if matches!(self.peek(), Token::Semicolon) {
2527 self.advance();
2528 }
2529 return;
2530 }
2531 }
2532 }
2533 Token::Eof => return,
2534 _ => {
2535 self.advance();
2536 }
2537 }
2538 }
2539 }
2540
2541 fn consume_mysql_view_prefix(&mut self) -> Result<(), ParseError> {
2555 loop {
2556 match self.peek().clone() {
2557 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("algorithm") => {
2558 self.advance(); if matches!(self.peek(), Token::Eq) {
2562 self.advance();
2563 }
2564 if matches!(
2568 self.peek(),
2569 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
2570 ) {
2571 self.advance();
2572 }
2573 }
2574 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("definer") => {
2575 self.advance(); if matches!(self.peek(), Token::Eq) {
2577 self.advance();
2578 }
2579 match self.peek().clone() {
2582 Token::String(_) | Token::Ident(_) | Token::QuotedIdent(_) => {
2583 self.advance();
2584 if matches!(self.peek(), Token::At) {
2586 self.advance();
2587 if matches!(
2588 self.peek(),
2589 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
2590 ) {
2591 self.advance();
2592 }
2593 }
2594 }
2595 _ => {}
2596 }
2597 }
2598 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sql") => {
2599 let save = self.pos;
2604 self.advance(); if matches!(self.peek(), Token::Ident(s2) | Token::QuotedIdent(s2)
2606 if s2.eq_ignore_ascii_case("security"))
2607 {
2608 self.advance(); if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
2611 self.advance();
2612 }
2613 } else {
2614 self.pos = save;
2617 return Ok(());
2618 }
2619 }
2620 _ => return Ok(()),
2621 }
2622 }
2623 }
2624
2625 fn parse_if_not_exists(&mut self) -> bool {
2626 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("if"))
2627 {
2628 let save = self.pos;
2629 self.advance();
2630 if matches!(self.peek(), Token::Not) {
2631 self.advance();
2632 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists"))
2633 {
2634 self.advance();
2635 return true;
2636 }
2637 }
2638 self.pos = save;
2639 }
2640 false
2641 }
2642
2643 fn parse_if_exists(&mut self) -> bool {
2644 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("if"))
2645 {
2646 let save = self.pos;
2647 self.advance();
2648 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists"))
2649 {
2650 self.advance();
2651 return true;
2652 }
2653 self.pos = save;
2654 }
2655 false
2656 }
2657
2658 fn parse_create_trigger_after_keyword(
2662 &mut self,
2663 or_replace: bool,
2664 ) -> Result<Statement, ParseError> {
2665 let name = self.expect_ident_like()?;
2666 let timing = {
2667 let ident = self.expect_ident_like()?;
2668 if ident.eq_ignore_ascii_case("before") {
2669 TriggerTiming::Before
2670 } else if ident.eq_ignore_ascii_case("after") {
2671 TriggerTiming::After
2672 } else if ident.eq_ignore_ascii_case("instead") {
2673 let next = self.expect_ident_like()?;
2674 if !next.eq_ignore_ascii_case("of") {
2675 return Err(self.err(alloc::format!(
2676 "expected OF after INSTEAD in trigger timing, got {next:?}"
2677 )));
2678 }
2679 TriggerTiming::InsteadOf
2680 } else {
2681 return Err(self.err(alloc::format!(
2682 "expected BEFORE / AFTER / INSTEAD OF in trigger timing, got {ident:?}"
2683 )));
2684 }
2685 };
2686 let mut events: Vec<TriggerEvent> = Vec::new();
2693 let mut update_columns: Vec<String> = Vec::new();
2694 let (first_ev, first_cols) = self.parse_trigger_event_with_optional_of()?;
2695 events.push(first_ev);
2696 if !first_cols.is_empty() {
2697 update_columns = first_cols;
2698 }
2699 while matches!(self.peek(), Token::Or) {
2700 self.advance();
2701 let (ev, cols) = self.parse_trigger_event_with_optional_of()?;
2702 events.push(ev);
2703 if !cols.is_empty() {
2704 if !update_columns.is_empty() {
2705 return Err(
2706 self.err("CREATE TRIGGER: `UPDATE OF cols` may appear at most once".into())
2707 );
2708 }
2709 update_columns = cols;
2710 }
2711 }
2712 let tok = self.peek();
2714 let Token::On = tok else {
2715 return Err(self.err(alloc::format!(
2716 "expected ON after trigger events, got {tok:?}"
2717 )));
2718 };
2719 self.advance();
2720 let table = self.expect_ident_like()?;
2721 if !matches!(self.peek(), Token::For) {
2725 return Err(self.err(alloc::format!(
2726 "expected FOR EACH ROW / STATEMENT, got {:?}",
2727 self.peek()
2728 )));
2729 }
2730 self.advance();
2731 let for_each = {
2732 let e = self.expect_ident_like()?;
2733 if !e.eq_ignore_ascii_case("each") {
2734 return Err(self.err(alloc::format!("expected EACH after FOR, got {e:?}")));
2735 }
2736 let unit = self.expect_ident_like()?;
2737 if unit.eq_ignore_ascii_case("row") {
2738 TriggerForEach::Row
2739 } else if unit.eq_ignore_ascii_case("statement") {
2740 TriggerForEach::Statement
2741 } else {
2742 return Err(self.err(alloc::format!(
2743 "expected ROW / STATEMENT after FOR EACH, got {unit:?}"
2744 )));
2745 }
2746 };
2747 let exec = self.expect_ident_like()?;
2749 if !exec.eq_ignore_ascii_case("execute") {
2750 return Err(self.err(alloc::format!(
2751 "expected EXECUTE FUNCTION/PROCEDURE in CREATE TRIGGER, got {exec:?}"
2752 )));
2753 }
2754 let fn_or_proc = self.expect_ident_like()?;
2755 if !(fn_or_proc.eq_ignore_ascii_case("function")
2756 || fn_or_proc.eq_ignore_ascii_case("procedure"))
2757 {
2758 return Err(self.err(alloc::format!(
2759 "expected FUNCTION / PROCEDURE after EXECUTE, got {fn_or_proc:?}"
2760 )));
2761 }
2762 let function = self.expect_ident_like()?;
2763 if matches!(self.peek(), Token::LParen) {
2765 self.advance();
2766 if !matches!(self.peek(), Token::RParen) {
2767 return Err(self.err(alloc::format!(
2768 "v7.12.4 trigger function calls take no args; got {:?}",
2769 self.peek()
2770 )));
2771 }
2772 self.advance();
2773 }
2774 Ok(Statement::CreateTrigger(CreateTriggerStatement {
2775 name,
2776 or_replace,
2777 timing,
2778 events,
2779 table,
2780 for_each,
2781 function,
2782 update_columns,
2783 }))
2784 }
2785
2786 fn parse_trigger_event_with_optional_of(
2790 &mut self,
2791 ) -> Result<(TriggerEvent, Vec<String>), ParseError> {
2792 let ev = self.parse_trigger_event()?;
2793 if !matches!(ev, TriggerEvent::Update) {
2794 return Ok((ev, Vec::new()));
2795 }
2796 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("of")) {
2798 return Ok((ev, Vec::new()));
2799 }
2800 self.advance(); let mut cols: Vec<String> = Vec::new();
2802 loop {
2803 cols.push(self.expect_ident_like()?);
2804 if matches!(self.peek(), Token::Comma) {
2805 self.advance();
2806 continue;
2807 }
2808 break;
2809 }
2810 if cols.is_empty() {
2811 return Err(
2812 self.err("CREATE TRIGGER: `UPDATE OF` requires at least one column name".into())
2813 );
2814 }
2815 Ok((ev, cols))
2816 }
2817
2818 pub(crate) fn parse_plpgsql_block(&mut self) -> Result<PlPgSqlBlock, ParseError> {
2825 let declarations = if matches!(
2827 self.peek(),
2828 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("declare")
2829 ) {
2830 self.advance();
2831 self.parse_plpgsql_declare_block()?
2832 } else {
2833 Vec::new()
2834 };
2835 if !matches!(self.peek(), Token::Begin) {
2840 return Err(self.err(alloc::format!(
2841 "expected BEGIN at start of plpgsql block, got {:?}",
2842 self.peek()
2843 )));
2844 }
2845 self.advance();
2846 let statements = self.parse_plpgsql_stmt_list_until_end()?;
2847 Ok(PlPgSqlBlock {
2848 declarations,
2849 statements,
2850 })
2851 }
2852
2853 fn parse_plpgsql_declare_block(&mut self) -> Result<Vec<PlPgSqlDeclare>, ParseError> {
2857 let mut out: Vec<PlPgSqlDeclare> = Vec::new();
2858 loop {
2859 if matches!(self.peek(), Token::Begin) {
2860 return Ok(out);
2861 }
2862 let name = self.expect_ident_like()?;
2863 let ty_token = self.expect_ident_like()?;
2864 let ty = match map_type_ident_to_column_type_name(&ty_token) {
2865 Some(t) => FunctionArgType::Typed(t),
2866 None => FunctionArgType::Raw(ty_token),
2867 };
2868 let default = match self.peek() {
2869 Token::ColonEq => {
2870 self.advance();
2871 Some(self.parse_expr(0)?)
2872 }
2873 Token::Eq => {
2874 self.advance();
2878 Some(self.parse_expr(0)?)
2879 }
2880 _ => None,
2881 };
2882 if !matches!(self.peek(), Token::Semicolon) {
2884 return Err(self.err(alloc::format!(
2885 "expected ; after DECLARE entry for {name:?}, got {:?}",
2886 self.peek()
2887 )));
2888 }
2889 self.advance();
2890 out.push(PlPgSqlDeclare { name, ty, default });
2891 }
2892 }
2893
2894 fn parse_plpgsql_stmt_list_until_end(&mut self) -> Result<Vec<PlPgSqlStmt>, ParseError> {
2899 let mut statements: Vec<PlPgSqlStmt> = Vec::new();
2900 loop {
2901 while matches!(self.peek(), Token::Semicolon) {
2903 self.advance();
2904 }
2905 if matches!(
2907 self.peek(),
2908 Token::Ident(s) | Token::QuotedIdent(s)
2909 if s.eq_ignore_ascii_case("end")
2910 || s.eq_ignore_ascii_case("else")
2911 || s.eq_ignore_ascii_case("elsif")
2912 || s.eq_ignore_ascii_case("elseif")
2913 ) {
2914 return Ok(statements);
2915 }
2916 let stmt = self.parse_plpgsql_stmt()?;
2919 statements.push(stmt);
2920 match self.peek() {
2921 Token::Semicolon => {
2922 self.advance();
2923 }
2924 Token::Ident(s) | Token::QuotedIdent(s)
2925 if s.eq_ignore_ascii_case("end")
2926 || s.eq_ignore_ascii_case("else")
2927 || s.eq_ignore_ascii_case("elsif")
2928 || s.eq_ignore_ascii_case("elseif") =>
2929 {
2930 }
2932 other => {
2933 return Err(self.err(alloc::format!(
2934 "expected ; or END/ELSE/ELSIF after plpgsql statement, got {other:?}"
2935 )));
2936 }
2937 }
2938 }
2939 }
2940
2941 fn parse_plpgsql_stmt(&mut self) -> Result<PlPgSqlStmt, ParseError> {
2942 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("return"))
2944 {
2945 self.advance();
2946 return self.parse_plpgsql_return();
2947 }
2948 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("if"))
2950 {
2951 self.advance();
2952 return self.parse_plpgsql_if();
2953 }
2954 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("raise"))
2956 {
2957 self.advance();
2958 return self.parse_plpgsql_raise();
2959 }
2960 if matches!(self.peek(), Token::Select)
2971 && let Some((select_body, var_name)) = self.try_parse_plpgsql_select_into()?
2972 {
2973 return Ok(PlPgSqlStmt::SelectInto {
2974 var: var_name,
2975 body: Box::new(select_body),
2976 });
2977 }
2978 if matches!(self.peek(), Token::Insert)
2988 || matches!(self.peek(), Token::Select)
2989 || matches!(self.peek(), Token::Create)
2990 || matches!(self.peek(), Token::Drop)
2991 || matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
2992 if s.eq_ignore_ascii_case("update")
2993 || s.eq_ignore_ascii_case("delete")
2994 || s.eq_ignore_ascii_case("alter"))
2995 {
2996 let stmt = self.parse_one_statement()?;
2997 return Ok(PlPgSqlStmt::EmbeddedSql(Box::new(stmt)));
2998 }
2999 let target = self.parse_plpgsql_assign_target()?;
3002 match self.peek() {
3005 Token::ColonEq => {
3006 self.advance();
3007 }
3008 Token::Colon => {
3009 self.advance();
3010 if !matches!(self.peek(), Token::Eq) {
3011 return Err(self.err(alloc::format!(
3012 "expected := after plpgsql assign target, got `:` then {:?}",
3013 self.peek()
3014 )));
3015 }
3016 self.advance();
3017 }
3018 other => {
3019 return Err(self.err(alloc::format!(
3020 "expected := after plpgsql assign target, got {other:?}"
3021 )));
3022 }
3023 }
3024 let value = self.parse_expr(0)?;
3025 Ok(PlPgSqlStmt::Assign { target, value })
3026 }
3027
3028 fn parse_plpgsql_if(&mut self) -> Result<PlPgSqlStmt, ParseError> {
3031 let mut branches: Vec<(Expr, Vec<PlPgSqlStmt>)> = Vec::new();
3032 let mut else_branch: Vec<PlPgSqlStmt> = Vec::new();
3033 loop {
3034 let cond = self.parse_expr(0)?;
3036 let then_kw = self.expect_ident_like()?;
3037 if !then_kw.eq_ignore_ascii_case("then") {
3038 return Err(self.err(alloc::format!(
3039 "expected THEN after IF/ELSIF condition, got {then_kw:?}"
3040 )));
3041 }
3042 let body = self.parse_plpgsql_stmt_list_until_end()?;
3043 branches.push((cond, body));
3044 match self.peek() {
3046 Token::Ident(s) | Token::QuotedIdent(s)
3047 if s.eq_ignore_ascii_case("elsif") || s.eq_ignore_ascii_case("elseif") =>
3048 {
3049 self.advance();
3050 continue;
3051 }
3052 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("else") => {
3053 self.advance();
3054 else_branch = self.parse_plpgsql_stmt_list_until_end()?;
3055 break;
3056 }
3057 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("end") => {
3058 break;
3059 }
3060 other => {
3061 return Err(self.err(alloc::format!(
3062 "expected ELSIF / ELSE / END after IF branch body, got {other:?}"
3063 )));
3064 }
3065 }
3066 }
3067 let end_kw = self.expect_ident_like()?;
3070 if !end_kw.eq_ignore_ascii_case("end") {
3071 return Err(self.err(alloc::format!("expected END IF, got {end_kw:?}")));
3072 }
3073 let if_kw = self.expect_ident_like()?;
3074 if !if_kw.eq_ignore_ascii_case("if") {
3075 return Err(self.err(alloc::format!("expected END IF, got END {if_kw:?}")));
3076 }
3077 Ok(PlPgSqlStmt::If {
3078 branches,
3079 else_branch,
3080 })
3081 }
3082
3083 fn parse_plpgsql_raise(&mut self) -> Result<PlPgSqlStmt, ParseError> {
3087 let lvl_ident = self.expect_ident_like()?;
3088 let level = match lvl_ident.to_ascii_lowercase().as_str() {
3089 "notice" => RaiseLevel::Notice,
3090 "warning" => RaiseLevel::Warning,
3091 "info" => RaiseLevel::Info,
3092 "log" => RaiseLevel::Log,
3093 "debug" => RaiseLevel::Debug,
3094 "exception" => RaiseLevel::Exception,
3095 other => {
3096 return Err(self.err(alloc::format!(
3097 "expected RAISE level (NOTICE/WARNING/INFO/LOG/DEBUG/EXCEPTION), got {other:?}"
3098 )));
3099 }
3100 };
3101 let Token::String(msg) = self.peek() else {
3105 return Err(self.err(alloc::format!(
3106 "expected RAISE message string, got {:?}",
3107 self.peek()
3108 )));
3109 };
3110 let message = msg.clone();
3111 self.advance();
3112 let mut args: Vec<Expr> = Vec::new();
3114 while matches!(self.peek(), Token::Comma) {
3115 self.advance();
3116 args.push(self.parse_expr(0)?);
3117 }
3118 Ok(PlPgSqlStmt::Raise {
3119 level,
3120 message,
3121 args,
3122 })
3123 }
3124
3125 #[allow(clippy::too_many_lines)]
3133 fn try_parse_plpgsql_select_into(
3134 &mut self,
3135 ) -> Result<Option<(SelectStatement, String)>, ParseError> {
3136 let start = self.pos;
3141 let mut into_pos: Option<usize> = None;
3142 let mut depth: i32 = 0;
3143 let mut i = start + 1;
3144 while i < self.tokens.len() {
3145 match &self.tokens[i] {
3146 Token::LParen => depth += 1,
3147 Token::RParen => depth -= 1,
3148 Token::Semicolon if depth == 0 => break,
3149 Token::Ident(s)
3150 if depth == 0
3151 && (s.eq_ignore_ascii_case("end")
3152 || s.eq_ignore_ascii_case("else")
3153 || s.eq_ignore_ascii_case("elsif")) =>
3154 {
3155 break;
3156 }
3157 Token::Into if depth == 0 => {
3158 into_pos = Some(i);
3159 break;
3160 }
3161 _ => {}
3162 }
3163 i += 1;
3164 }
3165 let Some(into_at) = into_pos else {
3166 return Ok(None);
3167 };
3168 let var = match self.tokens.get(into_at + 1) {
3172 Some(Token::Ident(s) | Token::QuotedIdent(s)) => s.clone(),
3173 other => {
3174 return Err(self.err(alloc::format!(
3175 "expected variable name after SELECT … INTO, got {other:?}"
3176 )));
3177 }
3178 };
3179 let mut end = into_at + 2;
3182 let mut depth2: i32 = 0;
3183 while end < self.tokens.len() {
3184 match &self.tokens[end] {
3185 Token::LParen => depth2 += 1,
3186 Token::RParen => depth2 -= 1,
3187 Token::Semicolon if depth2 == 0 => break,
3188 Token::Ident(s)
3189 if depth2 == 0
3190 && (s.eq_ignore_ascii_case("end")
3191 || s.eq_ignore_ascii_case("else")
3192 || s.eq_ignore_ascii_case("elsif")) =>
3193 {
3194 break;
3195 }
3196 _ => {}
3197 }
3198 end += 1;
3199 }
3200 let mut rebuilt: Vec<Token> = Vec::with_capacity(end - start);
3205 for j in start..into_at {
3206 rebuilt.push(self.tokens[j].clone());
3207 }
3208 for j in (into_at + 2)..end {
3209 rebuilt.push(self.tokens[j].clone());
3210 }
3211 rebuilt.push(Token::Eof);
3212 let saved_pos = self.pos;
3213 let saved_tokens = core::mem::replace(&mut self.tokens, rebuilt);
3214 self.pos = 0;
3215 if !matches!(self.peek(), Token::Select) {
3217 self.tokens = saved_tokens;
3218 self.pos = saved_pos;
3219 return Err(self.err("plpgsql SELECT … INTO: rebuilt stream missing SELECT".into()));
3220 }
3221 let sel = self.parse_select_stmt();
3222 self.tokens = saved_tokens;
3223 self.pos = end;
3224 let sel = sel?;
3225 let Statement::Select(body) = sel else {
3226 return Err(self.err(alloc::format!(
3227 "plpgsql SELECT … INTO: rebuilt SELECT did not produce a Select node, got {sel:?}"
3228 )));
3229 };
3230 Ok(Some((body, var)))
3231 }
3232
3233 fn parse_plpgsql_assign_target(&mut self) -> Result<AssignTarget, ParseError> {
3234 let head = match self.advance() {
3248 Token::Ident(s) | Token::QuotedIdent(s) => s,
3249 other => {
3250 return Err(self.err(alloc::format!(
3251 "expected NEW / OLD / <local_var> as plpgsql assign target, got {other:?}"
3252 )));
3253 }
3254 };
3255 if matches!(self.peek(), Token::Dot) {
3256 self.advance();
3257 let col = self.expect_ident_like()?;
3258 if head.eq_ignore_ascii_case("new") {
3259 return Ok(AssignTarget::NewColumn(col));
3260 }
3261 if head.eq_ignore_ascii_case("old") {
3262 return Ok(AssignTarget::OldColumn(col));
3263 }
3264 return Err(self.err(alloc::format!(
3265 "plpgsql assign target must be NEW.<col> / OLD.<col> / <local_var>; \
3266 got {head:?}.<col>"
3267 )));
3268 }
3269 Ok(AssignTarget::Local(head))
3270 }
3271
3272 fn parse_plpgsql_return(&mut self) -> Result<PlPgSqlStmt, ParseError> {
3273 match self.peek() {
3275 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("new") => {
3276 self.advance();
3277 return Ok(PlPgSqlStmt::Return(ReturnTarget::New));
3278 }
3279 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("old") => {
3280 self.advance();
3281 return Ok(PlPgSqlStmt::Return(ReturnTarget::Old));
3282 }
3283 Token::Null => {
3284 self.advance();
3285 return Ok(PlPgSqlStmt::Return(ReturnTarget::Null));
3286 }
3287 Token::Semicolon => {
3290 return Ok(PlPgSqlStmt::Return(ReturnTarget::Null));
3291 }
3292 _ => {}
3293 }
3294 let e = self.parse_expr(0)?;
3296 Ok(PlPgSqlStmt::Return(ReturnTarget::Expr(e)))
3297 }
3298
3299 fn parse_trigger_event(&mut self) -> Result<TriggerEvent, ParseError> {
3300 if matches!(self.peek(), Token::Insert) {
3305 self.advance();
3306 return Ok(TriggerEvent::Insert);
3307 }
3308 match self.peek() {
3309 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
3310 self.advance();
3311 Ok(TriggerEvent::Update)
3312 }
3313 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
3314 self.advance();
3315 Ok(TriggerEvent::Delete)
3316 }
3317 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("truncate") => {
3318 self.advance();
3319 Ok(TriggerEvent::Truncate)
3320 }
3321 other => Err(self.err(alloc::format!(
3322 "expected INSERT / UPDATE / DELETE / TRUNCATE in trigger event list, got {other:?}"
3323 ))),
3324 }
3325 }
3326
3327 fn parse_create_publication_after_keyword(&mut self) -> Result<Statement, ParseError> {
3334 let name = self.expect_ident_or_string()?;
3335 let scope = if matches!(self.peek(), Token::For) {
3338 self.advance();
3339 if matches!(self.peek(), Token::All) {
3340 self.advance();
3341 if !matches!(self.peek(), Token::Tables) {
3342 return Err(self.err(format!(
3343 "expected TABLES after FOR ALL, got {:?}",
3344 self.peek()
3345 )));
3346 }
3347 self.advance();
3348 if matches!(self.peek(), Token::Except) {
3349 self.advance();
3350 let tables = self.parse_publication_table_list()?;
3351 PublicationScope::AllTablesExcept(tables)
3352 } else {
3353 PublicationScope::AllTables
3354 }
3355 } else if matches!(self.peek(), Token::Table | Token::Tables) {
3356 self.advance();
3359 let tables = self.parse_publication_table_list()?;
3360 PublicationScope::ForTables(tables)
3361 } else {
3362 return Err(self.err(format!(
3363 "expected ALL TABLES or TABLE <list> after FOR, got {:?}",
3364 self.peek()
3365 )));
3366 }
3367 } else {
3368 PublicationScope::AllTables
3369 };
3370 Ok(Statement::CreatePublication(CreatePublicationStatement {
3371 name,
3372 scope,
3373 }))
3374 }
3375
3376 fn parse_publication_table_list(&mut self) -> Result<Vec<String>, ParseError> {
3381 let first = self.expect_ident_like()?;
3382 let mut out = alloc::vec![first];
3383 while matches!(self.peek(), Token::Comma) {
3384 self.advance();
3385 out.push(self.expect_ident_like()?);
3386 }
3387 Ok(out)
3388 }
3389
3390 fn parse_create_subscription_after_keyword(&mut self) -> Result<Statement, ParseError> {
3398 let name = self.expect_ident_or_string()?;
3399 if !matches!(self.peek(), Token::Connection) {
3400 return Err(self.err(format!(
3401 "expected CONNECTION after CREATE SUBSCRIPTION <name>, got {:?}",
3402 self.peek()
3403 )));
3404 }
3405 self.advance();
3406 let conn_str = self.expect_string_literal()?;
3407 if !matches!(self.peek(), Token::Publication) {
3408 return Err(self.err(format!(
3409 "expected PUBLICATION after CONNECTION '<conn>', got {:?}",
3410 self.peek()
3411 )));
3412 }
3413 self.advance();
3414 let first = self.expect_ident_like()?;
3417 let mut publications = alloc::vec![first];
3418 while matches!(self.peek(), Token::Comma) {
3419 self.advance();
3420 publications.push(self.expect_ident_like()?);
3421 }
3422 Ok(Statement::CreateSubscription(CreateSubscriptionStatement {
3423 name,
3424 conn_str,
3425 publications,
3426 }))
3427 }
3428
3429 fn parse_set_param_name(&mut self) -> Result<String, ParseError> {
3436 let mut name = self.expect_ident_like()?;
3437 while matches!(self.peek(), Token::Dot) {
3438 self.advance();
3439 let next = self.expect_ident_like()?;
3440 name.push('.');
3441 name.push_str(&next);
3442 }
3443 Ok(name.to_ascii_lowercase())
3444 }
3445
3446 fn parse_set_value(&mut self) -> Result<crate::ast::SetValue, ParseError> {
3447 match self.advance() {
3448 Token::String(s) => Ok(crate::ast::SetValue::String(s)),
3449 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("default") => {
3450 Ok(crate::ast::SetValue::Default)
3451 }
3452 Token::Ident(s) | Token::QuotedIdent(s) => {
3453 let mut accum = s;
3454 while matches!(self.peek(), Token::Dot) {
3455 self.advance();
3456 let next = self.expect_ident_like()?;
3457 accum.push('.');
3458 accum.push_str(&next);
3459 }
3460 Ok(crate::ast::SetValue::Ident(accum))
3461 }
3462 Token::Integer(n) => Ok(crate::ast::SetValue::Number(n.to_string())),
3463 Token::Float(f) => Ok(crate::ast::SetValue::Number(f.to_string())),
3464 Token::On => Ok(crate::ast::SetValue::Ident("on".to_string())),
3469 Token::True => Ok(crate::ast::SetValue::Ident("true".to_string())),
3470 Token::False => Ok(crate::ast::SetValue::Ident("false".to_string())),
3471 Token::SessionVar(s) => Ok(crate::ast::SetValue::Ident(s)),
3477 Token::Minus => match self.advance() {
3482 Token::Integer(n) => Ok(crate::ast::SetValue::Number(alloc::format!("-{n}"))),
3483 Token::Float(f) => Ok(crate::ast::SetValue::Number(alloc::format!("-{f}"))),
3484 other => Err(self.err(format!(
3485 "expected numeric after `-` in SET value, got {other:?}"
3486 ))),
3487 },
3488 other => Err(self.err(format!(
3489 "expected literal, identifier, or DEFAULT after `=` in SET, got {other:?}"
3490 ))),
3491 }
3492 }
3493
3494 fn parse_isolation_level_clauses(&mut self) -> Result<IsolationLevel, ParseError> {
3503 let mut level = IsolationLevel::default();
3504 let mut have_level = false;
3505 loop {
3506 let saw_isolation = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("isolation"));
3508 if saw_isolation {
3509 self.advance(); if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("level"))
3511 {
3512 return Err(self.err(alloc::format!(
3513 "expected LEVEL after ISOLATION, got {:?}",
3514 self.peek()
3515 )));
3516 }
3517 self.advance(); let w1 = self
3520 .expect_ident_like()
3521 .map_err(|e| self.err(alloc::format!("isolation level: {e:?}")))?;
3522 let lc = w1.to_ascii_lowercase();
3523 level = match lc.as_str() {
3524 "serializable" => IsolationLevel::Serializable,
3525 "repeatable" => {
3526 let w2 = self
3528 .expect_ident_like()
3529 .map_err(|e| self.err(alloc::format!("REPEATABLE …: {e:?}")))?;
3530 if !w2.eq_ignore_ascii_case("read") {
3531 return Err(self.err(alloc::format!(
3532 "expected READ after REPEATABLE, got {w2:?}"
3533 )));
3534 }
3535 IsolationLevel::RepeatableRead
3536 }
3537 "read" => {
3538 let w2 = self
3539 .expect_ident_like()
3540 .map_err(|e| self.err(alloc::format!("READ …: {e:?}")))?;
3541 match w2.to_ascii_lowercase().as_str() {
3542 "committed" => IsolationLevel::ReadCommitted,
3543 "uncommitted" => IsolationLevel::ReadUncommitted,
3544 other => {
3545 return Err(self.err(alloc::format!(
3546 "expected COMMITTED or UNCOMMITTED after READ, got {other:?}"
3547 )));
3548 }
3549 }
3550 }
3551 other => {
3552 return Err(self.err(alloc::format!(
3553 "unknown isolation level {other:?}"
3554 )));
3555 }
3556 };
3557 have_level = true;
3558 } else if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("read"))
3559 {
3560 self.advance();
3562 match self.peek().clone() {
3563 Token::Ident(s) if s.eq_ignore_ascii_case("only") => {
3564 self.advance();
3565 }
3566 Token::Ident(s) if s.eq_ignore_ascii_case("write") => {
3567 self.advance();
3568 }
3569 other => {
3570 return Err(self.err(alloc::format!(
3571 "expected ONLY or WRITE after READ, got {other:?}"
3572 )));
3573 }
3574 }
3575 } else if matches!(self.peek(), Token::Not) {
3576 self.advance();
3578 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("deferrable"))
3579 {
3580 return Err(self.err(alloc::format!(
3581 "expected DEFERRABLE after NOT, got {:?}",
3582 self.peek()
3583 )));
3584 }
3585 self.advance();
3586 } else if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("deferrable"))
3587 {
3588 self.advance();
3589 } else {
3590 break;
3591 }
3592 if matches!(self.peek(), Token::Comma) {
3594 self.advance();
3595 }
3596 }
3597 let _ = have_level;
3598 Ok(level)
3599 }
3600
3601 fn parse_wait_after_keyword(&mut self) -> Result<Statement, ParseError> {
3602 if !matches!(self.peek(), Token::For) {
3606 return Err(self.err(format!("expected FOR after WAIT, got {:?}", self.peek())));
3607 }
3608 self.advance();
3609 self.expect_keyword_ident("wal")?;
3610 self.expect_keyword_ident("position")?;
3611 let pos = self.expect_u64_literal()?;
3612 let timeout_ms = if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with"))
3613 {
3614 self.advance();
3615 self.expect_keyword_ident("timeout")?;
3616 Some(self.expect_u64_literal()?)
3617 } else {
3618 None
3619 };
3620 Ok(Statement::WaitForWalPosition { pos, timeout_ms })
3621 }
3622
3623 fn expect_u64_literal(&mut self) -> Result<u64, ParseError> {
3627 match self.advance() {
3628 Token::Integer(n) if n >= 0 => Ok(n as u64),
3629 Token::Integer(n) => Err(ParseError {
3630 message: format!("expected non-negative integer, got {n}"),
3631 token_pos: self.pos.saturating_sub(1),
3632 }),
3633 other => Err(ParseError {
3634 message: format!("expected integer literal, got {other:?}"),
3635 token_pos: self.pos.saturating_sub(1),
3636 }),
3637 }
3638 }
3639
3640 fn parse_create_user_after_keyword(&mut self) -> Result<Statement, ParseError> {
3644 let name = self.expect_ident_or_string()?;
3645 self.expect_keyword_ident("with")?;
3646 self.expect_keyword_ident("password")?;
3647 let password = self.expect_string_literal()?;
3648 let role = if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
3649 && s.eq_ignore_ascii_case("role")
3650 {
3651 self.advance();
3652 self.expect_string_literal()?
3653 } else {
3654 "readonly".to_string()
3655 };
3656 Ok(Statement::CreateUser(crate::ast::CreateUserStatement {
3657 name,
3658 password,
3659 role,
3660 }))
3661 }
3662
3663 fn parse_update_after_keyword(&mut self) -> Result<Statement, ParseError> {
3666 let table = self.expect_ident_like()?;
3667 self.expect_keyword_ident("set")?;
3668 let mut assignments = Vec::new();
3669 loop {
3670 let col = self.expect_ident_like()?;
3671 if !matches!(self.peek(), Token::Eq) {
3672 return Err(self.err(format!(
3673 "expected `=` after column name in UPDATE SET, got {:?}",
3674 self.peek()
3675 )));
3676 }
3677 self.advance();
3678 let value = self.parse_expr(0)?;
3679 assignments.push((col, value));
3680 if matches!(self.peek(), Token::Comma) {
3681 self.advance();
3682 continue;
3683 }
3684 break;
3685 }
3686 let where_ = if matches!(self.peek(), Token::Where) {
3687 self.advance();
3688 Some(self.parse_expr(0)?)
3689 } else {
3690 None
3691 };
3692 let returning = self.parse_optional_returning()?;
3693 Ok(Statement::Update(crate::ast::UpdateStatement {
3694 ctes: Vec::new(),
3695 table,
3696 assignments,
3697 where_,
3698 returning,
3699 }))
3700 }
3701
3702 fn parse_delete_after_keyword(&mut self) -> Result<Statement, ParseError> {
3705 if !matches!(self.peek(), Token::From) {
3706 return Err(self.err(format!("expected FROM after DELETE, got {:?}", self.peek())));
3707 }
3708 self.advance();
3709 let table = self.expect_ident_like()?;
3710 let where_ = if matches!(self.peek(), Token::Where) {
3711 self.advance();
3712 Some(self.parse_expr(0)?)
3713 } else {
3714 None
3715 };
3716 let returning = self.parse_optional_returning()?;
3717 Ok(Statement::Delete(crate::ast::DeleteStatement {
3718 ctes: Vec::new(),
3719 table,
3720 where_,
3721 returning,
3722 }))
3723 }
3724
3725 fn parse_merge_after_keyword(&mut self) -> Result<Statement, ParseError> {
3735 let is_into_kw = matches!(self.peek(), Token::Into)
3737 || matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("into"));
3738 if !is_into_kw {
3739 return Err(self.err(format!("expected INTO after MERGE, got {:?}", self.peek())));
3740 }
3741 self.advance();
3742 let target = self.expect_ident_like()?;
3743 let target_alias = match self.peek() {
3745 Token::Ident(s) | Token::QuotedIdent(s) if !s.eq_ignore_ascii_case("using") => {
3746 Some(self.expect_ident_like()?)
3747 }
3748 _ => None,
3749 };
3750 let is_using_kw = matches!(
3752 self.peek(),
3753 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("using")
3754 );
3755 if !is_using_kw {
3756 return Err(self.err(format!(
3757 "expected USING after MERGE INTO target, got {:?}",
3758 self.peek()
3759 )));
3760 }
3761 self.advance();
3762 let source = self.expect_ident_like()?;
3763 let source_alias = match self.peek() {
3764 Token::Ident(s) | Token::QuotedIdent(s) if !s.eq_ignore_ascii_case("on") => {
3765 Some(self.expect_ident_like()?)
3766 }
3767 _ => None,
3768 };
3769 if !matches!(self.peek(), Token::On) {
3771 return Err(self.err(format!(
3772 "expected ON after MERGE … USING source, got {:?}",
3773 self.peek()
3774 )));
3775 }
3776 self.advance();
3777 let on = self.parse_expr(0)?;
3778 let mut clauses: Vec<crate::ast::MergeWhenClause> = Vec::new();
3780 loop {
3781 let is_when_kw = matches!(
3782 self.peek(),
3783 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("when")
3784 );
3785 if !is_when_kw {
3786 break;
3787 }
3788 self.advance(); let matched = if matches!(self.peek(), Token::Not) {
3791 self.advance();
3792 crate::ast::MergeMatched::NotMatched
3793 } else {
3794 crate::ast::MergeMatched::Matched
3795 };
3796 let is_matched_kw = matches!(
3797 self.peek(),
3798 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("matched")
3799 );
3800 if !is_matched_kw {
3801 return Err(self.err(format!(
3802 "expected MATCHED in WHEN clause, got {:?}",
3803 self.peek()
3804 )));
3805 }
3806 self.advance();
3807 let condition = if matches!(self.peek(), Token::And) {
3809 self.advance();
3810 Some(self.parse_expr(0)?)
3811 } else {
3812 None
3813 };
3814 let is_then_kw = matches!(
3816 self.peek(),
3817 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("then")
3818 );
3819 if !is_then_kw {
3820 return Err(self.err(format!(
3821 "expected THEN in WHEN clause, got {:?}",
3822 self.peek()
3823 )));
3824 }
3825 self.advance();
3826 let action = match self.peek().clone() {
3828 Token::Insert => {
3829 self.advance();
3830 if !matches!(self.peek(), Token::LParen) {
3832 return Err(self.err(format!(
3833 "expected '(' after INSERT in MERGE, got {:?}",
3834 self.peek()
3835 )));
3836 }
3837 self.advance();
3838 let mut columns: Vec<String> = Vec::new();
3839 loop {
3840 columns.push(self.expect_ident_like()?);
3841 if matches!(self.peek(), Token::Comma) {
3842 self.advance();
3843 continue;
3844 }
3845 break;
3846 }
3847 if !matches!(self.peek(), Token::RParen) {
3848 return Err(self.err(format!(
3849 "expected ')' after INSERT column list, got {:?}",
3850 self.peek()
3851 )));
3852 }
3853 self.advance();
3854 if !matches!(self.peek(), Token::Values) {
3856 return Err(self.err(format!(
3857 "expected VALUES in MERGE INSERT, got {:?}",
3858 self.peek()
3859 )));
3860 }
3861 self.advance();
3862 if !matches!(self.peek(), Token::LParen) {
3863 return Err(self.err(format!(
3864 "expected '(' after VALUES in MERGE INSERT, got {:?}",
3865 self.peek()
3866 )));
3867 }
3868 self.advance();
3869 let mut values: Vec<crate::ast::Expr> = Vec::new();
3870 loop {
3871 values.push(self.parse_expr(0)?);
3872 if matches!(self.peek(), Token::Comma) {
3873 self.advance();
3874 continue;
3875 }
3876 break;
3877 }
3878 if !matches!(self.peek(), Token::RParen) {
3879 return Err(self.err(format!(
3880 "expected ')' after MERGE INSERT values, got {:?}",
3881 self.peek()
3882 )));
3883 }
3884 self.advance();
3885 if columns.len() != values.len() {
3886 return Err(self.err(format!(
3887 "MERGE INSERT column count ({}) ≠ value count ({})",
3888 columns.len(),
3889 values.len()
3890 )));
3891 }
3892 crate::ast::MergeAction::Insert { columns, values }
3893 }
3894 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
3895 self.advance();
3896 let is_set_kw = matches!(
3898 self.peek(),
3899 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set")
3900 );
3901 if !is_set_kw {
3902 return Err(self.err(format!(
3903 "expected SET after UPDATE in MERGE, got {:?}",
3904 self.peek()
3905 )));
3906 }
3907 self.advance();
3908 let mut assignments: Vec<(String, crate::ast::Expr)> = Vec::new();
3909 loop {
3910 let col = self.expect_ident_like()?;
3911 if !matches!(self.peek(), Token::Eq) {
3912 return Err(self.err(format!(
3913 "expected '=' in MERGE UPDATE assignment, got {:?}",
3914 self.peek()
3915 )));
3916 }
3917 self.advance();
3918 let expr = self.parse_expr(0)?;
3919 assignments.push((col, expr));
3920 if matches!(self.peek(), Token::Comma) {
3921 self.advance();
3922 continue;
3923 }
3924 break;
3925 }
3926 crate::ast::MergeAction::Update { assignments }
3927 }
3928 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
3929 self.advance();
3930 crate::ast::MergeAction::Delete
3931 }
3932 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {
3933 self.advance();
3934 let is_nothing_kw = matches!(
3935 self.peek(),
3936 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing")
3937 );
3938 if !is_nothing_kw {
3939 return Err(self.err(format!(
3940 "expected NOTHING after DO in MERGE clause, got {:?}",
3941 self.peek()
3942 )));
3943 }
3944 self.advance();
3945 crate::ast::MergeAction::DoNothing
3946 }
3947 other => {
3948 return Err(self.err(format!(
3949 "expected INSERT / UPDATE / DELETE / DO NOTHING in MERGE clause, got {other:?}"
3950 )));
3951 }
3952 };
3953 clauses.push(crate::ast::MergeWhenClause {
3954 matched,
3955 condition,
3956 action,
3957 });
3958 }
3959 if clauses.is_empty() {
3960 return Err(self.err(String::from("MERGE requires at least one WHEN clause")));
3961 }
3962 Ok(Statement::Merge(crate::ast::MergeStatement {
3963 target,
3964 target_alias,
3965 source,
3966 source_alias,
3967 on,
3968 clauses,
3969 }))
3970 }
3971
3972 fn parse_optional_returning(
3977 &mut self,
3978 ) -> Result<Option<Vec<crate::ast::SelectItem>>, ParseError> {
3979 let is_returning_kw = matches!(
3980 self.peek(),
3981 Token::Ident(s) if s.eq_ignore_ascii_case("returning")
3982 );
3983 if !is_returning_kw {
3984 return Ok(None);
3985 }
3986 self.advance();
3987 let mut items = Vec::new();
3988 loop {
3989 items.push(self.parse_select_item()?);
3990 if matches!(self.peek(), Token::Comma) {
3991 self.advance();
3992 continue;
3993 }
3994 break;
3995 }
3996 Ok(Some(items))
3997 }
3998
3999 fn parse_alter_after_keyword(&mut self) -> Result<Statement, ParseError> {
4007 match self.advance() {
4014 Token::Index => {}
4015 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("index") => {}
4016 Token::Table => {
4019 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("only")) {
4020 self.advance();
4021 }
4022 return self.parse_alter_table_after_keyword();
4023 }
4024 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("table") => {
4025 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("only")) {
4026 self.advance();
4027 }
4028 return self.parse_alter_table_after_keyword();
4029 }
4030 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sequence") => {
4033 return self.parse_alter_sequence_after_keyword();
4034 }
4035 Token::Ident(s) | Token::QuotedIdent(s)
4041 if matches!(
4042 s.to_ascii_lowercase().as_str(),
4043 "view"
4044 | "function"
4045 | "type"
4046 | "domain"
4047 | "database"
4048 | "role"
4049 | "schema"
4050 | "owner"
4051 | "default"
4052 | "extension"
4053 | "materialized"
4054 | "policy"
4055 | "publication"
4056 | "subscription"
4057 ) =>
4058 {
4059 self.consume_until_statement_boundary();
4060 return Ok(Statement::Empty);
4061 }
4062 other => {
4063 return Err(self.err(format!(
4064 "expected INDEX / TABLE / SEQUENCE / VIEW / FUNCTION / TYPE / OWNER / etc \
4065 after ALTER, got {other:?}"
4066 )));
4067 }
4068 }
4069 let if_exists = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
4074 let next = self.tokens.get(self.pos + 1);
4075 if matches!(next, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")) {
4076 self.advance();
4077 self.advance();
4078 true
4079 } else {
4080 false
4081 }
4082 } else {
4083 false
4084 };
4085 let name = self.expect_ident_like()?;
4086 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("rename")) {
4090 self.advance();
4091 if matches!(self.peek(), Token::To) {
4092 self.advance();
4093 } else {
4094 self.expect_keyword_ident("to")?;
4095 }
4096 let new = self.expect_ident_like()?;
4097 return Ok(Statement::AlterIndex(crate::ast::AlterIndexStatement {
4098 name,
4099 target: crate::ast::AlterIndexTarget::Rename { new, if_exists },
4100 }));
4101 }
4102 self.expect_keyword_ident("rebuild")?;
4104 let encoding = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
4106 self.advance();
4107 if !matches!(self.peek(), Token::LParen) {
4108 return Err(self.err(format!(
4109 "expected '(' after WITH in ALTER INDEX REBUILD, got {:?}",
4110 self.peek()
4111 )));
4112 }
4113 self.advance();
4114 self.expect_keyword_ident("encoding")?;
4115 if !matches!(self.peek(), Token::Eq) {
4116 return Err(self.err(format!(
4117 "expected '=' after encoding in ALTER INDEX REBUILD, got {:?}",
4118 self.peek()
4119 )));
4120 }
4121 self.advance();
4122 let enc_ident = match self.advance() {
4123 Token::Ident(s) | Token::QuotedIdent(s) => s,
4124 other => {
4125 return Err(self.err(format!("expected encoding name after =, got {other:?}")));
4126 }
4127 };
4128 let enc = match enc_ident.to_ascii_lowercase().as_str() {
4129 "f32" => VecEncoding::F32,
4130 "sq8" => VecEncoding::Sq8,
4131 "half" => VecEncoding::F16,
4132 other => {
4133 return Err(self.err(format!(
4134 "unknown vector encoding {other:?} in ALTER INDEX REBUILD; supported: F32, SQ8, HALF"
4135 )));
4136 }
4137 };
4138 if !matches!(self.peek(), Token::RParen) {
4139 return Err(self.err(format!(
4140 "expected ')' after encoding value, got {:?}",
4141 self.peek()
4142 )));
4143 }
4144 self.advance();
4145 Some(enc)
4146 } else {
4147 None
4148 };
4149 Ok(Statement::AlterIndex(crate::ast::AlterIndexStatement {
4150 name,
4151 target: crate::ast::AlterIndexTarget::Rebuild { encoding },
4152 }))
4153 }
4154
4155 fn parse_alter_table_after_keyword(&mut self) -> Result<Statement, ParseError> {
4161 let table_name = self.expect_ident_like()?;
4162 let mut targets: Vec<crate::ast::AlterTableTarget> = Vec::new();
4163 loop {
4164 let subaction = self.parse_alter_table_subaction()?;
4165 targets.extend(subaction);
4169 if matches!(self.peek(), Token::Comma) {
4170 self.advance();
4171 continue;
4172 }
4173 break;
4174 }
4175 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
4176 name: table_name,
4177 targets,
4178 }))
4179 }
4180
4181 fn parse_alter_table_subaction(
4185 &mut self,
4186 ) -> Result<Vec<crate::ast::AlterTableTarget>, ParseError> {
4187 match self.peek() {
4188 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
4189 self.advance();
4190 let setting = self.expect_ident_like()?;
4191 if !setting.eq_ignore_ascii_case("hot_tier_bytes") {
4192 return Err(self.err(alloc::format!(
4193 "ALTER TABLE SET: unknown setting {setting:?}; supported: hot_tier_bytes"
4194 )));
4195 }
4196 if !matches!(self.peek(), Token::Eq) {
4197 return Err(self.err(alloc::format!(
4198 "expected '=' after hot_tier_bytes, got {:?}",
4199 self.peek()
4200 )));
4201 }
4202 self.advance();
4203 let n = self.expect_u64_literal()?;
4204 Ok(alloc::vec![crate::ast::AlterTableTarget::SetHotTierBytes(n)])
4205 }
4206 Token::Ident(s) if s.eq_ignore_ascii_case("add") => {
4207 self.advance();
4208 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint"))
4215 {
4216 let kind_pos = self.pos + 2;
4219 let kind = self.tokens.get(kind_pos).cloned();
4220 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("foreign"))
4221 {
4222 let fk = self.parse_table_level_fk()?;
4223 return Ok(alloc::vec![
4224 crate::ast::AlterTableTarget::AddForeignKey(fk)
4225 ]);
4226 }
4227 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("primary"))
4228 {
4229 self.advance(); let _name = self.expect_ident_like()?;
4231 self.advance(); self.expect_keyword_ident("key")?;
4233 let cols = self.parse_paren_ident_list("PRIMARY KEY")?;
4234 return Ok(alloc::vec![
4235 crate::ast::AlterTableTarget::AddTableConstraint(
4236 crate::ast::TableConstraint::PrimaryKey {
4237 name: None,
4238 columns: cols,
4239 }
4240 )
4241 ]);
4242 }
4243 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("unique"))
4244 {
4245 self.advance(); let _name = self.expect_ident_like()?;
4247 let uc = self.parse_table_level_unique()?;
4253 return Ok(alloc::vec![
4254 crate::ast::AlterTableTarget::AddTableConstraint(uc)
4255 ]);
4256 }
4257 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("check"))
4258 {
4259 self.advance(); let _name = self.expect_ident_like()?;
4261 self.advance(); if !matches!(self.peek(), Token::LParen) {
4263 return Err(self.err(alloc::format!(
4264 "expected '(' after CHECK, got {:?}", self.peek()
4265 )));
4266 }
4267 self.advance();
4268 let expr = self.parse_expr(0)?;
4269 if matches!(self.peek(), Token::RParen) {
4270 self.advance();
4271 }
4272 return Ok(alloc::vec![
4273 crate::ast::AlterTableTarget::AddTableConstraint(
4274 crate::ast::TableConstraint::Check { name: None, expr }
4275 )
4276 ]);
4277 }
4278 }
4281 let is_fk = matches!(
4282 self.peek(),
4283 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
4284 || s.eq_ignore_ascii_case("foreign")
4285 );
4286 if is_fk {
4287 let fk = self.parse_table_level_fk()?;
4288 return Ok(alloc::vec![crate::ast::AlterTableTarget::AddForeignKey(fk)]);
4289 }
4290 match self.peek().clone() {
4293 Token::Ident(s) if s.eq_ignore_ascii_case("primary") => {
4294 self.advance();
4295 self.expect_keyword_ident("key")?;
4296 let cols = self.parse_paren_ident_list("PRIMARY KEY")?;
4297 return Ok(alloc::vec![
4298 crate::ast::AlterTableTarget::AddTableConstraint(
4299 crate::ast::TableConstraint::PrimaryKey {
4300 name: None,
4301 columns: cols,
4302 }
4303 )
4304 ]);
4305 }
4306 Token::Ident(s) if s.eq_ignore_ascii_case("unique") => {
4307 let uc = self.parse_table_level_unique()?;
4309 return Ok(alloc::vec![
4310 crate::ast::AlterTableTarget::AddTableConstraint(uc)
4311 ]);
4312 }
4313 _ => {}
4314 }
4315 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
4316 self.advance();
4317 }
4318 let mut if_not_exists = false;
4319 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
4320 self.advance();
4321 if !matches!(self.peek(), Token::Not) {
4322 return Err(self.err(alloc::format!(
4323 "expected NOT after IF in ALTER TABLE ADD COLUMN, got {:?}",
4324 self.peek()
4325 )));
4326 }
4327 self.advance();
4328 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("exists")) {
4329 return Err(self.err(alloc::format!(
4330 "expected EXISTS after IF NOT in ALTER TABLE ADD COLUMN, got {:?}",
4331 self.peek()
4332 )));
4333 }
4334 self.advance();
4335 if_not_exists = true;
4336 }
4337 let (column, col_level_fk) = self.parse_column_def_with_fk()?;
4341 let col_name = column.name.clone();
4342 let mut out = alloc::vec![crate::ast::AlterTableTarget::AddColumn {
4343 column,
4344 if_not_exists,
4345 }];
4346 if let Some(mut fk) = col_level_fk {
4347 if fk.columns.is_empty() {
4348 fk.columns.push(col_name);
4349 }
4350 out.push(crate::ast::AlterTableTarget::AddForeignKey(fk));
4351 }
4352 Ok(out)
4353 }
4354 Token::Drop => {
4355 self.advance();
4356 let subject = match self.peek() {
4363 Token::Ident(s) if s.eq_ignore_ascii_case("constraint") => {
4364 self.advance();
4365 "constraint"
4366 }
4367 Token::Ident(s) if s.eq_ignore_ascii_case("column") => {
4368 self.advance();
4369 "column"
4370 }
4371 Token::Ident(_) | Token::QuotedIdent(_) => "column",
4375 other => {
4376 return Err(self.err(alloc::format!(
4377 "expected COLUMN / CONSTRAINT after DROP in ALTER TABLE, got {other:?}"
4378 )));
4379 }
4380 };
4381 let mut if_exists = false;
4382 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
4383 let n1 = self.tokens.get(self.pos + 1);
4384 if matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")) {
4385 self.advance();
4386 self.advance();
4387 if_exists = true;
4388 }
4389 }
4390 let name = self.expect_ident_like()?;
4391 let mut cascade = false;
4392 if matches!(
4393 self.peek(),
4394 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
4395 || s.eq_ignore_ascii_case("restrict")
4396 ) {
4397 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("cascade"))
4398 {
4399 cascade = true;
4400 }
4401 self.advance();
4402 }
4403 if subject == "constraint" {
4404 Ok(alloc::vec![crate::ast::AlterTableTarget::DropForeignKey {
4405 name,
4406 if_exists,
4407 }])
4408 } else {
4409 Ok(alloc::vec![crate::ast::AlterTableTarget::DropColumn {
4410 column: name,
4411 if_exists,
4412 cascade,
4413 }])
4414 }
4415 }
4416 Token::Ident(s) if s.eq_ignore_ascii_case("alter") => {
4417 self.advance();
4418 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
4419 self.advance();
4420 }
4421 let col_name = self.expect_ident_like()?;
4422 match self.peek() {
4423 Token::Ident(s) if s.eq_ignore_ascii_case("type") => {
4424 self.advance();
4425 }
4426 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
4434 let is_default_nextval =
4445 matches!(self.tokens.get(self.pos + 1), Some(Token::Default))
4446 && matches!(
4447 self.tokens.get(self.pos + 2),
4448 Some(Token::Ident(f)) if f.eq_ignore_ascii_case("nextval")
4449 );
4450 let seq_name = if is_default_nextval {
4453 self.scan_sequence_name_until_boundary()
4454 } else {
4455 self.consume_until_statement_boundary();
4456 None
4457 };
4458 if is_default_nextval {
4459 return Ok(alloc::vec![
4460 crate::ast::AlterTableTarget::SetColumnAutoIncrement {
4461 column: col_name,
4462 seq_name,
4463 }
4464 ]);
4465 }
4466 return Ok(Vec::new());
4472 }
4473 Token::Ident(s) if s.eq_ignore_ascii_case("drop") => {
4474 self.consume_until_statement_boundary();
4476 return Ok(Vec::new());
4477 }
4478 Token::Drop => {
4479 self.consume_until_statement_boundary();
4485 return Ok(Vec::new());
4486 }
4487 Token::Ident(s) if s.eq_ignore_ascii_case("add") => {
4488 let is_generated = matches!(
4496 self.tokens.get(self.pos + 1),
4497 Some(Token::Ident(g)) if g.eq_ignore_ascii_case("generated")
4498 );
4499 if !is_generated {
4500 return Err(self.err(alloc::format!(
4501 "expected GENERATED after ALTER COLUMN {col_name} ADD, got {:?}",
4502 self.tokens.get(self.pos + 1)
4503 )));
4504 }
4505 let seq_name = self.scan_sequence_name_until_boundary();
4506 return Ok(alloc::vec![
4507 crate::ast::AlterTableTarget::SetColumnAutoIncrement {
4508 column: col_name,
4509 seq_name,
4510 }
4511 ]);
4512 }
4513 other => {
4514 return Err(self.err(alloc::format!(
4515 "expected TYPE / SET / DROP / ADD after ALTER COLUMN <name>, got {other:?}"
4516 )));
4517 }
4518 }
4519 let new_type = self.parse_column_type_name()?;
4520 let using = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using"))
4521 {
4522 self.advance();
4523 Some(self.parse_expr(0)?)
4524 } else {
4525 None
4526 };
4527 Ok(alloc::vec![crate::ast::AlterTableTarget::AlterColumnType {
4528 column: col_name,
4529 new_type,
4530 using,
4531 }])
4532 }
4533 Token::Ident(s) if s.eq_ignore_ascii_case("rename") => {
4540 self.advance();
4541 if matches!(self.peek(), Token::To)
4546 || matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("to"))
4547 {
4548 self.advance();
4549 let new = self.expect_ident_like()?;
4550 return Ok(alloc::vec![crate::ast::AlterTableTarget::RenameTable {
4551 new,
4552 }]);
4553 }
4554 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
4555 self.advance();
4556 }
4557 let old = self.expect_ident_like()?;
4558 if matches!(self.peek(), Token::To) {
4561 self.advance();
4562 } else {
4563 self.expect_keyword_ident("to")?;
4564 }
4565 let new = self.expect_ident_like()?;
4566 Ok(alloc::vec![crate::ast::AlterTableTarget::RenameColumn {
4567 old,
4568 new,
4569 }])
4570 }
4571 Token::Ident(s)
4578 if s.eq_ignore_ascii_case("enable") || s.eq_ignore_ascii_case("disable") =>
4579 {
4580 let enabled = s.eq_ignore_ascii_case("enable");
4581 self.advance();
4582 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("keys")) {
4592 self.advance();
4593 return Ok(Vec::new());
4594 }
4595 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("trigger")) {
4596 return Err(self.err(alloc::format!(
4597 "expected TRIGGER after {}, got {:?}",
4598 if enabled { "ENABLE" } else { "DISABLE" },
4599 self.peek()
4600 )));
4601 }
4602 self.advance();
4603 let which = if matches!(self.peek(), Token::All)
4606 || matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("all"))
4607 {
4608 self.advance();
4609 crate::ast::TriggerSelector::All
4610 } else {
4611 let name = self.expect_ident_like()?;
4612 crate::ast::TriggerSelector::Named(name)
4613 };
4614 Ok(alloc::vec![crate::ast::AlterTableTarget::SetTriggerEnabled {
4615 which,
4616 enabled,
4617 }])
4618 }
4619 other => Err(self.err(alloc::format!(
4620 "expected SET / ADD / DROP / ALTER / RENAME / ENABLE / DISABLE in ALTER TABLE, got {other:?}"
4621 ))),
4622 }
4623 }
4624
4625 fn try_peek_meta_qualified(&mut self) -> Option<String> {
4634 let schema = match self.tokens.get(self.pos) {
4636 Some(Token::Ident(s) | Token::QuotedIdent(s)) => s.clone(),
4637 _ => return None,
4638 };
4639 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Dot)) {
4641 return None;
4642 }
4643 let tbl = match self.tokens.get(self.pos + 2)? {
4647 Token::Ident(t) | Token::QuotedIdent(t) => t.clone(),
4648 Token::Tables => "tables".to_string(),
4649 _ => return None,
4652 };
4653 let (prefix, normalised) = if schema.eq_ignore_ascii_case("information_schema") {
4657 ("__spg_info_", tbl.to_ascii_lowercase())
4658 } else if schema.eq_ignore_ascii_case("pg_catalog") {
4659 let bare = tbl
4660 .to_ascii_lowercase()
4661 .strip_prefix("pg_")
4662 .map(alloc::string::String::from)
4663 .unwrap_or_else(|| tbl.to_ascii_lowercase());
4664 ("__spg_pg_", bare)
4665 } else if schema.eq_ignore_ascii_case("mysql") {
4666 ("__spg_mysql_", tbl.to_ascii_lowercase())
4670 } else {
4671 return None;
4672 };
4673 self.advance(); self.advance(); self.advance(); Some(alloc::format!("{prefix}{normalised}"))
4677 }
4678
4679 fn try_peek_meta_bare(&mut self) -> Option<String> {
4686 const PG_META_TABLES: &[&str] = &[
4687 "pg_attribute",
4688 "pg_class",
4689 "pg_constraint",
4690 "pg_database",
4691 "pg_extension",
4692 "pg_index",
4693 "pg_indexes",
4694 "pg_matviews",
4695 "pg_namespace",
4696 "pg_proc",
4697 "pg_roles",
4698 "pg_settings",
4699 "pg_trigger",
4700 "pg_type",
4701 "pg_user",
4702 "pg_views",
4703 ];
4704 let name = match self.tokens.get(self.pos) {
4705 Some(Token::Ident(s)) => s.to_ascii_lowercase(),
4706 _ => return None,
4707 };
4708 if matches!(self.tokens.get(self.pos + 1), Some(Token::Dot)) {
4711 return None;
4712 }
4713 if !PG_META_TABLES.contains(&name.as_str()) {
4714 return None;
4715 }
4716 self.advance();
4717 let bare = name.strip_prefix("pg_").unwrap_or(&name);
4718 Some(alloc::format!("__spg_pg_{bare}"))
4719 }
4720
4721 fn expect_keyword_ident(&mut self, kw: &str) -> Result<(), ParseError> {
4723 match self.advance() {
4724 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case(kw) => Ok(()),
4725 other => Err(ParseError {
4726 message: format!("expected {kw:?}, got {other:?}"),
4727 token_pos: self.pos.saturating_sub(1),
4728 }),
4729 }
4730 }
4731
4732 fn expect_ident_or_string(&mut self) -> Result<String, ParseError> {
4736 match self.advance() {
4737 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => Ok(s),
4738 other => Err(ParseError {
4739 message: format!("expected identifier or string, got {other:?}"),
4740 token_pos: self.pos.saturating_sub(1),
4741 }),
4742 }
4743 }
4744
4745 fn expect_string_literal(&mut self) -> Result<String, ParseError> {
4746 match self.advance() {
4747 Token::String(s) => Ok(s),
4748 other => Err(ParseError {
4749 message: format!("expected quoted string, got {other:?}"),
4750 token_pos: self.pos.saturating_sub(1),
4751 }),
4752 }
4753 }
4754
4755 fn parse_select_stmt(&mut self) -> Result<Statement, ParseError> {
4756 self.enter_nested()?;
4760 let r = self.parse_select_stmt_inner();
4761 self.nest_depth -= 1;
4762 r
4763 }
4764
4765 fn parse_select_stmt_inner(&mut self) -> Result<Statement, ParseError> {
4766 let mut head = self.parse_bare_select()?;
4771 while matches!(self.peek(), Token::Union) {
4772 self.advance();
4773 let kind = if matches!(self.peek(), Token::All) {
4774 self.advance();
4775 UnionKind::All
4776 } else {
4777 UnionKind::Distinct
4778 };
4779 let peer = self.parse_bare_select()?;
4780 head.unions.push((kind, peer));
4781 }
4782 head.order_by = if matches!(self.peek(), Token::Order) {
4783 self.advance();
4784 if !matches!(self.peek(), Token::By) {
4785 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
4786 }
4787 self.advance();
4788 let mut keys = Vec::new();
4791 loop {
4792 let expr = self.parse_expr(0)?;
4793 let desc = if matches!(self.peek(), Token::Desc) {
4794 self.advance();
4795 true
4796 } else if matches!(self.peek(), Token::Asc) {
4797 self.advance();
4798 false
4799 } else {
4800 false
4801 };
4802 let nulls_first = self.parse_optional_nulls_placement()?;
4804 keys.push(OrderBy {
4805 expr,
4806 desc,
4807 nulls_first,
4808 });
4809 if matches!(self.peek(), Token::Comma) {
4810 self.advance();
4811 } else {
4812 break;
4813 }
4814 }
4815 keys
4816 } else {
4817 Vec::new()
4818 };
4819 head.limit = if matches!(self.peek(), Token::Limit) {
4820 self.advance();
4821 if self.consume_limit_unbounded_sentinel() {
4828 None
4829 } else {
4830 Some(self.parse_limit_expr("LIMIT")?)
4831 }
4832 } else {
4833 None
4834 };
4835 head.offset = if matches!(self.peek(), Token::Offset) {
4836 self.advance();
4837 let off = self.parse_limit_expr("OFFSET")?;
4841 self.consume_optional_rows_keyword();
4842 Some(off)
4843 } else {
4844 None
4845 };
4846 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("fetch"))
4852 {
4853 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4856 if s.eq_ignore_ascii_case("first") || s.eq_ignore_ascii_case("next"))
4857 {
4858 self.advance();
4859 }
4860 let count = if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4863 if s.eq_ignore_ascii_case("row") || s.eq_ignore_ascii_case("rows"))
4864 {
4865 crate::ast::LimitExpr::Literal(1)
4867 } else {
4868 self.parse_limit_expr("FETCH FIRST")?
4869 };
4870 self.consume_optional_rows_keyword();
4872 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4878 if s.eq_ignore_ascii_case("only"))
4879 {
4880 self.advance();
4881 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4882 if s.eq_ignore_ascii_case("with"))
4883 {
4884 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4886 if s.eq_ignore_ascii_case("ties"))
4887 {
4888 self.advance();
4889 head.limit_with_ties = true;
4890 }
4891 }
4892 head.limit = Some(count);
4893 }
4894 self.consume_optional_for_lock_clauses();
4909 Ok(Statement::Select(head))
4910 }
4911
4912 fn consume_optional_for_lock_clauses(&mut self) {
4919 while matches!(self.peek(), Token::For) {
4920 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4924 if s.eq_ignore_ascii_case("no"))
4925 {
4926 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4930 if s.eq_ignore_ascii_case("key"))
4931 {
4932 self.advance(); }
4934 }
4935 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4937 if s.eq_ignore_ascii_case("key"))
4938 {
4939 self.advance(); }
4941 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4947 if s.eq_ignore_ascii_case("update") || s.eq_ignore_ascii_case("share"))
4948 {
4949 self.advance();
4950 } else {
4951 return;
4956 }
4957 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4960 if s.eq_ignore_ascii_case("of"))
4961 {
4962 self.advance(); #[allow(clippy::while_let_loop)]
4964 loop {
4965 match self.peek() {
4966 Token::Ident(_) | Token::QuotedIdent(_) => {
4967 self.advance();
4968 if matches!(self.peek(), Token::Dot) {
4970 self.advance();
4971 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
4972 self.advance();
4973 }
4974 }
4975 }
4976 _ => break,
4977 }
4978 if matches!(self.peek(), Token::Comma) {
4979 self.advance();
4980 } else {
4981 break;
4982 }
4983 }
4984 }
4985 match self.peek().clone() {
4987 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nowait") => {
4988 self.advance();
4989 }
4990 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("skip") => {
4991 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4993 if s.eq_ignore_ascii_case("locked"))
4994 {
4995 self.advance(); }
4997 }
4998 _ => {}
4999 }
5000 }
5002 }
5003
5004 fn consume_limit_unbounded_sentinel(&mut self) -> bool {
5013 if matches!(self.peek(), Token::Null) {
5014 self.advance();
5015 return true;
5016 }
5017 if matches!(self.peek(), Token::All) {
5018 self.advance();
5019 return true;
5020 }
5021 false
5022 }
5023
5024 fn consume_optional_rows_keyword(&mut self) {
5028 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
5029 if s.eq_ignore_ascii_case("row") || s.eq_ignore_ascii_case("rows"))
5030 {
5031 self.advance();
5032 }
5033 }
5034
5035 fn parse_limit_expr(&mut self, label: &str) -> Result<crate::ast::LimitExpr, ParseError> {
5036 match self.advance() {
5037 Token::Integer(n) if n >= 0 => u32::try_from(n)
5038 .map(crate::ast::LimitExpr::Literal)
5039 .map_err(|_| ParseError {
5040 message: alloc::format!("{label} value too large: {n}"),
5041 token_pos: self.pos.saturating_sub(1),
5042 }),
5043 Token::Placeholder(n) => Ok(crate::ast::LimitExpr::Placeholder(n)),
5044 other => Err(ParseError {
5045 message: alloc::format!(
5046 "expected non-negative integer or $N placeholder after {label}, got {other:?}"
5047 ),
5048 token_pos: self.pos.saturating_sub(1),
5049 }),
5050 }
5051 }
5052
5053 fn parse_bare_select(&mut self) -> Result<SelectStatement, ParseError> {
5058 if !matches!(self.peek(), Token::Select) {
5059 return Err(self.err(format!(
5060 "expected SELECT to start a query block, got {:?}",
5061 self.peek()
5062 )));
5063 }
5064 self.advance();
5065 let distinct = if matches!(self.peek(), Token::Distinct) {
5066 self.advance();
5067 true
5068 } else {
5069 false
5070 };
5071 let items = self.parse_select_list()?;
5072 let from = if matches!(self.peek(), Token::From) {
5073 self.advance();
5074 Some(self.parse_from_clause()?)
5075 } else {
5076 None
5077 };
5078 let where_ = if matches!(self.peek(), Token::Where) {
5079 self.advance();
5080 Some(self.parse_expr(0)?)
5081 } else {
5082 None
5083 };
5084 let mut group_by_all = false;
5085 let group_by = if matches!(self.peek(), Token::Group) {
5086 self.advance();
5087 if !matches!(self.peek(), Token::By) {
5088 return Err(self.err(format!("expected BY after GROUP, got {:?}", self.peek())));
5089 }
5090 self.advance();
5091 if matches!(self.peek(), Token::All) {
5094 self.advance();
5095 group_by_all = true;
5096 None
5097 } else {
5098 let mut groups = Vec::new();
5099 loop {
5100 groups.push(self.parse_expr(0)?);
5101 if matches!(self.peek(), Token::Comma) {
5102 self.advance();
5103 } else {
5104 break;
5105 }
5106 }
5107 Some(groups)
5108 }
5109 } else {
5110 None
5111 };
5112 let having = if matches!(self.peek(), Token::Having) {
5113 self.advance();
5114 Some(self.parse_expr(0)?)
5115 } else {
5116 None
5117 };
5118 Ok(SelectStatement {
5119 ctes: Vec::new(),
5120 distinct,
5121 items,
5122 from,
5123 where_,
5124 group_by,
5125 group_by_all,
5126 having,
5127 unions: Vec::new(),
5128 order_by: Vec::new(),
5129 limit: None,
5130 offset: None,
5131 limit_with_ties: false,
5132 })
5133 }
5134
5135 fn parse_create_table_stmt_after_create(&mut self) -> Result<Statement, ParseError> {
5136 debug_assert!(matches!(self.peek(), Token::Table));
5138 self.advance();
5139 let if_not_exists = self.consume_if_not_exists();
5140 let name = self.expect_ident_like()?;
5141 if matches!(self.peek(), Token::Partition)
5146 && Self::tokens_match_ident_ci(self.tokens.get(self.pos + 1), "of")
5147 {
5148 self.advance(); self.advance(); let partition_of = self.parse_partition_of_tail()?;
5151 return Ok(Statement::CreateTable(CreateTableStatement {
5152 name,
5153 columns: Vec::new(),
5154 if_not_exists,
5155 foreign_keys: Vec::new(),
5156 table_constraints: Vec::new(),
5157 partition_by: None,
5158 partition_of: Some(partition_of),
5159 }));
5160 }
5161 if !matches!(self.peek(), Token::LParen) {
5162 return Err(self.err(format!(
5163 "expected '(' after table name, got {:?}",
5164 self.peek()
5165 )));
5166 }
5167 self.advance();
5168 let mut columns = Vec::new();
5169 let mut foreign_keys: Vec<ForeignKeyConstraint> = Vec::new();
5170 let mut table_constraints: Vec<crate::ast::TableConstraint> = Vec::new();
5171 loop {
5172 if self.peek_table_level_pk_start() {
5178 table_constraints.push(self.parse_table_level_primary_key()?);
5179 } else if self.peek_table_level_unique_start() {
5180 table_constraints.push(self.parse_table_level_unique()?);
5181 } else if self.peek_table_level_check_start() {
5182 table_constraints.push(self.parse_table_level_check()?);
5184 } else if self.peek_mysql_inline_key_start() {
5185 if let Some(uc) = self.parse_mysql_inline_key()? {
5191 table_constraints.push(uc);
5192 }
5193 } else if let Some(kind) = self.peek_named_table_constraint_kind() {
5194 self.advance(); let _name = self.expect_ident_like()?;
5202 table_constraints.push(match kind {
5203 NamedTableConstraintKind::Check => self.parse_table_level_check()?,
5204 NamedTableConstraintKind::Unique => self.parse_table_level_unique()?,
5205 NamedTableConstraintKind::PrimaryKey => self.parse_table_level_primary_key()?,
5206 });
5207 } else if self.peek_constraint_or_fk_start() {
5208 foreign_keys.push(self.parse_table_level_fk()?);
5209 } else {
5210 let (col, col_level_fk) = self.parse_column_def_with_fk()?;
5211 if col.is_unique {
5215 table_constraints.push(crate::ast::TableConstraint::Unique {
5216 name: None,
5217 columns: alloc::vec![col.name.clone()],
5218 nulls_not_distinct: false,
5219 });
5220 }
5221 if let Some(check_expr) = col.check.clone() {
5222 table_constraints.push(crate::ast::TableConstraint::Check {
5223 name: None,
5224 expr: check_expr,
5225 });
5226 }
5227 columns.push(col);
5228 if let Some(fk) = col_level_fk {
5229 foreign_keys.push(fk);
5230 }
5231 }
5232 match self.peek() {
5233 Token::Comma => {
5234 self.advance();
5235 }
5236 Token::RParen => {
5237 self.advance();
5238 break;
5239 }
5240 other => {
5241 return Err(
5242 self.err(format!("expected ',' or ')' in column list, got {other:?}"))
5243 );
5244 }
5245 }
5246 }
5247 if columns.is_empty() {
5248 return Err(self.err("CREATE TABLE requires at least one column".into()));
5249 }
5250 self.consume_mysql_table_options();
5257 let partition_by = if matches!(self.peek(), Token::Partition) {
5263 self.advance(); if !matches!(self.peek(), Token::By) {
5265 return Err(self.err(format!(
5266 "expected BY after PARTITION, got {:?}",
5267 self.peek()
5268 )));
5269 }
5270 self.advance();
5271 Some(self.parse_partition_by_tail()?)
5272 } else {
5273 None
5274 };
5275 Ok(Statement::CreateTable(CreateTableStatement {
5276 name,
5277 columns,
5278 if_not_exists,
5279 foreign_keys,
5280 table_constraints,
5281 partition_by,
5282 partition_of: None,
5283 }))
5284 }
5285
5286 fn tokens_match_ident_ci(t: Option<&Token>, want: &str) -> bool {
5292 matches!(t, Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case(want))
5293 }
5294
5295 fn parse_partition_by_tail(&mut self) -> Result<crate::ast::PartitionBySpec, ParseError> {
5297 use crate::ast::{PartitionBySpec, PartitionKindAst};
5298 let kind = match self.peek() {
5299 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("range") => {
5300 self.advance();
5301 PartitionKindAst::Range
5302 }
5303 other => {
5304 return Err(self.err(format!(
5305 "PARTITION BY: only RANGE is supported at v7.37.6-B, got {other:?}"
5306 )));
5307 }
5308 };
5309 if !matches!(self.peek(), Token::LParen) {
5310 return Err(self.err(format!(
5311 "expected '(' after PARTITION BY RANGE, got {:?}",
5312 self.peek()
5313 )));
5314 }
5315 self.advance();
5316 let mut key_columns = Vec::new();
5317 loop {
5318 key_columns.push(self.expect_ident_like()?);
5319 match self.peek() {
5320 Token::Comma => {
5321 self.advance();
5322 }
5323 Token::RParen => {
5324 self.advance();
5325 break;
5326 }
5327 other => {
5328 return Err(self.err(format!(
5329 "expected ',' or ')' in PARTITION BY key list, got {other:?}"
5330 )));
5331 }
5332 }
5333 }
5334 if key_columns.is_empty() {
5335 return Err(self.err("PARTITION BY RANGE requires at least one key column".to_string()));
5336 }
5337 Ok(PartitionBySpec { kind, key_columns })
5338 }
5339
5340 fn parse_partition_of_tail(&mut self) -> Result<crate::ast::PartitionOfSpec, ParseError> {
5345 use crate::ast::{PartitionOfBoundsAst, PartitionOfSpec};
5346 let parent_name = self.expect_ident_like()?;
5347 if matches!(self.peek(), Token::LParen) {
5353 return Err(self.err(
5354 "CREATE TABLE … PARTITION OF parent: explicit column list not supported \
5355 at v7.37.6-B; the child inherits its columns from the parent"
5356 .to_string(),
5357 ));
5358 }
5359 let bounds = match self.peek() {
5360 Token::Default => {
5361 self.advance();
5362 PartitionOfBoundsAst::Default
5363 }
5364 Token::For => {
5365 self.advance();
5366 if !matches!(self.peek(), Token::Values) {
5367 return Err(
5368 self.err(format!("expected VALUES after FOR, got {:?}", self.peek()))
5369 );
5370 }
5371 self.advance();
5372 if !matches!(self.peek(), Token::From) {
5373 return Err(self.err(format!(
5374 "expected FROM after FOR VALUES, got {:?}",
5375 self.peek()
5376 )));
5377 }
5378 self.advance();
5379 let lower = Box::new(self.parse_partition_bound_expr()?);
5380 if !matches!(self.peek(), Token::To) {
5381 return Err(self.err(format!(
5382 "expected TO after FROM (...), got {:?}",
5383 self.peek()
5384 )));
5385 }
5386 self.advance();
5387 let upper = Box::new(self.parse_partition_bound_expr()?);
5388 PartitionOfBoundsAst::Range { lower, upper }
5389 }
5390 other => {
5391 return Err(self.err(format!(
5392 "expected FOR VALUES or DEFAULT after PARTITION OF parent, got {other:?}"
5393 )));
5394 }
5395 };
5396 Ok(PartitionOfSpec {
5397 parent_name,
5398 bounds,
5399 })
5400 }
5401
5402 fn parse_partition_bound_expr(&mut self) -> Result<crate::ast::Expr, ParseError> {
5407 if !matches!(self.peek(), Token::LParen) {
5408 return Err(self.err(format!(
5409 "expected '(' before partition bound, got {:?}",
5410 self.peek()
5411 )));
5412 }
5413 self.advance();
5414 let expr = match self.peek() {
5415 Token::Ident(s) | Token::QuotedIdent(s)
5416 if s.eq_ignore_ascii_case("minvalue") || s.eq_ignore_ascii_case("maxvalue") =>
5417 {
5418 let name = s.to_ascii_uppercase();
5419 self.advance();
5420 crate::ast::Expr::FunctionCall {
5421 name,
5422 args: Vec::new(),
5423 }
5424 }
5425 _ => self.parse_expr(0)?,
5426 };
5427 if !matches!(self.peek(), Token::RParen) {
5428 return Err(self.err(format!(
5429 "expected ')' after partition bound, got {:?}",
5430 self.peek()
5431 )));
5432 }
5433 self.advance();
5434 Ok(expr)
5435 }
5436
5437 fn peek_mysql_inline_key_start(&self) -> bool {
5446 let cur = self.peek();
5447 let after_keyword_followed_by_paren_or_ident_paren = |skip: usize| -> bool {
5457 match self.tokens.get(skip) {
5460 Some(Token::LParen) => true,
5461 Some(Token::Ident(_) | Token::QuotedIdent(_)) => {
5462 matches!(self.tokens.get(skip + 1), Some(Token::LParen))
5463 }
5464 _ => false,
5465 }
5466 };
5467 let is_key_or_index_tok = |t: &Token| -> bool {
5471 matches!(t, Token::Index)
5472 || matches!(t, Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index"))
5473 };
5474 match cur {
5475 Token::Index => after_keyword_followed_by_paren_or_ident_paren(self.pos + 1),
5476 Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index") => {
5477 after_keyword_followed_by_paren_or_ident_paren(self.pos + 1)
5478 }
5479 Token::Ident(s)
5480 if s.eq_ignore_ascii_case("fulltext") || s.eq_ignore_ascii_case("spatial") =>
5481 {
5482 let nxt = self.tokens.get(self.pos + 1);
5483 let after_after = if nxt.is_some_and(is_key_or_index_tok) {
5484 self.pos + 2
5485 } else {
5486 self.pos + 1
5487 };
5488 after_keyword_followed_by_paren_or_ident_paren(after_after)
5489 }
5490 Token::Ident(s) if s.eq_ignore_ascii_case("unique") => {
5491 let nxt = self.tokens.get(self.pos + 1);
5492 if !nxt.is_some_and(is_key_or_index_tok) {
5493 return false;
5494 }
5495 after_keyword_followed_by_paren_or_ident_paren(self.pos + 2)
5496 }
5497 _ => false,
5498 }
5499 }
5500
5501 fn parse_mysql_inline_key(
5510 &mut self,
5511 ) -> Result<Option<crate::ast::TableConstraint>, ParseError> {
5512 let is_unique = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unique"))
5514 {
5515 self.advance();
5516 true
5517 } else {
5518 false
5519 };
5520 let mut is_fulltext = false;
5526 let mut is_spatial = false;
5527 if let Token::Ident(s) = self.peek().clone() {
5528 if s.eq_ignore_ascii_case("fulltext") {
5529 self.advance();
5530 is_fulltext = true;
5531 } else if s.eq_ignore_ascii_case("spatial") {
5532 self.advance();
5533 is_spatial = true;
5534 }
5535 }
5536 match self.peek() {
5539 Token::Index => {
5540 self.advance();
5541 }
5542 Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index") => {
5543 self.advance();
5544 }
5545 other => {
5546 return Err(self.err(alloc::format!(
5547 "expected KEY/INDEX in inline index declaration, got {other:?}"
5548 )));
5549 }
5550 }
5551 let mut idx_name: Option<String> = None;
5556 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_))
5557 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
5558 {
5559 if let Token::Ident(s) | Token::QuotedIdent(s) = self.advance() {
5560 idx_name = Some(s);
5561 }
5562 }
5563 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
5565 self.advance();
5566 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
5567 self.advance();
5568 }
5569 }
5570 if !matches!(self.peek(), Token::LParen) {
5572 return Err(self.err(alloc::format!(
5573 "expected '(' in inline KEY/INDEX, got {:?}",
5574 self.peek()
5575 )));
5576 }
5577 self.advance();
5578 let mut cols: Vec<String> = Vec::new();
5579 while let Token::Ident(s) | Token::QuotedIdent(s) = self.peek().clone() {
5580 self.advance();
5581 cols.push(s);
5582 if matches!(self.peek(), Token::LParen) {
5584 let mut depth = 1usize;
5585 self.advance();
5586 while depth > 0 {
5587 match self.peek() {
5588 Token::LParen => depth += 1,
5589 Token::RParen => depth -= 1,
5590 Token::Eof => break,
5591 _ => {}
5592 }
5593 self.advance();
5594 }
5595 }
5596 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("asc") || s.eq_ignore_ascii_case("desc"))
5598 || matches!(self.peek(), Token::Asc | Token::Desc)
5599 {
5600 self.advance();
5601 }
5602 if matches!(self.peek(), Token::Comma) {
5603 self.advance();
5604 continue;
5605 }
5606 break;
5607 }
5608 if matches!(self.peek(), Token::RParen) {
5609 self.advance();
5610 }
5611 while !matches!(self.peek(), Token::Comma | Token::RParen | Token::Eof) {
5614 self.advance();
5615 }
5616 if cols.is_empty() {
5617 return Ok(None);
5618 }
5619 if is_unique {
5620 Ok(Some(crate::ast::TableConstraint::Unique {
5626 name: idx_name,
5627 columns: cols,
5628 nulls_not_distinct: false,
5629 }))
5630 } else if is_fulltext {
5631 Ok(Some(crate::ast::TableConstraint::FulltextIndex {
5637 name: idx_name,
5638 columns: cols,
5639 }))
5640 } else if is_spatial {
5641 Ok(None)
5644 } else {
5645 Ok(Some(crate::ast::TableConstraint::Index {
5648 name: idx_name,
5649 columns: cols,
5650 }))
5651 }
5652 }
5653
5654 fn consume_mysql_table_options(&mut self) {
5659 loop {
5660 let name_lc = match self.peek().clone() {
5664 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
5665 Token::Default => alloc::string::String::from("default"),
5666 _ => break,
5667 };
5668 let known = matches!(
5669 name_lc.as_str(),
5670 "engine"
5671 | "default"
5672 | "charset"
5673 | "collate"
5674 | "auto_increment"
5675 | "row_format"
5676 | "comment"
5677 | "pack_keys"
5678 | "stats_persistent"
5679 | "stats_auto_recalc"
5680 | "stats_sample_pages"
5681 | "key_block_size"
5682 | "tablespace"
5683 | "min_rows"
5684 | "max_rows"
5685 | "checksum"
5686 | "delay_key_write"
5687 | "insert_method"
5688 | "data"
5689 | "index"
5690 | "encryption"
5691 | "compression"
5692 );
5693 if !known {
5694 break;
5695 }
5696 self.advance(); if name_lc == "default" {
5700 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
5701 self.advance();
5702 }
5703 }
5704 if matches!(self.peek(), Token::Eq) {
5705 self.advance();
5706 }
5707 match self.peek() {
5708 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_) | Token::Integer(_) => {
5709 self.advance();
5710 }
5711 _ => {}
5712 }
5713 }
5714 }
5715
5716 fn peek_table_level_pk_start(&self) -> bool {
5721 let cur = self.peek();
5722 let nxt = self.tokens.get(self.pos + 1);
5723 let nxt2 = self.tokens.get(self.pos + 2);
5724 let is_primary = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("primary"));
5725 let is_key = matches!(nxt, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("key"));
5726 let is_lparen = matches!(nxt2, Some(Token::LParen));
5727 is_primary && is_key && is_lparen
5728 }
5729
5730 fn peek_table_level_unique_start(&self) -> bool {
5734 let cur = self.peek();
5735 let is_unique = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("unique"));
5736 if !is_unique {
5737 return false;
5738 }
5739 let n1 = self.tokens.get(self.pos + 1);
5740 if matches!(n1, Some(Token::LParen)) {
5742 return true;
5743 }
5744 let is_nulls = matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("nulls"));
5746 if !is_nulls {
5747 return false;
5748 }
5749 let n2 = self.tokens.get(self.pos + 2);
5750 let n3 = self.tokens.get(self.pos + 3);
5751 let n4 = self.tokens.get(self.pos + 4);
5752 if matches!(n2, Some(Token::Distinct)) && matches!(n3, Some(Token::LParen)) {
5754 return true;
5755 }
5756 if matches!(n2, Some(Token::Not))
5758 && matches!(n3, Some(Token::Distinct))
5759 && matches!(n4, Some(Token::LParen))
5760 {
5761 return true;
5762 }
5763 false
5764 }
5765
5766 fn parse_table_level_primary_key(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
5767 self.advance(); self.advance(); let columns = self.parse_paren_ident_list("PRIMARY KEY")?;
5770 Ok(crate::ast::TableConstraint::PrimaryKey {
5771 name: None,
5772 columns,
5773 })
5774 }
5775
5776 fn parse_table_level_unique(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
5777 self.advance(); let mut nulls_not_distinct = false;
5782 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("nulls")) {
5783 let n1 = self.tokens.get(self.pos + 1);
5784 let n2 = self.tokens.get(self.pos + 2);
5785 let is_not = matches!(n1, Some(Token::Not));
5786 let is_distinct = matches!(n2, Some(Token::Distinct));
5787 if is_not && is_distinct {
5788 self.advance(); self.advance(); self.advance(); nulls_not_distinct = true;
5792 } else if matches!(n1, Some(Token::Distinct)) {
5793 self.advance(); self.advance(); }
5796 }
5797 let columns = self.parse_paren_ident_list("UNIQUE")?;
5798 Ok(crate::ast::TableConstraint::Unique {
5799 name: None,
5800 columns,
5801 nulls_not_distinct,
5802 })
5803 }
5804
5805 fn parse_table_level_check(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
5809 self.advance(); if !matches!(self.peek(), Token::LParen) {
5811 return Err(self.err(alloc::format!(
5812 "expected '(' after CHECK, got {:?}",
5813 self.peek()
5814 )));
5815 }
5816 self.advance();
5817 let expr = self.parse_expr(0)?;
5818 if !matches!(self.peek(), Token::RParen) {
5819 return Err(self.err(alloc::format!(
5820 "expected ')' to close CHECK predicate, got {:?}",
5821 self.peek()
5822 )));
5823 }
5824 self.advance();
5825 Ok(crate::ast::TableConstraint::Check { name: None, expr })
5826 }
5827
5828 fn peek_table_level_check_start(&self) -> bool {
5830 matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("check"))
5831 }
5832
5833 fn peek_named_table_constraint_kind(&self) -> Option<NamedTableConstraintKind> {
5838 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
5839 return None;
5840 }
5841 match self.tokens.get(self.pos + 2) {
5844 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("check") => {
5845 Some(NamedTableConstraintKind::Check)
5846 }
5847 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("unique") => {
5848 Some(NamedTableConstraintKind::Unique)
5849 }
5850 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("primary") => {
5851 Some(NamedTableConstraintKind::PrimaryKey)
5852 }
5853 _ => None,
5854 }
5855 }
5856
5857 fn parse_paren_ident_list(&mut self, ctx: &str) -> Result<Vec<String>, ParseError> {
5858 if !matches!(self.peek(), Token::LParen) {
5859 return Err(self.err(alloc::format!(
5860 "expected '(' after {ctx}, got {:?}",
5861 self.peek()
5862 )));
5863 }
5864 self.advance();
5865 let mut out = Vec::new();
5866 loop {
5867 out.push(self.expect_ident_like()?);
5868 match self.peek() {
5869 Token::Comma => {
5870 self.advance();
5871 }
5872 Token::RParen => {
5873 self.advance();
5874 break;
5875 }
5876 other => {
5877 return Err(self.err(alloc::format!(
5878 "expected ',' or ')' in {ctx} list, got {other:?}"
5879 )));
5880 }
5881 }
5882 }
5883 if out.is_empty() {
5884 return Err(self.err(alloc::format!("{ctx} requires at least one column")));
5885 }
5886 Ok(out)
5887 }
5888
5889 fn peek_constraint_or_fk_start(&self) -> bool {
5894 let is_constraint_kw = matches!(
5895 self.peek(),
5896 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
5897 );
5898 let is_foreign_kw = matches!(
5899 self.peek(),
5900 Token::Ident(s) if s.eq_ignore_ascii_case("foreign")
5901 );
5902 is_constraint_kw || is_foreign_kw
5903 }
5904
5905 fn parse_table_level_fk(&mut self) -> Result<ForeignKeyConstraint, ParseError> {
5909 let mut name: Option<String> = None;
5910 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
5911 self.advance();
5912 name = Some(self.expect_ident_like()?);
5913 }
5914 match self.advance() {
5916 Token::Ident(s) if s.eq_ignore_ascii_case("foreign") => {}
5917 other => return Err(self.err(format!("expected FOREIGN, got {other:?}"))),
5918 }
5919 match self.advance() {
5921 Token::Ident(s) if s.eq_ignore_ascii_case("key") => {}
5922 other => return Err(self.err(format!("expected KEY after FOREIGN, got {other:?}"))),
5923 }
5924 if !matches!(self.peek(), Token::LParen) {
5926 return Err(self.err(format!(
5927 "expected '(' after FOREIGN KEY, got {:?}",
5928 self.peek()
5929 )));
5930 }
5931 self.advance();
5932 let mut columns = Vec::new();
5933 loop {
5934 columns.push(self.expect_ident_like()?);
5935 match self.peek() {
5936 Token::Comma => {
5937 self.advance();
5938 }
5939 Token::RParen => {
5940 self.advance();
5941 break;
5942 }
5943 other => {
5944 return Err(self.err(format!(
5945 "expected ',' or ')' in FK column list, got {other:?}"
5946 )));
5947 }
5948 }
5949 }
5950 if columns.is_empty() {
5951 return Err(self.err("FOREIGN KEY requires at least one column".into()));
5952 }
5953 let (parent_table, parent_columns, on_delete, on_update) =
5954 self.parse_references_tail(columns.len())?;
5955 Ok(ForeignKeyConstraint {
5956 name,
5957 columns,
5958 parent_table,
5959 parent_columns,
5960 on_delete,
5961 on_update,
5962 })
5963 }
5964
5965 fn parse_references_tail(
5970 &mut self,
5971 expected_arity: usize,
5972 ) -> Result<(String, Vec<String>, FkAction, FkAction), ParseError> {
5973 match self.advance() {
5974 Token::Ident(s) if s.eq_ignore_ascii_case("references") => {}
5975 other => return Err(self.err(format!("expected REFERENCES, got {other:?}"))),
5976 }
5977 let parent_table = self.expect_ident_like()?;
5978 let mut parent_columns: Vec<String> = Vec::new();
5979 if matches!(self.peek(), Token::LParen) {
5980 self.advance();
5981 loop {
5982 parent_columns.push(self.expect_ident_like()?);
5983 match self.peek() {
5984 Token::Comma => {
5985 self.advance();
5986 }
5987 Token::RParen => {
5988 self.advance();
5989 break;
5990 }
5991 other => {
5992 return Err(self.err(format!(
5993 "expected ',' or ')' in REFERENCES column list, got {other:?}"
5994 )));
5995 }
5996 }
5997 }
5998 }
5999 if !parent_columns.is_empty() && parent_columns.len() != expected_arity {
6000 return Err(self.err(format!(
6001 "FK arity mismatch: {} local column(s) vs {} parent column(s)",
6002 expected_arity,
6003 parent_columns.len()
6004 )));
6005 }
6006 let mut on_delete = FkAction::Restrict;
6019 let mut on_update = FkAction::Restrict;
6020 let mut seen_on_delete = false;
6021 let mut seen_on_update = false;
6022 loop {
6023 let before = self.pos;
6025 self.consume_optional_deferrable_clauses()?;
6026 if self.pos != before {
6027 continue;
6028 }
6029 if !matches!(self.peek(), Token::On) {
6031 break;
6032 }
6033 self.advance();
6034 let which = self.advance();
6035 let action = self.parse_fk_action()?;
6036 match which {
6037 Token::Ident(ref s) if s.eq_ignore_ascii_case("delete") => {
6038 if seen_on_delete {
6039 return Err(self.err("ON DELETE specified twice".into()));
6040 }
6041 seen_on_delete = true;
6042 on_delete = action;
6043 }
6044 Token::Ident(ref s) if s.eq_ignore_ascii_case("update") => {
6045 if seen_on_update {
6046 return Err(self.err("ON UPDATE specified twice".into()));
6047 }
6048 seen_on_update = true;
6049 on_update = action;
6050 }
6051 other => {
6052 return Err(
6053 self.err(format!("expected DELETE or UPDATE after ON, got {other:?}"))
6054 );
6055 }
6056 }
6057 }
6058 Ok((parent_table, parent_columns, on_delete, on_update))
6059 }
6060
6061 fn parse_fk_action(&mut self) -> Result<FkAction, ParseError> {
6064 match self.advance() {
6065 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => Ok(FkAction::Cascade),
6066 Token::Ident(s) if s.eq_ignore_ascii_case("restrict") => Ok(FkAction::Restrict),
6067 Token::Ident(s) if s.eq_ignore_ascii_case("set") => match self.advance() {
6068 Token::Null => Ok(FkAction::SetNull),
6069 Token::Default => Ok(FkAction::SetDefault),
6070 other => Err(self.err(format!(
6071 "expected NULL or DEFAULT after SET in FK action, got {other:?}"
6072 ))),
6073 },
6074 Token::Ident(s) if s.eq_ignore_ascii_case("no") => match self.advance() {
6075 Token::Ident(s) if s.eq_ignore_ascii_case("action") => Ok(FkAction::NoAction),
6076 other => Err(self.err(format!(
6077 "expected ACTION after NO in FK action, got {other:?}"
6078 ))),
6079 },
6080 other => Err(self.err(format!(
6081 "expected CASCADE | RESTRICT | SET NULL | SET DEFAULT | NO ACTION, got {other:?}"
6082 ))),
6083 }
6084 }
6085
6086 fn consume_if_not_exists(&mut self) -> bool {
6089 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
6093 if !looks_like_if {
6094 return false;
6095 }
6096 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Not)) {
6099 return false;
6100 }
6101 if !matches!(
6102 self.tokens.get(self.pos + 2),
6103 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
6104 ) {
6105 return false;
6106 }
6107 self.advance(); self.advance(); self.advance(); true
6111 }
6112
6113 fn consume_if_exists(&mut self) -> bool {
6117 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
6118 if !looks_like_if {
6119 return false;
6120 }
6121 if !matches!(
6122 self.tokens.get(self.pos + 1),
6123 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
6124 ) {
6125 return false;
6126 }
6127 self.advance(); self.advance(); true
6130 }
6131
6132 fn parse_optional_nulls_placement(&mut self) -> Result<Option<bool>, ParseError> {
6140 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("nulls")) {
6141 return Ok(None);
6142 }
6143 self.advance();
6144 match self.advance() {
6145 Token::Ident(s) if s.eq_ignore_ascii_case("first") => Ok(Some(true)),
6146 Token::Ident(s) if s.eq_ignore_ascii_case("last") => Ok(Some(false)),
6147 other => Err(self.err(alloc::format!(
6148 "expected FIRST or LAST after NULLS, got {other:?}"
6149 ))),
6150 }
6151 }
6152
6153 fn consume_optional_index_column_qualifiers(&mut self) {
6154 loop {
6155 match self.peek() {
6156 Token::Asc | Token::Desc => {
6157 self.advance();
6158 }
6159 Token::Ident(s) if s.eq_ignore_ascii_case("nulls") => {
6160 let look = self.tokens.get(self.pos + 1);
6161 if matches!(
6162 look,
6163 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("first")
6164 || k.eq_ignore_ascii_case("last")
6165 ) {
6166 self.advance();
6167 self.advance();
6168 } else {
6169 break;
6170 }
6171 }
6172 _ => break,
6173 }
6174 }
6175 }
6176
6177 fn parse_create_index_stmt_after_create(
6178 &mut self,
6179 is_unique: bool,
6180 ) -> Result<Statement, ParseError> {
6181 debug_assert!(matches!(self.peek(), Token::Index));
6183 self.advance();
6184 let if_not_exists = self.consume_if_not_exists();
6185 let name = self.expect_ident_like()?;
6186 if !matches!(self.peek(), Token::On) {
6187 return Err(self.err(format!(
6188 "expected ON after CREATE INDEX <name>, got {:?}",
6189 self.peek()
6190 )));
6191 }
6192 self.advance();
6193 let table = self.expect_ident_like()?;
6194 let method = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
6199 self.advance();
6200 let m = self.expect_ident_like()?;
6201 match m.to_ascii_lowercase().as_str() {
6202 "hnsw" => IndexMethod::Hnsw,
6203 "btree" => IndexMethod::BTree,
6204 "brin" => IndexMethod::Brin,
6205 "gin" => IndexMethod::Gin,
6210 "gist" | "spgist" | "hash" => IndexMethod::BTree,
6219 "ivfflat" => IndexMethod::Hnsw,
6227 other => {
6228 return Err(self.err(alloc::format!(
6229 "unknown index method {other:?}; supported: hnsw, btree, brin, gin (gist/spgist/hash accepted as BTree fallback)"
6230 )));
6231 }
6232 }
6233 } else {
6234 IndexMethod::BTree
6235 };
6236 if !matches!(self.peek(), Token::LParen) {
6237 return Err(self.err(format!(
6238 "expected '(' before indexed column, got {:?}",
6239 self.peek()
6240 )));
6241 }
6242 self.advance();
6243 let mut opclass: Option<String> = None;
6252 let (column, expression): (String, Option<Expr>) = match self.peek().clone() {
6253 Token::Ident(s) | Token::QuotedIdent(s)
6260 if matches!(
6261 self.tokens.get(self.pos + 1),
6262 Some(Token::RParen | Token::Comma)
6263 ) =>
6264 {
6265 self.advance();
6266 (s, None)
6267 }
6268 Token::Ident(s) | Token::QuotedIdent(s)
6282 if matches!(
6283 self.tokens.get(self.pos + 1),
6284 Some(Token::Ident(_) | Token::QuotedIdent(_))
6285 ) && matches!(self.tokens.get(self.pos + 2), Some(Token::Dot))
6286 && matches!(
6287 self.tokens.get(self.pos + 3),
6288 Some(Token::Ident(op) | Token::QuotedIdent(op))
6289 if is_vector_opclass_name(op)
6290 ) =>
6291 {
6292 self.advance(); self.advance(); self.advance(); let op_tok = self.advance();
6296 if let Token::Ident(op) | Token::QuotedIdent(op) = op_tok {
6297 opclass = Some(op.to_ascii_lowercase());
6298 }
6299 (s, None)
6300 }
6301 Token::Ident(s) | Token::QuotedIdent(s)
6302 if matches!(
6303 self.tokens.get(self.pos + 1),
6304 Some(Token::Ident(op) | Token::QuotedIdent(op))
6305 if is_vector_opclass_name(op)
6306 ) =>
6307 {
6308 self.advance(); let op_tok = self.advance();
6312 if let Token::Ident(op) | Token::QuotedIdent(op) = op_tok {
6313 opclass = Some(op.to_ascii_lowercase());
6314 }
6315 (s, None)
6316 }
6317 Token::Ident(_) | Token::QuotedIdent(_) => {
6318 let key_expr = self.parse_expr(0)?;
6319 let primary = extract_first_column(&key_expr).ok_or_else(|| {
6320 self.err("expression index key must reference at least one column".into())
6321 })?;
6322 (primary, Some(key_expr))
6323 }
6324 Token::LParen => {
6332 let key_expr = self.parse_expr(0)?;
6333 let primary = extract_first_column(&key_expr).ok_or_else(|| {
6334 self.err("expression index key must reference at least one column".into())
6335 })?;
6336 (primary, Some(key_expr))
6337 }
6338 other => {
6339 return Err(self.err(format!(
6340 "expected column ident or expression, got {other:?}"
6341 )));
6342 }
6343 };
6344 let mut extra_columns: Vec<String> = Vec::new();
6353 self.consume_optional_index_column_qualifiers();
6355 while matches!(self.peek(), Token::Comma) {
6356 self.advance();
6357 let extra = self.expect_ident_like()?;
6358 self.consume_optional_index_column_qualifiers();
6359 extra_columns.push(extra);
6360 }
6361 if !matches!(self.peek(), Token::RParen) {
6362 return Err(self.err(format!(
6363 "expected ')' after indexed column / expression, got {:?}",
6364 self.peek()
6365 )));
6366 }
6367 self.advance();
6368 let included_columns = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("include"))
6372 {
6373 self.advance();
6374 if !matches!(self.peek(), Token::LParen) {
6375 return Err(self.err(format!("expected '(' after INCLUDE, got {:?}", self.peek())));
6376 }
6377 self.advance();
6378 let mut cols = Vec::new();
6379 loop {
6380 cols.push(self.expect_ident_like()?);
6381 match self.peek() {
6382 Token::Comma => {
6383 self.advance();
6384 }
6385 Token::RParen => {
6386 self.advance();
6387 break;
6388 }
6389 other => {
6390 return Err(self.err(format!(
6391 "expected ',' or ')' in INCLUDE list, got {other:?}"
6392 )));
6393 }
6394 }
6395 }
6396 cols
6397 } else {
6398 Vec::new()
6399 };
6400 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
6406 self.advance();
6407 if !matches!(self.peek(), Token::LParen) {
6408 return Err(self.err(format!(
6409 "expected '(' after WITH in CREATE INDEX, got {:?}",
6410 self.peek()
6411 )));
6412 }
6413 self.advance();
6414 loop {
6415 if matches!(self.peek(), Token::RParen) {
6416 self.advance();
6417 break;
6418 }
6419 let _ = self.advance(); if matches!(self.peek(), Token::Eq) {
6422 self.advance();
6423 let _ = self.advance(); }
6425 match self.peek() {
6426 Token::Comma => {
6427 self.advance();
6428 }
6429 Token::RParen => {
6430 self.advance();
6431 break;
6432 }
6433 other => {
6434 return Err(self.err(format!(
6435 "expected ',' or ')' in WITH (…) clause, got {other:?}"
6436 )));
6437 }
6438 }
6439 }
6440 }
6441 let partial_predicate = if matches!(self.peek(), Token::Where) {
6443 self.advance();
6444 Some(self.parse_expr(0)?)
6445 } else {
6446 None
6447 };
6448 if is_unique && !matches!(method, IndexMethod::BTree) {
6453 return Err(self.err(alloc::format!(
6454 "UNIQUE is only supported on BTree indexes, got USING {:?}",
6455 method
6456 )));
6457 }
6458 Ok(Statement::CreateIndex(CreateIndexStatement {
6459 name,
6460 table,
6461 column,
6462 method,
6463 if_not_exists,
6464 included_columns,
6465 partial_predicate,
6466 extra_columns: extra_columns.clone(),
6467 expression,
6468 is_unique,
6469 opclass,
6470 }))
6471 }
6472
6473 fn parse_column_def_with_fk(
6478 &mut self,
6479 ) -> Result<(ColumnDef, Option<ForeignKeyConstraint>), ParseError> {
6480 let col = self.parse_column_def()?;
6481 let inline_references = matches!(
6483 self.peek(),
6484 Token::Ident(s) if s.eq_ignore_ascii_case("references")
6485 );
6486 if !inline_references {
6487 return Ok((col, None));
6488 }
6489 let (parent_table, parent_columns, on_delete, on_update) = self.parse_references_tail(1)?;
6490 let fk = ForeignKeyConstraint {
6491 name: None,
6492 columns: vec![col.name.clone()],
6493 parent_table,
6494 parent_columns,
6495 on_delete,
6496 on_update,
6497 };
6498 Ok((col, Some(fk)))
6499 }
6500
6501 fn parse_column_type_name(&mut self) -> Result<ColumnTypeName, ParseError> {
6509 let (ty, _, _, _, _, _, _, _) = self.parse_type_with_implied_flags()?;
6510 Ok(ty)
6511 }
6512
6513 #[allow(clippy::type_complexity)]
6514 fn parse_type_with_implied_flags(
6515 &mut self,
6516 ) -> Result<
6517 (
6518 ColumnTypeName,
6519 bool,
6520 bool,
6521 Option<String>,
6522 Collation,
6523 bool,
6524 Option<Vec<String>>,
6528 Option<Vec<String>>,
6531 ),
6532 ParseError,
6533 > {
6534 let mut ty_ident = match self.advance() {
6535 Token::Ident(s) => s,
6536 Token::Interval => "interval".to_string(),
6542 other => {
6543 return Err(ParseError {
6544 message: format!("expected column type, got {other:?}"),
6545 token_pos: self.pos.saturating_sub(1),
6546 });
6547 }
6548 };
6549 while matches!(self.peek(), Token::Dot) {
6554 self.advance();
6555 ty_ident = self.expect_ident_like()?;
6556 }
6557 let mut implied_auto_increment = false;
6558 let mut implied_not_null = false;
6559 let mut user_type_ref: Option<String> = None;
6560 let mut inline_enum_variants: Option<Vec<String>> = None;
6565 let mut inline_set_variants: Option<Vec<String>> = None;
6567 let mut ty = match ty_ident.as_str() {
6568 "smallserial" | "serial2" => {
6570 implied_auto_increment = true;
6571 implied_not_null = true;
6572 ColumnTypeName::SmallInt
6573 }
6574 "serial" | "serial4" => {
6575 implied_auto_increment = true;
6576 implied_not_null = true;
6577 ColumnTypeName::Int
6578 }
6579 "bigserial" | "serial8" => {
6580 implied_auto_increment = true;
6581 implied_not_null = true;
6582 ColumnTypeName::BigInt
6583 }
6584 "smallint" => {
6590 self.consume_optional_paren_size();
6595 ColumnTypeName::SmallInt
6596 }
6597 "tinyint" => {
6608 let width = self.peek_optional_paren_size_value();
6609 self.consume_optional_paren_size();
6610 if width == Some(1) {
6611 ColumnTypeName::Bool
6612 } else {
6613 ColumnTypeName::SmallInt
6614 }
6615 }
6616 "int" | "integer" | "mediumint" => {
6617 self.consume_optional_paren_size();
6618 ColumnTypeName::Int
6619 }
6620 "bigint" => {
6621 self.consume_optional_paren_size();
6622 ColumnTypeName::BigInt
6623 }
6624 "float" | "double" | "real" => {
6629 if ty_ident.eq_ignore_ascii_case("double")
6630 && matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("precision"))
6631 {
6632 self.advance();
6633 }
6634 ColumnTypeName::Float
6635 }
6636 "float4" | "float8" => ColumnTypeName::Float,
6638 "text" => ColumnTypeName::Text,
6639 "bool" | "boolean" => ColumnTypeName::Bool,
6640 "varchar" => ColumnTypeName::Varchar(self.parse_paren_size("VARCHAR")?),
6641 "char" => ColumnTypeName::Char(self.parse_paren_size("CHAR")?),
6642 "vector" => {
6643 let dim = self.parse_paren_size("VECTOR")?;
6644 let encoding = self.parse_optional_vector_encoding()?;
6645 ColumnTypeName::Vector { dim, encoding }
6646 }
6647 "numeric" => {
6648 let (precision, scale) = self.parse_optional_numeric_params()?;
6649 ColumnTypeName::Numeric(precision, scale)
6650 }
6651 "date" => ColumnTypeName::Date,
6652 "timestamp" | "datetime" => {
6655 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with"))
6661 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("time"))
6662 && matches!(self.tokens.get(self.pos + 2), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("zone"))
6663 {
6664 self.advance(); self.advance(); self.advance(); ColumnTypeName::Timestamptz
6668 } else if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("without"))
6669 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("time"))
6670 && matches!(self.tokens.get(self.pos + 2), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("zone"))
6671 {
6672 self.advance(); self.advance(); self.advance(); ColumnTypeName::Timestamp
6676 } else {
6677 self.consume_optional_paren_size();
6681 ColumnTypeName::Timestamp
6682 }
6683 }
6684 "timestamptz" => ColumnTypeName::Timestamptz,
6688 "json" => ColumnTypeName::Json,
6693 "jsonb" => ColumnTypeName::Jsonb,
6694 "bytea" | "bytes" => ColumnTypeName::Bytes,
6700 "tsvector" => ColumnTypeName::TsVector,
6710 "tsquery" => ColumnTypeName::TsQuery,
6711 "uuid" => ColumnTypeName::Uuid,
6715 "interval" => ColumnTypeName::Interval,
6720 "time" => ColumnTypeName::Time,
6723 "year" => ColumnTypeName::Year,
6726 "timetz" => ColumnTypeName::TimeTz,
6729 "money" => ColumnTypeName::Money,
6732 "int4range" => ColumnTypeName::Range(RangeKindAst::Int4),
6734 "int8range" => ColumnTypeName::Range(RangeKindAst::Int8),
6735 "numrange" => ColumnTypeName::Range(RangeKindAst::Num),
6736 "tsrange" => ColumnTypeName::Range(RangeKindAst::Ts),
6737 "tstzrange" => ColumnTypeName::Range(RangeKindAst::TsTz),
6738 "daterange" => ColumnTypeName::Range(RangeKindAst::Date),
6739 "int4multirange" => ColumnTypeName::Multirange(RangeKindAst::Int4),
6741 "int8multirange" => ColumnTypeName::Multirange(RangeKindAst::Int8),
6742 "nummultirange" => ColumnTypeName::Multirange(RangeKindAst::Num),
6743 "tsmultirange" => ColumnTypeName::Multirange(RangeKindAst::Ts),
6744 "tstzmultirange" => ColumnTypeName::Multirange(RangeKindAst::TsTz),
6745 "datemultirange" => ColumnTypeName::Multirange(RangeKindAst::Date),
6746 "point" => ColumnTypeName::Point,
6748 "lseg" => ColumnTypeName::Lseg,
6749 "path" => ColumnTypeName::Path,
6750 "box" => ColumnTypeName::PgBox,
6751 "polygon" => ColumnTypeName::Polygon,
6752 "line" => ColumnTypeName::Line,
6753 "circle" => ColumnTypeName::Circle,
6754 "inet" => ColumnTypeName::Inet,
6756 "cidr" => ColumnTypeName::Cidr,
6757 "macaddr" => ColumnTypeName::Macaddr,
6758 "macaddr8" => ColumnTypeName::Macaddr8,
6759 "bit" => ColumnTypeName::Bit,
6760 "varbit" => ColumnTypeName::BitVarying,
6761 "xml" => ColumnTypeName::Xml,
6762 "hstore" => ColumnTypeName::Hstore,
6764 "enum" => {
6770 if !matches!(self.peek(), Token::LParen) {
6772 return Err(self.err(alloc::format!(
6773 "expected '(' after ENUM, got {:?}",
6774 self.peek()
6775 )));
6776 }
6777 self.advance();
6778 let mut variants: Vec<String> = Vec::new();
6779 loop {
6780 match self.advance() {
6781 Token::String(s) => variants.push(s),
6782 other => {
6783 return Err(self.err(alloc::format!(
6784 "ENUM(...) expects string literal variants, got {other:?}"
6785 )));
6786 }
6787 }
6788 match self.peek() {
6789 Token::Comma => {
6790 self.advance();
6791 continue;
6792 }
6793 Token::RParen => {
6794 self.advance();
6795 break;
6796 }
6797 other => {
6798 return Err(self.err(alloc::format!(
6799 "expected ',' or ')' in ENUM(...), got {other:?}"
6800 )));
6801 }
6802 }
6803 }
6804 if variants.is_empty() {
6805 return Err(self.err("ENUM(...) must declare at least one variant".into()));
6806 }
6807 inline_enum_variants = Some(variants);
6808 ColumnTypeName::Text
6811 }
6812 "set" => {
6816 if !matches!(self.peek(), Token::LParen) {
6817 return Err(self.err(alloc::format!(
6818 "expected '(' after SET, got {:?}",
6819 self.peek()
6820 )));
6821 }
6822 self.advance();
6823 let mut variants: Vec<String> = Vec::new();
6824 loop {
6825 match self.advance() {
6826 Token::String(s) => variants.push(s),
6827 other => {
6828 return Err(self.err(alloc::format!(
6829 "SET(...) expects string literal variants, got {other:?}"
6830 )));
6831 }
6832 }
6833 match self.peek() {
6834 Token::Comma => {
6835 self.advance();
6836 continue;
6837 }
6838 Token::RParen => {
6839 self.advance();
6840 break;
6841 }
6842 other => {
6843 return Err(self.err(alloc::format!(
6844 "expected ',' or ')' in SET(...), got {other:?}"
6845 )));
6846 }
6847 }
6848 }
6849 if variants.is_empty() {
6850 return Err(self.err("SET(...) must declare at least one variant".into()));
6851 }
6852 inline_set_variants = Some(variants);
6853 ColumnTypeName::Text
6854 }
6855 _other => {
6856 user_type_ref = Some(ty_ident.clone());
6862 ColumnTypeName::Text
6863 }
6864 };
6865 let is_unsigned = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unsigned"))
6875 {
6876 self.advance();
6877 true
6878 } else {
6879 false
6880 };
6881 let mut collation = Collation::Binary;
6897 loop {
6898 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("character"))
6899 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("set"))
6900 {
6901 self.advance(); self.advance(); if matches!(
6904 self.peek(),
6905 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
6906 ) {
6907 self.advance();
6908 }
6909 continue;
6910 }
6911 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("collate")) {
6912 self.advance(); let read_collation_atom = |this: &mut Self| -> Option<alloc::string::String> {
6919 match this.peek().clone() {
6920 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => {
6921 this.advance();
6922 Some(s)
6923 }
6924 Token::Default => {
6925 this.advance();
6926 Some(alloc::string::String::from("default"))
6927 }
6928 _ => None,
6929 }
6930 };
6931 let raw = if let Some(head) = read_collation_atom(self) {
6932 if matches!(self.peek(), Token::Dot) {
6934 self.advance();
6935 let tail = read_collation_atom(self).unwrap_or_default();
6936 alloc::format!("{head}.{tail}")
6937 } else {
6938 head
6939 }
6940 } else {
6941 alloc::string::String::new()
6942 };
6943 if !raw.is_empty() {
6944 let parsed = Collation::from_collation_name(&raw);
6945 if parsed != Collation::Binary {
6951 collation = parsed;
6952 }
6953 }
6954 continue;
6955 }
6956 break;
6957 }
6958 if matches!(self.peek(), Token::LBracket) {
6963 self.advance();
6964 if !matches!(self.peek(), Token::RBracket) {
6965 return Err(self.err(alloc::format!(
6966 "TEXT[] takes no dimension; got {:?}",
6967 self.peek()
6968 )));
6969 }
6970 self.advance();
6971 ty = match ty {
6975 ColumnTypeName::Text => ColumnTypeName::TextArray,
6976 ColumnTypeName::Int => ColumnTypeName::IntArray,
6977 ColumnTypeName::BigInt => ColumnTypeName::BigIntArray,
6978 ColumnTypeName::Interval => ColumnTypeName::IntervalArray,
6981 ColumnTypeName::Bool => ColumnTypeName::BoolArray,
6983 ColumnTypeName::SmallInt => ColumnTypeName::SmallIntArray,
6984 ColumnTypeName::Float => ColumnTypeName::FloatArray,
6985 ColumnTypeName::Numeric(_, _) => ColumnTypeName::NumericArray,
6989 ColumnTypeName::Date => ColumnTypeName::DateArray,
6990 ColumnTypeName::Timestamp => ColumnTypeName::TimestampArray,
6991 ColumnTypeName::Timestamptz => ColumnTypeName::TimestamptzArray,
6992 ColumnTypeName::Uuid => ColumnTypeName::UuidArray,
6993 ColumnTypeName::Json => ColumnTypeName::JsonArray,
6994 ColumnTypeName::Jsonb => ColumnTypeName::JsonbArray,
6995 ColumnTypeName::Bytes => ColumnTypeName::BytesArray,
6996 ColumnTypeName::Varchar(_) => ColumnTypeName::VarcharArray,
7000 ColumnTypeName::Char(_) => ColumnTypeName::CharArray,
7001 ColumnTypeName::Money => ColumnTypeName::MoneyArray,
7004 other => {
7005 return Err(self.err(alloc::format!("{other:?}[] not yet supported")));
7006 }
7007 };
7008 if matches!(self.peek(), Token::LBracket) {
7011 self.advance();
7012 if !matches!(self.peek(), Token::RBracket) {
7013 return Err(self.err(alloc::format!(
7014 "TYPE[][] second dimension takes no size; got {:?}",
7015 self.peek()
7016 )));
7017 }
7018 self.advance();
7019 ty = match ty {
7020 ColumnTypeName::IntArray => ColumnTypeName::IntArray2D,
7021 ColumnTypeName::BigIntArray => ColumnTypeName::BigIntArray2D,
7022 ColumnTypeName::TextArray => ColumnTypeName::TextArray2D,
7023 other => {
7024 return Err(self.err(alloc::format!(
7025 "v7.17 2D arrays support INT[][] / BIGINT[][] / \
7026 TEXT[][] only; got {other:?}"
7027 )));
7028 }
7029 };
7030 }
7031 }
7032 Ok((
7033 ty,
7034 implied_auto_increment,
7035 implied_not_null,
7036 user_type_ref,
7037 collation,
7038 is_unsigned,
7039 inline_enum_variants,
7040 inline_set_variants,
7041 ))
7042 }
7043
7044 fn parse_column_def(&mut self) -> Result<ColumnDef, ParseError> {
7045 if let Token::Ident(s) = self.peek()
7054 && [
7055 "unique",
7056 "primary",
7057 "foreign",
7058 "constraint",
7059 "check",
7060 "references",
7061 "exclude",
7062 ]
7063 .iter()
7064 .any(|kw| s.eq_ignore_ascii_case(kw))
7065 {
7066 return Err(self.err(alloc::format!(
7067 "unexpected reserved keyword '{s}' at start of column definition \
7068 (malformed table constraint?)"
7069 )));
7070 }
7071 let name = self.expect_ident_like()?;
7072 let (
7073 ty,
7074 implied_auto_increment,
7075 implied_not_null,
7076 user_type_ref,
7077 collation,
7078 is_unsigned,
7079 inline_enum_variants,
7080 inline_set_variants,
7081 ) = self.parse_type_with_implied_flags()?;
7082 let mut default: Option<Expr> = None;
7086 let mut nullable = !implied_not_null;
7087 let mut nullability_seen = implied_not_null;
7088 let mut auto_increment = implied_auto_increment;
7089 let mut is_primary_key = false;
7090 let mut is_unique = false;
7091 let mut check: Option<Expr> = None;
7092 let mut on_update_runtime: Option<Expr> = None;
7093 let mut generated_stored_expr: Option<Box<Expr>> = None;
7094 loop {
7095 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
7101 self.advance();
7102 let _name = self.expect_ident_like()?;
7103 continue;
7104 }
7105 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("generated")) {
7115 self.advance();
7116 match self.peek().clone() {
7117 Token::Ident(s) if s.eq_ignore_ascii_case("always") => {
7118 self.advance();
7119 }
7120 Token::By => {
7122 self.advance();
7123 if !matches!(self.peek(), Token::Default) {
7124 return Err(self.err(alloc::format!(
7125 "expected DEFAULT after GENERATED BY, got {:?}",
7126 self.peek()
7127 )));
7128 }
7129 self.advance();
7130 }
7131 other => {
7132 return Err(self.err(alloc::format!(
7133 "expected ALWAYS or BY DEFAULT after GENERATED, got {other:?}"
7134 )));
7135 }
7136 }
7137 if !matches!(self.peek(), Token::As) {
7138 return Err(self.err(alloc::format!(
7139 "expected AS after GENERATED ALWAYS/BY DEFAULT, got {:?}",
7140 self.peek()
7141 )));
7142 }
7143 self.advance();
7144 if matches!(self.peek(), Token::LParen) {
7151 self.advance();
7152 let expr = self.parse_expr(0)?;
7153 if !matches!(self.peek(), Token::RParen) {
7154 return Err(self.err(alloc::format!(
7155 "expected ')' after GENERATED ALWAYS AS (<expr>), got {:?}",
7156 self.peek()
7157 )));
7158 }
7159 self.advance();
7160 let stored = match self.peek() {
7161 Token::Ident(s) | Token::QuotedIdent(s)
7162 if s.eq_ignore_ascii_case("stored") =>
7163 {
7164 self.advance();
7165 true
7166 }
7167 Token::Ident(s) | Token::QuotedIdent(s)
7168 if s.eq_ignore_ascii_case("virtual") =>
7169 {
7170 return Err(self.err(
7171 "GENERATED ALWAYS AS (expr) VIRTUAL is not supported \
7172 at v7.37.7; use STORED"
7173 .into(),
7174 ));
7175 }
7176 other => {
7177 return Err(self.err(alloc::format!(
7178 "expected STORED after GENERATED ALWAYS AS (<expr>), \
7179 got {other:?}"
7180 )));
7181 }
7182 };
7183 let _ = stored; generated_stored_expr = Some(Box::new(expr));
7185 continue;
7186 }
7187 self.expect_keyword_ident("identity")?;
7188 if matches!(self.peek(), Token::LParen) {
7192 let mut depth = 0usize;
7193 loop {
7194 match self.advance() {
7195 Token::LParen => depth += 1,
7196 Token::RParen => {
7197 depth -= 1;
7198 if depth == 0 {
7199 break;
7200 }
7201 }
7202 Token::Eof => {
7203 return Err(self.err(
7204 "unterminated sequence-options parens after IDENTITY".into(),
7205 ));
7206 }
7207 _ => {}
7208 }
7209 }
7210 }
7211 auto_increment = true;
7212 nullable = false;
7214 continue;
7215 }
7216 if matches!(self.peek(), Token::On)
7221 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("update"))
7222 {
7223 self.advance(); self.advance(); let next = self.peek().clone();
7227 match next {
7228 Token::Ident(s) | Token::QuotedIdent(s)
7229 if s.eq_ignore_ascii_case("current_timestamp") =>
7230 {
7231 self.advance();
7232 if matches!(self.peek(), Token::LParen) {
7234 self.advance();
7235 if !matches!(self.peek(), Token::Integer(_)) {
7236 return Err(self.err(alloc::format!(
7237 "expected integer precision inside CURRENT_TIMESTAMP(…), got {:?}",
7238 self.peek()
7239 )));
7240 }
7241 self.advance();
7242 if !matches!(self.peek(), Token::RParen) {
7243 return Err(self.err(alloc::format!(
7244 "expected ')' after CURRENT_TIMESTAMP precision, got {:?}",
7245 self.peek()
7246 )));
7247 }
7248 self.advance();
7249 }
7250 on_update_runtime = Some(Expr::FunctionCall {
7251 name: "now".into(),
7252 args: Vec::new(),
7253 });
7254 continue;
7255 }
7256 other => {
7257 return Err(self.err(alloc::format!(
7258 "v7.17 only supports ON UPDATE CURRENT_TIMESTAMP, got {other:?}"
7259 )));
7260 }
7261 }
7262 }
7263 if matches!(self.peek(), Token::Default) {
7264 if default.is_some() {
7265 return Err(self.err("DEFAULT specified twice".into()));
7266 }
7267 self.advance();
7268 default = Some(self.parse_expr(0)?);
7269 continue;
7270 }
7271 if matches!(self.peek(), Token::Not) {
7272 if nullability_seen {
7273 return Err(self.err("NOT NULL specified twice".into()));
7274 }
7275 self.advance();
7276 if !matches!(self.peek(), Token::Null) {
7277 return Err(self.err(format!(
7278 "expected NULL after NOT in column def, got {:?}",
7279 self.peek()
7280 )));
7281 }
7282 self.advance();
7283 nullable = false;
7284 nullability_seen = true;
7285 continue;
7286 }
7287 if matches!(self.peek(), Token::Null) {
7293 if nullability_seen && !nullable {
7294 return Err(self.err("column declared NOT NULL then NULL — pick one".into()));
7295 }
7296 self.advance();
7297 nullable = true;
7298 nullability_seen = true;
7299 continue;
7300 }
7301 if let Token::Ident(s) = self.peek()
7304 && (s.eq_ignore_ascii_case("auto_increment")
7305 || s.eq_ignore_ascii_case("autoincrement"))
7306 {
7307 if auto_increment {
7308 return Err(self.err("AUTO_INCREMENT specified twice".into()));
7309 }
7310 self.advance();
7311 auto_increment = true;
7312 continue;
7313 }
7314 if let Token::Ident(s) = self.peek()
7319 && s.eq_ignore_ascii_case("primary")
7320 {
7321 if is_primary_key {
7322 return Err(self.err("PRIMARY KEY specified twice".into()));
7323 }
7324 let next = self.tokens.get(self.pos + 1);
7326 let next_is_key = matches!(
7327 next,
7328 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("key")
7329 );
7330 if !next_is_key {
7331 return Err(self.err(format!(
7332 "expected KEY after PRIMARY in column def, got {:?}",
7333 next
7334 )));
7335 }
7336 self.advance(); self.advance(); is_primary_key = true;
7339 if nullability_seen && nullable {
7340 return Err(self.err(
7341 "column declared NULL but inline PRIMARY KEY implies NOT NULL".into(),
7342 ));
7343 }
7344 nullable = false;
7345 nullability_seen = true;
7346 continue;
7347 }
7348 if let Token::Ident(s) = self.peek()
7352 && s.eq_ignore_ascii_case("unique")
7353 {
7354 if is_unique {
7355 return Err(self.err("UNIQUE specified twice".into()));
7356 }
7357 self.advance();
7358 is_unique = true;
7359 continue;
7360 }
7361 if let Token::Ident(s) = self.peek()
7366 && s.eq_ignore_ascii_case("check")
7367 {
7368 self.advance();
7369 if !matches!(self.peek(), Token::LParen) {
7370 return Err(self.err(alloc::format!(
7371 "expected '(' after CHECK in column def, got {:?}",
7372 self.peek()
7373 )));
7374 }
7375 self.advance();
7376 let pred = self.parse_expr(0)?;
7377 if !matches!(self.peek(), Token::RParen) {
7378 return Err(self.err(alloc::format!(
7379 "expected ')' to close CHECK predicate, got {:?}",
7380 self.peek()
7381 )));
7382 }
7383 self.advance();
7384 check = Some(match check.take() {
7385 Some(prev) => Expr::Binary {
7386 op: BinOp::And,
7387 lhs: Box::new(prev),
7388 rhs: Box::new(pred),
7389 },
7390 None => pred,
7391 });
7392 continue;
7393 }
7394 break;
7395 }
7396 Ok(ColumnDef {
7397 name,
7398 ty,
7399 nullable,
7400 default,
7401 auto_increment,
7402 is_primary_key,
7403 is_unique,
7404 check,
7405 user_type_ref,
7406 on_update_runtime,
7407 collation,
7408 is_unsigned,
7409 inline_enum_variants,
7410 inline_set_variants,
7411 generated_stored_expr,
7412 })
7413 }
7414
7415 fn parse_optional_numeric_params(&mut self) -> Result<(u8, u8), ParseError> {
7419 if !matches!(self.peek(), Token::LParen) {
7420 return Ok((0, 0));
7424 }
7425 self.advance();
7426 let precision = match self.advance() {
7427 Token::Integer(n) if (1..=38).contains(&n) => u8::try_from(n).expect("range-checked"),
7428 other => {
7429 return Err(ParseError {
7430 message: format!(
7431 "NUMERIC precision must be an integer in 1..=38, got {other:?}"
7432 ),
7433 token_pos: self.pos.saturating_sub(1),
7434 });
7435 }
7436 };
7437 let scale = if matches!(self.peek(), Token::Comma) {
7438 self.advance();
7439 match self.advance() {
7440 Token::Integer(n) if (0..=i64::from(precision)).contains(&n) => {
7441 u8::try_from(n).expect("range-checked")
7442 }
7443 other => {
7444 return Err(ParseError {
7445 message: format!(
7446 "NUMERIC scale must be a non-negative integer ≤ precision, got {other:?}"
7447 ),
7448 token_pos: self.pos.saturating_sub(1),
7449 });
7450 }
7451 }
7452 } else {
7453 0
7454 };
7455 if !matches!(self.peek(), Token::RParen) {
7456 return Err(self.err(format!(
7457 "expected ')' to close NUMERIC params, got {:?}",
7458 self.peek()
7459 )));
7460 }
7461 self.advance();
7462 Ok((precision, scale))
7463 }
7464
7465 fn parse_optional_vector_encoding(&mut self) -> Result<VecEncoding, ParseError> {
7473 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
7474 return Ok(VecEncoding::F32);
7475 }
7476 let n1 = self.tokens.get(self.pos + 1);
7482 let next_is_encoding = matches!(
7483 n1,
7484 Some(Token::Ident(s))
7485 if s.eq_ignore_ascii_case("sq8") || s.eq_ignore_ascii_case("half")
7486 );
7487 if !next_is_encoding {
7488 return Ok(VecEncoding::F32);
7489 }
7490 self.advance();
7491 let enc_ident = match self.advance() {
7492 Token::Ident(s) => s,
7493 other => {
7494 return Err(self.err(format!(
7495 "expected vector encoding after USING, got {other:?}"
7496 )));
7497 }
7498 };
7499 match enc_ident.to_ascii_lowercase().as_str() {
7500 "sq8" => Ok(VecEncoding::Sq8),
7501 "half" => Ok(VecEncoding::F16),
7504 other => Err(self.err(format!(
7505 "unknown vector encoding {other:?}; supported: SQ8, HALF"
7506 ))),
7507 }
7508 }
7509
7510 fn peek_optional_paren_size_value(&self) -> Option<i64> {
7516 if !matches!(self.peek(), Token::LParen) {
7517 return None;
7518 }
7519 let next = self.tokens.get(self.pos + 1)?;
7520 let n = match next {
7521 Token::Integer(n) => *n,
7522 _ => return None,
7523 };
7524 if !matches!(self.tokens.get(self.pos + 2), Some(Token::RParen)) {
7525 return None;
7526 }
7527 Some(n)
7528 }
7529
7530 fn consume_optional_paren_size(&mut self) {
7534 if !matches!(self.peek(), Token::LParen) {
7535 return;
7536 }
7537 self.advance();
7538 let mut depth = 1usize;
7540 while depth > 0 {
7541 match self.peek() {
7542 Token::LParen => depth += 1,
7543 Token::RParen => depth -= 1,
7544 Token::Eof => return,
7545 _ => {}
7546 }
7547 self.advance();
7548 }
7549 }
7550
7551 fn parse_paren_size(&mut self, label: &str) -> Result<u32, ParseError> {
7552 if !matches!(self.peek(), Token::LParen) {
7553 return Err(self.err(format!("{label} type requires (N), got {:?}", self.peek())));
7554 }
7555 self.advance();
7556 let n = match self.advance() {
7557 Token::Integer(n) if n > 0 => u32::try_from(n).map_err(|_| ParseError {
7558 message: format!("{label} size too large: {n}"),
7559 token_pos: self.pos.saturating_sub(1),
7560 })?,
7561 other => {
7562 return Err(ParseError {
7563 message: format!("expected positive integer {label} size, got {other:?}"),
7564 token_pos: self.pos.saturating_sub(1),
7565 });
7566 }
7567 };
7568 if !matches!(self.peek(), Token::RParen) {
7569 return Err(self.err(format!(
7570 "expected ')' after {label} size, got {:?}",
7571 self.peek()
7572 )));
7573 }
7574 self.advance();
7575 Ok(n)
7576 }
7577
7578 fn parse_insert_stmt(&mut self) -> Result<Statement, ParseError> {
7579 debug_assert!(matches!(self.peek(), Token::Insert));
7580 self.advance();
7581 if !matches!(self.peek(), Token::Into) {
7582 return Err(self.err(format!("expected INTO after INSERT, got {:?}", self.peek())));
7583 }
7584 self.advance();
7585 let table = self.expect_ident_like()?;
7586 let columns = if matches!(self.peek(), Token::LParen) {
7588 self.advance();
7589 let mut names = Vec::new();
7590 loop {
7591 names.push(self.expect_ident_like()?);
7592 match self.peek() {
7593 Token::Comma => {
7594 self.advance();
7595 }
7596 Token::RParen => {
7597 self.advance();
7598 break;
7599 }
7600 other => {
7601 return Err(self.err(format!(
7602 "expected ',' or ')' in INSERT column list, got {other:?}"
7603 )));
7604 }
7605 }
7606 }
7607 Some(names)
7608 } else {
7609 None
7610 };
7611 if matches!(self.peek(), Token::Select) {
7614 let select_stmt = match self.parse_select_stmt()? {
7615 Statement::Select(s) => s,
7616 other => {
7617 return Err(self.err(alloc::format!(
7618 "expected SELECT after INSERT INTO ... target, got {other:?}"
7619 )));
7620 }
7621 };
7622 let on_conflict = self.parse_optional_on_conflict()?;
7623 let returning = self.parse_optional_returning()?;
7624 return Ok(Statement::Insert(InsertStatement {
7625 ctes: Vec::new(),
7626 table,
7627 columns,
7628 rows: Vec::new(),
7629 select_source: Some(Box::new(select_stmt)),
7630 on_conflict,
7631 returning,
7632 }));
7633 }
7634 if !matches!(self.peek(), Token::Values) {
7635 return Err(self.err(format!(
7636 "expected VALUES or SELECT after table name, got {:?}",
7637 self.peek()
7638 )));
7639 }
7640 self.advance();
7641 if !matches!(self.peek(), Token::LParen) {
7642 return Err(self.err(format!("expected '(' after VALUES, got {:?}", self.peek())));
7643 }
7644 let mut rows = Vec::new();
7645 loop {
7646 if !matches!(self.peek(), Token::LParen) {
7648 return Err(self.err(format!(
7649 "expected '(' for next VALUES tuple, got {:?}",
7650 self.peek()
7651 )));
7652 }
7653 self.advance();
7654 let mut tuple = Vec::new();
7655 loop {
7656 tuple.push(self.parse_expr(0)?);
7657 match self.peek() {
7658 Token::Comma => {
7659 self.advance();
7660 }
7661 Token::RParen => {
7662 self.advance();
7663 break;
7664 }
7665 other => {
7666 return Err(self.err(format!(
7667 "expected ',' or ')' in VALUES tuple, got {other:?}"
7668 )));
7669 }
7670 }
7671 }
7672 if tuple.is_empty() {
7673 return Err(self.err("INSERT VALUES tuple requires at least one value".into()));
7674 }
7675 rows.push(tuple);
7676 if matches!(self.peek(), Token::Comma) {
7678 self.advance();
7679 } else {
7680 break;
7681 }
7682 }
7683 let on_conflict = self.parse_optional_on_conflict()?;
7684 let returning = self.parse_optional_returning()?;
7685 Ok(Statement::Insert(InsertStatement {
7686 ctes: Vec::new(),
7687 table,
7688 columns,
7689 rows,
7690 select_source: None,
7691 on_conflict,
7692 returning,
7693 }))
7694 }
7695
7696 fn parse_optional_on_conflict(
7701 &mut self,
7702 ) -> Result<Option<crate::ast::OnConflictClause>, ParseError> {
7703 if !matches!(self.peek(), Token::On) {
7704 return Ok(None);
7705 }
7706 let next_is_conflict = matches!(
7709 self.tokens.get(self.pos + 1),
7710 Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("conflict")
7711 );
7712 if !next_is_conflict {
7713 return Ok(None);
7714 }
7715 self.advance(); self.advance(); let mut target_columns: Vec<String> = Vec::new();
7719 if matches!(self.peek(), Token::LParen) {
7720 self.advance();
7721 loop {
7722 target_columns.push(self.expect_ident_like()?);
7723 match self.peek() {
7724 Token::Comma => {
7725 self.advance();
7726 }
7727 Token::RParen => {
7728 self.advance();
7729 break;
7730 }
7731 other => {
7732 return Err(self.err(alloc::format!(
7733 "expected ',' or ')' in ON CONFLICT target list, got {other:?}"
7734 )));
7735 }
7736 }
7737 }
7738 }
7739 match self.advance() {
7741 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {}
7742 other => {
7743 return Err(self.err(alloc::format!(
7744 "expected DO after ON CONFLICT [(…)], got {other:?}"
7745 )));
7746 }
7747 }
7748 let action = match self.advance() {
7750 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing") => {
7751 crate::ast::OnConflictAction::Nothing
7752 }
7753 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
7754 self.parse_on_conflict_update_action()?
7755 }
7756 other => {
7757 return Err(self.err(alloc::format!(
7758 "expected NOTHING or UPDATE after ON CONFLICT DO, got {other:?}"
7759 )));
7760 }
7761 };
7762 Ok(Some(crate::ast::OnConflictClause {
7763 target_columns,
7764 action,
7765 }))
7766 }
7767
7768 fn parse_on_conflict_update_action(
7772 &mut self,
7773 ) -> Result<crate::ast::OnConflictAction, ParseError> {
7774 match self.advance() {
7776 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {}
7777 other => {
7778 return Err(self.err(alloc::format!(
7779 "expected SET after ON CONFLICT DO UPDATE, got {other:?}"
7780 )));
7781 }
7782 }
7783 let mut assignments: Vec<(String, Expr)> = Vec::new();
7784 loop {
7785 let col = self.expect_ident_like()?;
7786 if !matches!(self.peek(), Token::Eq) {
7787 return Err(self.err(alloc::format!(
7788 "expected `=` after column in ON CONFLICT DO UPDATE SET, got {:?}",
7789 self.peek()
7790 )));
7791 }
7792 self.advance();
7793 let value = self.parse_expr(0)?;
7794 assignments.push((col, value));
7795 if matches!(self.peek(), Token::Comma) {
7796 self.advance();
7797 continue;
7798 }
7799 break;
7800 }
7801 let where_ = if matches!(self.peek(), Token::Where) {
7802 self.advance();
7803 Some(self.parse_expr(0)?)
7804 } else {
7805 None
7806 };
7807 Ok(crate::ast::OnConflictAction::Update {
7808 assignments,
7809 where_,
7810 })
7811 }
7812
7813 fn parse_select_list(&mut self) -> Result<Vec<SelectItem>, ParseError> {
7814 let mut items = Vec::new();
7815 loop {
7816 items.push(self.parse_select_item()?);
7817 if matches!(self.peek(), Token::Comma) {
7818 self.advance();
7819 } else {
7820 break;
7821 }
7822 }
7823 Ok(items)
7824 }
7825
7826 fn parse_select_item(&mut self) -> Result<SelectItem, ParseError> {
7827 if matches!(self.peek(), Token::Star) {
7828 self.advance();
7829 return Ok(SelectItem::Wildcard);
7830 }
7831 let expr = self.parse_expr(0)?;
7832 let alias = self.parse_optional_alias();
7833 Ok(SelectItem::Expr { expr, alias })
7834 }
7835
7836 fn parse_table_ref(&mut self) -> Result<TableRef, ParseError> {
7837 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("lateral"))
7847 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("jsonb_each_text"))
7848 && matches!(self.tokens.get(self.pos + 2), Some(Token::LParen))
7849 {
7850 self.advance(); self.advance(); self.advance(); let arg = self.parse_expr(0)?;
7854 if !matches!(self.peek(), Token::RParen) {
7855 return Err(self.err(alloc::format!(
7856 "expected ')' after LATERAL jsonb_each_text() argument, got {:?}",
7857 self.peek()
7858 )));
7859 }
7860 self.advance();
7861 let (alias_ident, column_aliases) = self.parse_optional_alias_with_columns();
7862 let alias = alias_ident
7863 .clone()
7864 .unwrap_or_else(|| "jsonb_each_text".to_string());
7865 let srf_alias = "__srf__".to_string();
7871 let key_alias = column_aliases
7872 .first()
7873 .cloned()
7874 .unwrap_or_else(|| "key".to_string());
7875 let value_alias = column_aliases
7876 .get(1)
7877 .cloned()
7878 .unwrap_or_else(|| "value".to_string());
7879 let inner_select = crate::ast::SelectStatement {
7880 ctes: Vec::new(),
7881 distinct: false,
7882 items: alloc::vec![
7883 crate::ast::SelectItem::Expr {
7884 expr: crate::ast::Expr::Column(crate::ast::ColumnName {
7885 qualifier: Some(srf_alias.clone()),
7886 name: "key".to_string(),
7887 }),
7888 alias: Some(key_alias),
7889 },
7890 crate::ast::SelectItem::Expr {
7891 expr: crate::ast::Expr::Column(crate::ast::ColumnName {
7892 qualifier: Some(srf_alias.clone()),
7893 name: "value".to_string(),
7894 }),
7895 alias: Some(value_alias),
7896 },
7897 ],
7898 from: Some(crate::ast::FromClause {
7899 primary: TableRef {
7900 name: srf_alias.clone(),
7901 alias: Some(srf_alias.clone()),
7902 as_of_segment: None,
7903 unnest_expr: None,
7904 unnest_column_aliases: Vec::new(),
7905 generate_series_args: None,
7906 lateral_subquery: None,
7907 jsonb_each_text_arg: Some(Box::new(arg)),
7908 },
7909 joins: Vec::new(),
7910 }),
7911 where_: None,
7912 group_by: None,
7913 group_by_all: false,
7914 having: None,
7915 unions: Vec::new(),
7916 order_by: Vec::new(),
7917 limit: None,
7918 offset: None,
7919 limit_with_ties: false,
7920 };
7921 return Ok(TableRef {
7922 name: alias.clone(),
7923 alias: Some(alias),
7924 as_of_segment: None,
7925 unnest_expr: None,
7926 unnest_column_aliases: Vec::new(),
7927 generate_series_args: None,
7928 lateral_subquery: Some(Box::new(inner_select)),
7929 jsonb_each_text_arg: None,
7930 });
7931 }
7932 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("lateral"))
7952 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
7953 {
7954 self.advance(); self.advance(); let inner = match self.parse_one_statement()? {
7958 Statement::Select(s) => s,
7959 other => {
7960 return Err(self.err(alloc::format!(
7961 "expected SELECT inside LATERAL ( … ), got {other:?}"
7962 )));
7963 }
7964 };
7965 if !matches!(self.peek(), Token::RParen) {
7966 return Err(self.err(alloc::format!(
7967 "expected ')' after LATERAL subquery, got {:?}",
7968 self.peek()
7969 )));
7970 }
7971 self.advance();
7972 let alias_ident = self.parse_optional_alias();
7973 let name = alias_ident.clone().unwrap_or_else(|| "lateral".to_string());
7974 return Ok(TableRef {
7975 name,
7976 alias: alias_ident,
7977 as_of_segment: None,
7978 unnest_expr: None,
7979 unnest_column_aliases: Vec::new(),
7980 generate_series_args: None,
7981 lateral_subquery: Some(Box::new(inner)),
7982 jsonb_each_text_arg: None,
7983 });
7984 }
7985 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("jsonb_each_text"))
7993 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
7994 {
7995 self.advance(); self.advance(); let arg = self.parse_expr(0)?;
7998 if !matches!(self.peek(), Token::RParen) {
7999 return Err(self.err(alloc::format!(
8000 "expected ')' after jsonb_each_text() argument, got {:?}",
8001 self.peek()
8002 )));
8003 }
8004 self.advance();
8005 let (alias_ident, _column_aliases) = self.parse_optional_alias_with_columns();
8006 let name = alias_ident
8007 .clone()
8008 .unwrap_or_else(|| "jsonb_each_text".to_string());
8009 return Ok(TableRef {
8010 name,
8011 alias: alias_ident,
8012 as_of_segment: None,
8013 unnest_expr: None,
8014 unnest_column_aliases: Vec::new(),
8015 generate_series_args: None,
8016 lateral_subquery: None,
8017 jsonb_each_text_arg: Some(Box::new(arg)),
8018 });
8019 }
8020 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unnest"))
8024 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
8025 {
8026 self.advance(); self.advance(); let expr = self.parse_expr(0)?;
8029 if !matches!(self.peek(), Token::RParen) {
8030 return Err(self.err(alloc::format!(
8031 "expected ')' after unnest() argument, got {:?}",
8032 self.peek()
8033 )));
8034 }
8035 self.advance();
8036 let (alias_ident, unnest_column_aliases) = self.parse_optional_alias_with_columns();
8037 let name = alias_ident.clone().unwrap_or_else(|| "unnest".to_string());
8038 return Ok(TableRef {
8039 name,
8040 alias: alias_ident,
8041 as_of_segment: None,
8042 unnest_expr: Some(Box::new(expr)),
8043 unnest_column_aliases,
8044 generate_series_args: None,
8045 lateral_subquery: None,
8046 jsonb_each_text_arg: None,
8047 });
8048 }
8049 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("generate_series"))
8059 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
8060 {
8061 self.advance(); self.advance(); let mut args: Vec<Expr> = Vec::new();
8064 loop {
8065 args.push(self.parse_expr(0)?);
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(alloc::format!(
8074 "expected ')' after generate_series() arguments, got {:?}",
8075 self.peek()
8076 )));
8077 }
8078 self.advance();
8079 if args.len() < 2 || args.len() > 3 {
8080 return Err(self.err(alloc::format!(
8081 "generate_series() expects 2 or 3 arguments (start, stop [, step]); got {}",
8082 args.len()
8083 )));
8084 }
8085 let (alias_ident, _column_aliases) = self.parse_optional_alias_with_columns();
8086 let name = alias_ident
8087 .clone()
8088 .unwrap_or_else(|| "generate_series".to_string());
8089 return Ok(TableRef {
8090 name,
8091 alias: alias_ident,
8092 as_of_segment: None,
8093 unnest_expr: None,
8094 unnest_column_aliases: Vec::new(),
8095 generate_series_args: Some(args),
8096 lateral_subquery: None,
8097 jsonb_each_text_arg: None,
8098 });
8099 }
8100 let name = if let Some(synth) = self.try_peek_meta_qualified() {
8109 synth
8110 } else if let Some(synth) = self.try_peek_meta_bare() {
8111 synth
8112 } else {
8113 self.expect_ident_like()?
8114 };
8115 let as_of_segment = if matches!(self.peek(), Token::As)
8121 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("of"))
8122 {
8123 self.advance(); self.advance(); let kw = match self.peek().clone() {
8126 Token::Ident(s) | Token::QuotedIdent(s) => s,
8127 other => {
8128 return Err(self.err(format!("expected SEGMENT after AS OF, got {other:?}")));
8129 }
8130 };
8131 if !kw.eq_ignore_ascii_case("segment") {
8132 return Err(self.err(format!(
8133 "expected SEGMENT after AS OF, got {kw:?}; v6.10.2 supports SEGMENT only"
8134 )));
8135 }
8136 self.advance();
8137 let id = match self.advance() {
8140 Token::String(s) => s
8141 .parse::<u32>()
8142 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
8143 Token::Integer(n) => u32::try_from(n)
8144 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
8145 other => {
8146 return Err(self.err(format!(
8147 "expected segment id literal after AS OF SEGMENT, got {other:?}"
8148 )));
8149 }
8150 };
8151 Some(id)
8152 } else {
8153 None
8154 };
8155 let alias = self.parse_optional_alias();
8156 Ok(TableRef {
8157 name,
8158 alias,
8159 as_of_segment,
8160 unnest_expr: None,
8161 unnest_column_aliases: Vec::new(),
8162 generate_series_args: None,
8163 lateral_subquery: None,
8164 jsonb_each_text_arg: None,
8165 })
8166 }
8167
8168 fn parse_optional_alias_with_columns(&mut self) -> (Option<String>, Vec<String>) {
8174 let alias = self.parse_optional_alias();
8175 if alias.is_none() {
8176 return (None, Vec::new());
8177 }
8178 let mut cols: Vec<String> = Vec::new();
8179 if matches!(self.peek(), Token::LParen) {
8180 self.advance();
8181 while let Token::Ident(s) | Token::QuotedIdent(s) = self.peek().clone() {
8182 self.advance();
8183 cols.push(s);
8184 if matches!(self.peek(), Token::Comma) {
8185 self.advance();
8186 continue;
8187 }
8188 break;
8189 }
8190 if matches!(self.peek(), Token::RParen) {
8191 self.advance();
8192 }
8193 }
8194 (alias, cols)
8195 }
8196
8197 fn parse_from_clause(&mut self) -> Result<FromClause, ParseError> {
8202 let primary = self.parse_table_ref()?;
8203 let mut joins = Vec::new();
8204 loop {
8205 if matches!(self.peek(), Token::Comma) {
8207 self.advance();
8208 let table = self.parse_table_ref()?;
8209 joins.push(FromJoin {
8210 kind: JoinKind::Cross,
8211 table,
8212 on: None,
8213 });
8214 continue;
8215 }
8216 let kind =
8219 match self.peek() {
8220 Token::Inner => {
8221 self.advance();
8222 if !matches!(self.peek(), Token::Join) {
8223 return Err(self
8224 .err(format!("expected JOIN after INNER, got {:?}", self.peek())));
8225 }
8226 self.advance();
8227 JoinKind::Inner
8228 }
8229 Token::Left => {
8230 self.advance();
8231 if matches!(self.peek(), Token::Outer) {
8232 self.advance();
8233 }
8234 if !matches!(self.peek(), Token::Join) {
8235 return Err(self.err(format!(
8236 "expected JOIN after LEFT [OUTER], got {:?}",
8237 self.peek()
8238 )));
8239 }
8240 self.advance();
8241 JoinKind::Left
8242 }
8243 Token::Cross => {
8244 self.advance();
8245 if !matches!(self.peek(), Token::Join) {
8246 return Err(self
8247 .err(format!("expected JOIN after CROSS, got {:?}", self.peek())));
8248 }
8249 self.advance();
8250 JoinKind::Cross
8251 }
8252 Token::Join => {
8253 self.advance();
8254 JoinKind::Inner
8255 }
8256 _ => break,
8257 };
8258 let table = self.parse_table_ref()?;
8259 let using_match = matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("using"));
8269 let on = if matches!(self.peek(), Token::On) {
8270 self.advance();
8271 Some(self.parse_expr(0)?)
8272 } else if using_match {
8273 self.advance();
8274 if !matches!(self.peek(), Token::LParen) {
8275 return Err(self.err(format!(
8276 "expected '(' after USING, got {:?}",
8277 self.peek()
8278 )));
8279 }
8280 self.advance();
8281 let mut cols: Vec<String> = Vec::new();
8282 loop {
8283 match self.peek().clone() {
8284 Token::Ident(s) | Token::QuotedIdent(s) => {
8285 self.advance();
8286 cols.push(s);
8287 }
8288 other => {
8289 return Err(self.err(format!(
8290 "expected column name inside USING (…), got {other:?}"
8291 )));
8292 }
8293 }
8294 match self.peek() {
8295 Token::Comma => {
8296 self.advance();
8297 continue;
8298 }
8299 Token::RParen => {
8300 self.advance();
8301 break;
8302 }
8303 other => {
8304 return Err(self.err(format!(
8305 "expected ',' or ')' inside USING (…), got {other:?}"
8306 )));
8307 }
8308 }
8309 }
8310 if cols.is_empty() {
8311 return Err(self.err("USING (…) requires at least one column".to_string()));
8312 }
8313 let left_qual: String = joins
8317 .last()
8318 .map(|j| {
8319 j.table
8320 .alias
8321 .clone()
8322 .unwrap_or_else(|| j.table.name.clone())
8323 })
8324 .unwrap_or_else(|| {
8325 primary
8326 .alias
8327 .clone()
8328 .unwrap_or_else(|| primary.name.clone())
8329 });
8330 let right_qual = table
8331 .alias
8332 .clone()
8333 .unwrap_or_else(|| table.name.clone());
8334 let mut iter = cols.into_iter().map(|c| Expr::Binary {
8335 lhs: alloc::boxed::Box::new(Expr::Column(crate::ast::ColumnName {
8336 qualifier: Some(left_qual.clone()),
8337 name: c.clone(),
8338 })),
8339 op: crate::ast::BinOp::Eq,
8340 rhs: alloc::boxed::Box::new(Expr::Column(crate::ast::ColumnName {
8341 qualifier: Some(right_qual.clone()),
8342 name: c,
8343 })),
8344 });
8345 let first = iter.next().expect("at least one col");
8346 Some(iter.fold(first, |acc, pred| Expr::Binary {
8347 lhs: alloc::boxed::Box::new(acc),
8348 op: crate::ast::BinOp::And,
8349 rhs: alloc::boxed::Box::new(pred),
8350 }))
8351 } else if kind == JoinKind::Cross {
8352 None
8353 } else {
8354 return Err(self.err(format!(
8355 "expected ON or USING after {:?} JOIN, got {:?}",
8356 kind,
8357 self.peek()
8358 )));
8359 };
8360 joins.push(FromJoin { kind, table, on });
8361 }
8362 Ok(FromClause { primary, joins })
8363 }
8364
8365 fn parse_optional_alias(&mut self) -> Option<String> {
8370 if matches!(self.peek(), Token::As) {
8371 self.advance();
8372 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
8377 return self.expect_ident_like().ok();
8378 }
8379 return None;
8380 }
8381 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
8389 if is_alias_stopword(s) {
8390 return None;
8391 }
8392 return self.expect_ident_like().ok();
8393 }
8394 None
8395 }
8396
8397 fn parse_expr(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
8399 self.enter_nested()?;
8403 let r = self.parse_expr_inner(min_prec);
8404 self.nest_depth -= 1;
8405 r
8406 }
8407
8408 fn parse_expr_inner(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
8409 let mut lhs = self.parse_unary()?;
8410 let mut chain_len = 0usize;
8411 while let Some((op, prec)) = binop_from(self.peek()) {
8412 if prec < min_prec {
8413 break;
8414 }
8415 chain_len += 1;
8419 if chain_len > MAX_BINARY_CHAIN {
8420 return Err(self.err(alloc::format!(
8421 "more than {MAX_BINARY_CHAIN} chained binary operators; rewrite long OR-equality chains as IN (…)"
8422 )));
8423 }
8424 self.advance();
8425 let any_kind = match self.peek() {
8430 Token::All if matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) => {
8431 Some(false)
8432 }
8433 Token::Ident(s) | Token::QuotedIdent(s)
8434 if (s.eq_ignore_ascii_case("any") || s.eq_ignore_ascii_case("all"))
8435 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) =>
8436 {
8437 Some(s.eq_ignore_ascii_case("any"))
8438 }
8439 _ => None,
8440 };
8441 if let Some(is_any) = any_kind {
8442 self.advance(); self.advance(); let arr = self.parse_expr(0)?;
8445 if !matches!(self.peek(), Token::RParen) {
8446 return Err(self.err(alloc::format!(
8447 "expected ')' after ANY/ALL argument, got {:?}",
8448 self.peek()
8449 )));
8450 }
8451 self.advance();
8452 lhs = Expr::AnyAll {
8453 expr: Box::new(lhs),
8454 op,
8455 array: Box::new(arr),
8456 is_any,
8457 };
8458 continue;
8459 }
8460 let rhs = self.parse_expr(prec + 1)?;
8461 lhs = Expr::Binary {
8462 lhs: Box::new(lhs),
8463 op,
8464 rhs: Box::new(rhs),
8465 };
8466 }
8467 Ok(lhs)
8468 }
8469
8470 fn parse_unary(&mut self) -> Result<Expr, ParseError> {
8471 match self.peek() {
8472 Token::Not => {
8473 self.advance();
8474 let e = self.parse_expr(3)?;
8477 Ok(Expr::Unary {
8478 op: UnOp::Not,
8479 expr: Box::new(e),
8480 })
8481 }
8482 Token::Minus => {
8483 self.advance();
8484 let e = self.parse_expr(8)?;
8487 Ok(Expr::Unary {
8488 op: UnOp::Neg,
8489 expr: Box::new(e),
8490 })
8491 }
8492 Token::Tilde => {
8493 self.advance();
8494 let e = self.parse_expr(8)?;
8496 Ok(Expr::Unary {
8497 op: UnOp::BitNot,
8498 expr: Box::new(e),
8499 })
8500 }
8501 _ => self.parse_atom(),
8502 }
8503 }
8504
8505 fn parse_atom(&mut self) -> Result<Expr, ParseError> {
8506 let tok_pos = self.pos;
8507 match self.advance() {
8508 Token::Integer(n) => Ok(Expr::Literal(Literal::Integer(n))),
8509 Token::Float(x) => Ok(Expr::Literal(Literal::Float(x))),
8510 Token::String(s) => Ok(Expr::Literal(Literal::String(s))),
8511 Token::True => Ok(Expr::Literal(Literal::Bool(true))),
8512 Token::False => Ok(Expr::Literal(Literal::Bool(false))),
8513 Token::Null => Ok(Expr::Literal(Literal::Null)),
8514 Token::Placeholder(n) => Ok(Expr::Placeholder(n)),
8518 Token::LParen => {
8519 if matches!(self.peek(), Token::Select) {
8523 let inner = self.parse_select_stmt()?;
8524 match self.advance() {
8525 Token::RParen => {
8526 let Statement::Select(s) = inner else {
8527 unreachable!("parse_select_stmt returns Select")
8528 };
8529 Ok(Expr::ScalarSubquery(Box::new(s)))
8530 }
8531 other => Err(ParseError {
8532 message: format!("expected ')' after scalar subquery, got {other:?}"),
8533 token_pos: self.pos.saturating_sub(1),
8534 }),
8535 }
8536 } else {
8537 let e = self.parse_expr(0)?;
8538 match self.advance() {
8539 Token::RParen => Ok(e),
8540 other => Err(ParseError {
8541 message: format!("expected ')', got {other:?}"),
8542 token_pos: self.pos.saturating_sub(1),
8543 }),
8544 }
8545 }
8546 }
8547 Token::LBracket => self.parse_vector_literal_body(),
8548 Token::Extract => self.parse_extract_atom(),
8549 Token::Interval => self.parse_interval_atom(),
8550 Token::Left if matches!(self.peek(), Token::LParen) => {
8557 self.advance(); let mut args = Vec::new();
8559 if !matches!(self.peek(), Token::RParen) {
8560 loop {
8561 args.push(self.parse_expr(0)?);
8562 match self.peek() {
8563 Token::Comma => {
8564 self.advance();
8565 }
8566 Token::RParen => break,
8567 other => {
8568 return Err(self.err(alloc::format!(
8569 "expected ',' or ')' in left() args, got {other:?}"
8570 )));
8571 }
8572 }
8573 }
8574 }
8575 self.advance(); Ok(Expr::FunctionCall {
8577 name: "left".into(),
8578 args,
8579 })
8580 }
8581 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists") => {
8586 self.parse_exists_atom(false)
8587 }
8588 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("case") => {
8592 self.parse_case_atom()
8593 }
8594 Token::Ident(s) | Token::QuotedIdent(s)
8598 if s.eq_ignore_ascii_case("array") && matches!(self.peek(), Token::LBracket) =>
8599 {
8600 self.advance(); let mut items: Vec<Expr> = Vec::new();
8602 if !matches!(self.peek(), Token::RBracket) {
8603 loop {
8604 items.push(self.parse_expr(0)?);
8605 match self.peek() {
8606 Token::Comma => {
8607 self.advance();
8608 }
8609 Token::RBracket => break,
8610 other => {
8611 return Err(self.err(alloc::format!(
8612 "expected ',' or ']' in ARRAY literal, got {other:?}"
8613 )));
8614 }
8615 }
8616 }
8617 }
8618 self.advance(); Ok(Expr::Array(items))
8620 }
8621 Token::Ident(s) | Token::QuotedIdent(s)
8636 if s.eq_ignore_ascii_case("match") && matches!(self.peek(), Token::LParen) =>
8637 {
8638 self.parse_match_against_atom()
8639 }
8640 Token::Ident(s) | Token::QuotedIdent(s) => self.finish_ident_atom(s),
8641 other if unreserved_keyword_text(&other).is_some() => {
8648 let s = unreserved_keyword_text(&other).unwrap();
8649 self.finish_ident_atom(s)
8650 }
8651 other => Err(ParseError {
8652 message: format!("unexpected token {other:?} in expression"),
8653 token_pos: tok_pos,
8654 }),
8655 }
8656 .and_then(|atom| self.finish_postfix_casts(atom))
8658 }
8659
8660 fn parse_cast_target(&mut self) -> Result<CastTarget, ParseError> {
8665 let target = match self.advance() {
8666 Token::Ident(s) => match s.to_ascii_lowercase().as_str() {
8667 "int" | "integer" | "int4" => {
8668 if matches!(self.peek(), Token::LBracket)
8669 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
8670 {
8671 self.advance();
8672 self.advance();
8673 CastTarget::IntArray
8674 } else {
8675 CastTarget::Int
8676 }
8677 }
8678 "bigint" | "int8" => {
8679 if matches!(self.peek(), Token::LBracket)
8680 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
8681 {
8682 self.advance();
8683 self.advance();
8684 CastTarget::BigIntArray
8685 } else {
8686 CastTarget::BigInt
8687 }
8688 }
8689 "float" | "double" | "real" => CastTarget::Float,
8690 "text" => {
8691 if matches!(self.peek(), Token::LBracket)
8693 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
8694 {
8695 self.advance();
8696 self.advance();
8697 CastTarget::TextArray
8698 } else {
8699 CastTarget::Text
8700 }
8701 }
8702 "bool" | "boolean" => CastTarget::Bool,
8703 "vector" => CastTarget::Vector,
8704 "date" => CastTarget::Date,
8705 "timestamp" | "datetime" => CastTarget::Timestamp,
8706 "timestamptz" => CastTarget::Timestamptz,
8707 "interval" => CastTarget::Interval,
8708 "json" => CastTarget::Json,
8709 "jsonb" => CastTarget::Jsonb,
8710 "regtype" => CastTarget::RegType,
8711 "regclass" => CastTarget::RegClass,
8712 "tsvector" => CastTarget::TsVector,
8716 "tsquery" => CastTarget::TsQuery,
8717 "uuid" => CastTarget::Uuid,
8720 "bytea" => CastTarget::Bytea,
8725 other => {
8734 let mut name = other.to_string();
8740 if matches!(self.peek(), Token::LParen) {
8741 let mut buf = alloc::string::String::from("(");
8742 let mut depth = 0usize;
8743 loop {
8744 match self.advance() {
8745 Token::LParen => {
8746 depth += 1;
8747 if depth > 1 {
8748 buf.push('(');
8749 }
8750 }
8751 Token::RParen => {
8752 depth -= 1;
8753 if depth == 0 {
8754 buf.push(')');
8755 break;
8756 }
8757 buf.push(')');
8758 }
8759 Token::Comma => buf.push(','),
8760 Token::Integer(n) => buf.push_str(&alloc::format!("{n}")),
8761 Token::Eof => break,
8762 _ => {}
8763 }
8764 }
8765 name.push_str(&buf);
8766 }
8767 if matches!(self.peek(), Token::LBracket)
8772 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
8773 {
8774 self.advance();
8775 self.advance();
8776 name.push_str("_array");
8777 }
8778 CastTarget::Named(name)
8779 }
8780 },
8781 Token::Interval => CastTarget::Interval,
8782 other => {
8783 return Err(ParseError {
8784 message: format!("expected type ident after `::`, got {other:?}"),
8785 token_pos: self.pos.saturating_sub(1),
8786 });
8787 }
8788 };
8789 if matches!(self.peek(), Token::LBracket)
8798 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
8799 {
8800 let widened = match &target {
8801 CastTarget::Bool => Some(CastTarget::Named("bool_array".to_string())),
8802 CastTarget::Date => Some(CastTarget::Named("date_array".to_string())),
8803 CastTarget::Timestamp | CastTarget::Timestamptz => {
8804 Some(CastTarget::Named("timestamptz_array".to_string()))
8805 }
8806 CastTarget::Uuid => Some(CastTarget::Named("uuid_array".to_string())),
8807 CastTarget::Json | CastTarget::Jsonb => {
8808 Some(CastTarget::Named("jsonb_array".to_string()))
8809 }
8810 CastTarget::Bytea => Some(CastTarget::Named("bytea_array".to_string())),
8811 CastTarget::Interval => Some(CastTarget::Named("interval_array".to_string())),
8812 CastTarget::Float => Some(CastTarget::Named("float_array".to_string())),
8813 CastTarget::Named(name) => {
8814 let mut a = name.clone();
8815 a.push_str("_array");
8816 Some(CastTarget::Named(a))
8817 }
8818 _ => None,
8822 };
8823 if let Some(w) = widened {
8824 self.advance();
8825 self.advance();
8826 return Ok(w);
8827 }
8828 }
8829 Ok(target)
8830 }
8831
8832 fn finish_postfix_casts(&mut self, mut expr: Expr) -> Result<Expr, ParseError> {
8833 loop {
8834 if matches!(self.peek(), Token::DoubleColon) {
8835 self.advance();
8836 let target = self.parse_cast_target()?;
8841 expr = Expr::Cast {
8842 expr: Box::new(expr),
8843 target,
8844 };
8845 continue;
8846 }
8847 if matches!(self.peek(), Token::Is) {
8848 self.advance();
8849 let negated = if matches!(self.peek(), Token::Not) {
8850 self.advance();
8851 true
8852 } else {
8853 false
8854 };
8855 if matches!(self.peek(), Token::Distinct) {
8858 self.advance();
8859 if !matches!(self.peek(), Token::From) {
8860 return Err(self.err(format!(
8861 "expected FROM after IS{} DISTINCT, got {:?}",
8862 if negated { " NOT" } else { "" },
8863 self.peek()
8864 )));
8865 }
8866 self.advance();
8867 let rhs = self.parse_expr(20)?;
8871 let op = if negated {
8872 BinOp::IsNotDistinctFrom
8873 } else {
8874 BinOp::IsDistinctFrom
8875 };
8876 expr = Expr::Binary {
8877 op,
8878 lhs: Box::new(expr),
8879 rhs: Box::new(rhs),
8880 };
8881 continue;
8882 }
8883 if !matches!(self.peek(), Token::Null) {
8884 return Err(self.err(format!(
8885 "expected NULL or DISTINCT after IS{}, got {:?}",
8886 if negated { " NOT" } else { "" },
8887 self.peek()
8888 )));
8889 }
8890 self.advance();
8891 expr = Expr::IsNull {
8892 expr: Box::new(expr),
8893 negated,
8894 };
8895 continue;
8896 }
8897 let negated = if matches!(self.peek(), Token::Not) {
8901 let next = self.tokens.get(self.pos + 1);
8902 matches!(next, Some(Token::Between | Token::In | Token::Like))
8903 || matches!(next, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("ilike"))
8904 } else {
8905 false
8906 };
8907 if negated {
8908 self.advance();
8909 }
8910 if matches!(self.peek(), Token::Between) {
8911 expr = self.parse_between_tail(expr, negated)?;
8912 continue;
8913 }
8914 if matches!(self.peek(), Token::In) {
8915 expr = self.parse_in_tail(expr, negated)?;
8916 continue;
8917 }
8918 if matches!(self.peek(), Token::Like) {
8919 self.advance();
8920 let pattern = self.parse_expr(5)?;
8923 expr = Expr::Like {
8924 expr: Box::new(expr),
8925 pattern: Box::new(pattern),
8926 negated,
8927 case_insensitive: false,
8928 };
8929 continue;
8930 }
8931 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("ilike")) {
8934 self.advance();
8935 let pattern = self.parse_expr(5)?;
8936 expr = Expr::Like {
8937 expr: Box::new(expr),
8938 pattern: Box::new(pattern),
8939 negated,
8940 case_insensitive: true,
8941 };
8942 continue;
8943 }
8944 if matches!(self.peek(), Token::LBracket) {
8948 self.advance();
8949 let index = self.parse_expr(0)?;
8950 if !matches!(self.peek(), Token::RBracket) {
8951 return Err(self.err(alloc::format!(
8952 "expected ']' after array index, got {:?}",
8953 self.peek()
8954 )));
8955 }
8956 self.advance();
8957 expr = Expr::ArraySubscript {
8958 target: Box::new(expr),
8959 index: Box::new(index),
8960 };
8961 continue;
8962 }
8963 return Ok(expr);
8964 }
8965 }
8966
8967 fn parse_between_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
8971 self.advance(); let low = self.parse_expr(5)?;
8973 if !matches!(self.peek(), Token::And) {
8974 return Err(self.err(format!(
8975 "expected AND after BETWEEN low bound, got {:?}",
8976 self.peek()
8977 )));
8978 }
8979 self.advance();
8980 let high = self.parse_expr(5)?;
8981 let target = Box::new(expr);
8982 let combined = Expr::Binary {
8983 lhs: Box::new(Expr::Binary {
8984 lhs: target.clone(),
8985 op: BinOp::GtEq,
8986 rhs: Box::new(low),
8987 }),
8988 op: BinOp::And,
8989 rhs: Box::new(Expr::Binary {
8990 lhs: target,
8991 op: BinOp::LtEq,
8992 rhs: Box::new(high),
8993 }),
8994 };
8995 Ok(maybe_not(combined, negated))
8996 }
8997
8998 fn parse_with_cte_then_select(&mut self) -> Result<Statement, ParseError> {
9003 let mut recursive = false;
9008 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
9009 && s.eq_ignore_ascii_case("recursive")
9010 {
9011 self.advance();
9012 recursive = true;
9013 }
9014 let mut ctes = Vec::new();
9015 loop {
9016 let name = self.expect_ident_like()?;
9017 let column_overrides: Vec<String> = if matches!(self.peek(), Token::LParen) {
9021 self.advance();
9022 let mut names = Vec::new();
9023 loop {
9024 names.push(self.expect_ident_like()?);
9025 if matches!(self.peek(), Token::Comma) {
9026 self.advance();
9027 continue;
9028 }
9029 break;
9030 }
9031 if !matches!(self.peek(), Token::RParen) {
9032 return Err(self.err(format!(
9033 "expected ')' to close CTE column list, got {:?}",
9034 self.peek()
9035 )));
9036 }
9037 self.advance();
9038 names
9039 } else {
9040 Vec::new()
9041 };
9042 if !matches!(self.peek(), Token::As) {
9046 return Err(self.err(format!(
9047 "expected AS after CTE name {name:?}, got {:?}",
9048 self.peek()
9049 )));
9050 }
9051 self.advance();
9052 if !matches!(self.peek(), Token::LParen) {
9053 return Err(self.err(format!(
9054 "expected '(' after AS in WITH clause, got {:?}",
9055 self.peek()
9056 )));
9057 }
9058 self.advance();
9059 let is_update_kw = matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update"));
9066 let is_delete_kw = matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete"));
9067 let body = match self.peek() {
9068 Token::Select => {
9069 let inner = self.parse_select_stmt()?;
9070 let Statement::Select(s) = inner else {
9071 unreachable!("parse_select_stmt returns Select");
9072 };
9073 crate::ast::CteBody::Select(s)
9074 }
9075 Token::Insert => {
9076 let inner = self.parse_one_statement()?;
9077 let Statement::Insert(s) = inner else {
9078 unreachable!("Token::Insert routes to Insert");
9079 };
9080 crate::ast::CteBody::Insert(alloc::boxed::Box::new(s))
9081 }
9082 _ if is_update_kw => {
9083 let inner = self.parse_one_statement()?;
9084 let Statement::Update(s) = inner else {
9085 return Err(
9086 self.err(format!("expected UPDATE inside WITH (…), got {inner:?}"))
9087 );
9088 };
9089 crate::ast::CteBody::Update(alloc::boxed::Box::new(s))
9090 }
9091 _ if is_delete_kw => {
9092 let inner = self.parse_one_statement()?;
9093 let Statement::Delete(s) = inner else {
9094 return Err(
9095 self.err(format!("expected DELETE inside WITH (…), got {inner:?}"))
9096 );
9097 };
9098 crate::ast::CteBody::Delete(alloc::boxed::Box::new(s))
9099 }
9100 other => {
9101 return Err(self.err(format!(
9102 "WITH body must be SELECT / INSERT / UPDATE / DELETE, got {other:?}"
9103 )));
9104 }
9105 };
9106 if !matches!(self.peek(), Token::RParen) {
9107 return Err(self.err(format!(
9108 "expected ')' after CTE body, got {:?}",
9109 self.peek()
9110 )));
9111 }
9112 self.advance();
9113 ctes.push(crate::ast::Cte {
9114 name,
9115 body,
9116 recursive,
9117 column_overrides,
9118 });
9119 if matches!(self.peek(), Token::Comma) {
9120 self.advance();
9121 continue;
9122 }
9123 break;
9124 }
9125 let outer_is_update = matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update"));
9129 let outer_is_delete = matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete"));
9130 match self.peek() {
9131 Token::Select => {
9132 let body_stmt = self.parse_select_stmt()?;
9133 let Statement::Select(mut body) = body_stmt else {
9134 unreachable!()
9135 };
9136 body.ctes = ctes;
9137 Ok(Statement::Select(body))
9138 }
9139 Token::Insert => {
9140 let body_stmt = self.parse_one_statement()?;
9141 let Statement::Insert(mut body) = body_stmt else {
9142 unreachable!()
9143 };
9144 body.ctes = ctes;
9145 Ok(Statement::Insert(body))
9146 }
9147 _ if outer_is_update => {
9148 let body_stmt = self.parse_one_statement()?;
9149 let Statement::Update(mut body) = body_stmt else {
9150 return Err(self.err(format!("expected UPDATE after WITH clause")));
9151 };
9152 body.ctes = ctes;
9153 Ok(Statement::Update(body))
9154 }
9155 _ if outer_is_delete => {
9156 let body_stmt = self.parse_one_statement()?;
9157 let Statement::Delete(mut body) = body_stmt else {
9158 return Err(self.err(format!("expected DELETE after WITH clause")));
9159 };
9160 body.ctes = ctes;
9161 Ok(Statement::Delete(body))
9162 }
9163 other => Err(self.err(format!(
9164 "expected SELECT / INSERT / UPDATE / DELETE after WITH clause, got {other:?}"
9165 ))),
9166 }
9167 }
9168
9169 fn parse_case_atom(&mut self) -> Result<Expr, ParseError> {
9178 let operand = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("when")) {
9182 None
9183 } else {
9184 Some(Box::new(self.parse_expr(0)?))
9185 };
9186 let mut branches: Vec<(Expr, Expr)> = Vec::new();
9187 loop {
9188 match self.peek() {
9189 Token::Ident(s) if s.eq_ignore_ascii_case("when") => {
9190 self.advance();
9191 let cond = self.parse_expr(0)?;
9192 match self.peek() {
9193 Token::Ident(t) if t.eq_ignore_ascii_case("then") => {
9194 self.advance();
9195 }
9196 other => {
9197 return Err(self.err(alloc::format!(
9198 "expected THEN after CASE WHEN <expr>, got {other:?}"
9199 )));
9200 }
9201 }
9202 let value = self.parse_expr(0)?;
9203 branches.push((cond, value));
9204 }
9205 _ => break,
9206 }
9207 }
9208 if branches.is_empty() {
9209 return Err(self.err("CASE requires at least one WHEN … THEN … branch".into()));
9210 }
9211 let else_branch = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("else"))
9212 {
9213 self.advance();
9214 Some(Box::new(self.parse_expr(0)?))
9215 } else {
9216 None
9217 };
9218 match self.peek() {
9219 Token::Ident(s) if s.eq_ignore_ascii_case("end") => {
9220 self.advance();
9221 }
9222 other => {
9223 return Err(self.err(alloc::format!(
9224 "expected END to close CASE expression, got {other:?}"
9225 )));
9226 }
9227 }
9228 Ok(Expr::Case {
9229 operand,
9230 branches,
9231 else_branch,
9232 })
9233 }
9234
9235 fn parse_exists_atom(&mut self, negated: bool) -> Result<Expr, ParseError> {
9236 if !matches!(self.peek(), Token::LParen) {
9237 return Err(self.err(format!("expected '(' after EXISTS, got {:?}", self.peek())));
9238 }
9239 self.advance();
9240 let inner = self.parse_select_stmt()?;
9241 if !matches!(self.peek(), Token::RParen) {
9242 return Err(self.err(format!(
9243 "expected ')' after EXISTS-subquery, got {:?}",
9244 self.peek()
9245 )));
9246 }
9247 self.advance();
9248 let Statement::Select(s) = inner else {
9249 unreachable!("parse_select_stmt returns Select")
9250 };
9251 Ok(Expr::Exists {
9252 subquery: Box::new(s),
9253 negated,
9254 })
9255 }
9256
9257 fn parse_in_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
9258 self.advance(); if !matches!(self.peek(), Token::LParen) {
9260 return Err(self.err(format!("expected '(' after IN, got {:?}", self.peek())));
9261 }
9262 self.advance();
9263 if matches!(self.peek(), Token::Select) {
9265 let inner = self.parse_select_stmt()?;
9266 if !matches!(self.peek(), Token::RParen) {
9267 return Err(self.err(format!(
9268 "expected ')' after IN-subquery, got {:?}",
9269 self.peek()
9270 )));
9271 }
9272 self.advance();
9273 let Statement::Select(s) = inner else {
9274 unreachable!("parse_select_stmt always returns Statement::Select")
9275 };
9276 return Ok(Expr::InSubquery {
9277 expr: Box::new(expr),
9278 subquery: Box::new(s),
9279 negated,
9280 });
9281 }
9282 let mut elements = Vec::new();
9283 if !matches!(self.peek(), Token::RParen) {
9284 loop {
9285 elements.push(self.parse_expr(0)?);
9286 match self.peek() {
9287 Token::Comma => {
9288 self.advance();
9289 }
9290 Token::RParen => break,
9291 other => {
9292 return Err(
9293 self.err(format!("expected ',' or ')' in IN list, got {other:?}"))
9294 );
9295 }
9296 }
9297 }
9298 }
9299 self.advance(); if elements.is_empty() {
9304 return Ok(maybe_not(Expr::Literal(Literal::Bool(false)), negated));
9305 }
9306 Ok(Expr::InList {
9307 expr: Box::new(expr),
9308 list: elements,
9309 negated,
9310 })
9311 }
9312
9313 fn parse_match_against_atom(&mut self) -> Result<Expr, ParseError> {
9334 if !matches!(self.peek(), Token::LParen) {
9337 return Err(self.err(alloc::format!(
9338 "expected '(' after MATCH, got {:?}",
9339 self.peek()
9340 )));
9341 }
9342 self.advance();
9343 let mut cols: Vec<Expr> = Vec::new();
9344 loop {
9345 cols.push(self.parse_expr(0)?);
9346 match self.peek() {
9347 Token::Comma => {
9348 self.advance();
9349 }
9350 Token::RParen => break,
9351 other => {
9352 return Err(self.err(alloc::format!(
9353 "expected ',' or ')' in MATCH column list, got {other:?}"
9354 )));
9355 }
9356 }
9357 }
9358 self.advance(); match self.peek() {
9361 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("against") => {
9362 self.advance();
9363 }
9364 other => {
9365 return Err(self.err(alloc::format!(
9366 "expected AGAINST after MATCH column list, got {other:?}"
9367 )));
9368 }
9369 }
9370 if !matches!(self.peek(), Token::LParen) {
9371 return Err(self.err(alloc::format!(
9372 "expected '(' after AGAINST, got {:?}",
9373 self.peek()
9374 )));
9375 }
9376 self.advance();
9377 let term = match self.advance() {
9387 Token::String(s) => Expr::Literal(crate::ast::Literal::String(s)),
9388 Token::Placeholder(n) => Expr::Placeholder(n),
9389 Token::Ident(s) | Token::QuotedIdent(s) => Expr::Column(crate::ast::ColumnName {
9390 qualifier: None,
9391 name: s,
9392 }),
9393 other => {
9394 return Err(self.err(alloc::format!(
9395 "MATCH ... AGAINST(<term>) expects a string literal, \
9396 bound parameter, or column ref, got {other:?}"
9397 )));
9398 }
9399 };
9400 loop {
9405 match self.peek() {
9406 Token::In => {
9409 self.advance();
9410 }
9411 Token::Ident(s) | Token::QuotedIdent(s)
9412 if s.eq_ignore_ascii_case("natural")
9413 || s.eq_ignore_ascii_case("language")
9414 || s.eq_ignore_ascii_case("boolean")
9415 || s.eq_ignore_ascii_case("mode")
9416 || s.eq_ignore_ascii_case("with")
9417 || s.eq_ignore_ascii_case("query")
9418 || s.eq_ignore_ascii_case("expansion") =>
9419 {
9420 self.advance();
9421 }
9422 _ => break,
9423 }
9424 }
9425 if !matches!(self.peek(), Token::RParen) {
9426 return Err(self.err(alloc::format!(
9427 "expected ')' to close AGAINST, got {:?}",
9428 self.peek()
9429 )));
9430 }
9431 self.advance();
9432 let simple_lit = || Expr::Literal(crate::ast::Literal::String(String::from("simple")));
9435 let plainto = Expr::FunctionCall {
9436 name: String::from("plainto_tsquery"),
9437 args: alloc::vec![simple_lit(), term.clone()],
9438 };
9439 let mut folded: Option<Expr> = None;
9440 for col in cols {
9441 let to_tsv = Expr::FunctionCall {
9442 name: String::from("to_tsvector"),
9443 args: alloc::vec![simple_lit(), col],
9444 };
9445 let leaf = Expr::Binary {
9446 lhs: Box::new(to_tsv),
9447 op: crate::ast::BinOp::TsMatch,
9448 rhs: Box::new(plainto.clone()),
9449 };
9450 folded = Some(match folded {
9451 None => leaf,
9452 Some(prev) => Expr::Binary {
9453 lhs: Box::new(prev),
9454 op: crate::ast::BinOp::Or,
9455 rhs: Box::new(leaf),
9456 },
9457 });
9458 }
9459 match folded {
9460 Some(e) => Ok(e),
9461 None => Err(self.err(String::from(
9462 "MATCH(...) AGAINST(...) requires at least one column",
9463 ))),
9464 }
9465 }
9466
9467 fn parse_extract_atom(&mut self) -> Result<Expr, ParseError> {
9468 if !matches!(self.peek(), Token::LParen) {
9469 return Err(self.err(format!("expected '(' after EXTRACT, got {:?}", self.peek())));
9470 }
9471 self.advance();
9472 let field_name = self.expect_ident_like()?;
9473 let field = match field_name.to_ascii_lowercase().as_str() {
9474 "year" => ExtractField::Year,
9475 "month" => ExtractField::Month,
9476 "day" => ExtractField::Day,
9477 "hour" => ExtractField::Hour,
9478 "minute" => ExtractField::Minute,
9479 "second" => ExtractField::Second,
9480 "microsecond" | "microseconds" => ExtractField::Microsecond,
9481 "epoch" => ExtractField::Epoch,
9482 other => {
9483 return Err(self.err(format!(
9484 "unknown EXTRACT field {other:?}; \
9485 supported: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MICROSECOND, EPOCH"
9486 )));
9487 }
9488 };
9489 if !matches!(self.peek(), Token::From) {
9490 return Err(self.err(format!(
9491 "expected FROM after EXTRACT field, got {:?}",
9492 self.peek()
9493 )));
9494 }
9495 self.advance();
9496 let source = self.parse_expr(0)?;
9497 if !matches!(self.peek(), Token::RParen) {
9498 return Err(self.err(format!(
9499 "expected ')' to close EXTRACT, got {:?}",
9500 self.peek()
9501 )));
9502 }
9503 self.advance();
9504 Ok(Expr::Extract {
9505 field,
9506 source: Box::new(source),
9507 })
9508 }
9509
9510 fn parse_interval_atom(&mut self) -> Result<Expr, ParseError> {
9515 let tok = self.advance();
9516 let Token::String(text) = tok else {
9517 return Err(self.err(format!(
9518 "expected string literal after INTERVAL, got {tok:?}"
9519 )));
9520 };
9521 let (months, days, micros) = parse_interval_text(&text).ok_or_else(|| ParseError {
9522 message: format!(
9523 "cannot parse INTERVAL {text:?}; \
9524 expected `<n> <unit> [<n> <unit> ...]` with units \
9525 microsecond[s], millisecond[s], second[s], minute[s], \
9526 hour[s], day[s], week[s], month[s], year[s]"
9527 ),
9528 token_pos: self.pos.saturating_sub(1),
9529 })?;
9530 Ok(Expr::Literal(Literal::Interval {
9531 months,
9532 days,
9533 micros,
9534 text,
9535 }))
9536 }
9537
9538 fn parse_vector_literal_body(&mut self) -> Result<Expr, ParseError> {
9539 let mut elems = Vec::new();
9540 if matches!(self.peek(), Token::RBracket) {
9541 self.advance();
9542 return Ok(Expr::Literal(Literal::Vector(elems)));
9543 }
9544 loop {
9545 let e = self.parse_expr(0)?;
9546 let x = extract_numeric_literal(&e).ok_or_else(|| ParseError {
9547 message: format!("vector element must be a numeric literal, got {e:?}"),
9548 token_pos: self.pos,
9549 })?;
9550 elems.push(x);
9551 match self.peek() {
9552 Token::Comma => {
9553 self.advance();
9554 }
9555 Token::RBracket => {
9556 self.advance();
9557 break;
9558 }
9559 other => {
9560 return Err(self.err(format!("expected ',' or ']' in vector, got {other:?}")));
9561 }
9562 }
9563 }
9564 Ok(Expr::Literal(Literal::Vector(elems)))
9565 }
9566
9567 fn parse_null_treatment_modifier(&mut self) -> NullTreatment {
9576 let Token::Ident(s) = self.peek().clone() else {
9577 return NullTreatment::Respect;
9578 };
9579 let is_ignore = s.eq_ignore_ascii_case("ignore");
9580 let is_respect = s.eq_ignore_ascii_case("respect");
9581 if !is_ignore && !is_respect {
9582 return NullTreatment::Respect;
9583 }
9584 if self.pos + 1 < self.tokens.len()
9587 && let Token::Ident(s2) = &self.tokens[self.pos + 1]
9588 && s2.eq_ignore_ascii_case("nulls")
9589 {
9590 self.advance();
9591 self.advance();
9592 return if is_ignore {
9593 NullTreatment::Ignore
9594 } else {
9595 NullTreatment::Respect
9596 };
9597 }
9598 NullTreatment::Respect
9599 }
9600
9601 fn parse_filter_clause(&mut self) -> Result<Option<Box<Expr>>, ParseError> {
9606 let (Token::Ident(s) | Token::QuotedIdent(s)) = self.peek() else {
9607 return Ok(None);
9608 };
9609 if !s.eq_ignore_ascii_case("filter") {
9610 return Ok(None);
9611 }
9612 self.advance(); if !matches!(self.peek(), Token::LParen) {
9614 return Err(self.err(format!("expected '(' after FILTER, got {:?}", self.peek())));
9615 }
9616 self.advance(); if !matches!(self.peek(), Token::Where) {
9618 return Err(self.err(format!(
9619 "expected WHERE inside FILTER (...), got {:?}",
9620 self.peek()
9621 )));
9622 }
9623 self.advance(); let cond = self.parse_expr(0)?;
9625 if !matches!(self.peek(), Token::RParen) {
9626 return Err(self.err(format!(
9627 "expected ')' to close FILTER (WHERE ...), got {:?}",
9628 self.peek()
9629 )));
9630 }
9631 self.advance(); Ok(Some(Box::new(cond)))
9633 }
9634
9635 fn parse_within_group_clause(&mut self) -> Result<Vec<OrderBy>, ParseError> {
9640 let (Token::Ident(s) | Token::QuotedIdent(s)) = self.peek() else {
9641 return Ok(Vec::new());
9642 };
9643 if !s.eq_ignore_ascii_case("within") {
9644 return Ok(Vec::new());
9645 }
9646 self.advance(); if !matches!(self.peek(), Token::Group) {
9648 return Err(self.err(format!(
9649 "expected GROUP after WITHIN, got {:?}",
9650 self.peek()
9651 )));
9652 }
9653 self.advance(); if !matches!(self.peek(), Token::LParen) {
9655 return Err(self.err(format!(
9656 "expected '(' after WITHIN GROUP, got {:?}",
9657 self.peek()
9658 )));
9659 }
9660 self.advance(); if !matches!(self.peek(), Token::Order) {
9662 return Err(self.err(format!(
9663 "expected ORDER BY inside WITHIN GROUP (...), got {:?}",
9664 self.peek()
9665 )));
9666 }
9667 self.advance(); if !matches!(self.peek(), Token::By) {
9669 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
9670 }
9671 self.advance(); let mut keys: Vec<OrderBy> = Vec::new();
9673 loop {
9674 let expr = self.parse_expr(0)?;
9675 let desc = if matches!(self.peek(), Token::Desc) {
9676 self.advance();
9677 true
9678 } else if matches!(self.peek(), Token::Asc) {
9679 self.advance();
9680 false
9681 } else {
9682 false
9683 };
9684 let nulls_first = self.parse_optional_nulls_placement()?;
9685 keys.push(OrderBy {
9686 expr,
9687 desc,
9688 nulls_first,
9689 });
9690 if matches!(self.peek(), Token::Comma) {
9691 self.advance();
9692 } else {
9693 break;
9694 }
9695 }
9696 if !matches!(self.peek(), Token::RParen) {
9697 return Err(self.err(format!(
9698 "expected ')' to close WITHIN GROUP (ORDER BY ...), got {:?}",
9699 self.peek()
9700 )));
9701 }
9702 self.advance(); Ok(keys)
9704 }
9705
9706 #[allow(clippy::type_complexity)] fn parse_over_clause(
9709 &mut self,
9710 ) -> Result<
9711 (
9712 Vec<Expr>,
9713 Vec<(Expr, bool, Option<bool>)>,
9714 Option<WindowFrame>,
9715 ),
9716 ParseError,
9717 > {
9718 if !matches!(self.peek(), Token::LParen) {
9719 return Err(self.err(format!("expected '(' after OVER, got {:?}", self.peek())));
9720 }
9721 self.advance();
9722 let mut partition_by = Vec::new();
9723 let mut order_by = Vec::new();
9724 let is_partition_kw = match self.peek() {
9730 Token::Partition => true,
9731 Token::Ident(s) | Token::QuotedIdent(s) => s.eq_ignore_ascii_case("partition"),
9732 _ => false,
9733 };
9734 if is_partition_kw {
9735 self.advance();
9736 if !matches!(self.peek(), Token::By) {
9737 return Err(self.err(format!(
9738 "expected BY after PARTITION, got {:?}",
9739 self.peek()
9740 )));
9741 }
9742 self.advance();
9743 loop {
9744 partition_by.push(self.parse_expr(0)?);
9745 if matches!(self.peek(), Token::Comma) {
9746 self.advance();
9747 continue;
9748 }
9749 break;
9750 }
9751 }
9752 if matches!(self.peek(), Token::Order) {
9754 self.advance();
9755 if !matches!(self.peek(), Token::By) {
9756 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
9757 }
9758 self.advance();
9759 loop {
9760 let e = self.parse_expr(0)?;
9761 let desc = if matches!(self.peek(), Token::Desc) {
9762 self.advance();
9763 true
9764 } else if matches!(self.peek(), Token::Asc) {
9765 self.advance();
9766 false
9767 } else {
9768 false
9769 };
9770 let nulls_first = self.parse_optional_nulls_placement()?;
9772 order_by.push((e, desc, nulls_first));
9773 if matches!(self.peek(), Token::Comma) {
9774 self.advance();
9775 continue;
9776 }
9777 break;
9778 }
9779 }
9780 let mut frame: Option<WindowFrame> = None;
9784 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
9785 let kind = if s.eq_ignore_ascii_case("rows") {
9786 Some(FrameKind::Rows)
9787 } else if s.eq_ignore_ascii_case("range") {
9788 Some(FrameKind::Range)
9789 } else {
9790 None
9791 };
9792 if let Some(kind) = kind {
9793 self.advance();
9794 frame = Some(self.parse_frame_tail(kind)?);
9795 }
9796 }
9797 if !matches!(self.peek(), Token::RParen) {
9798 return Err(self.err(format!(
9799 "expected ')' to close OVER clause, got {:?}",
9800 self.peek()
9801 )));
9802 }
9803 self.advance();
9804 Ok((partition_by, order_by, frame))
9805 }
9806
9807 fn parse_frame_tail(&mut self, kind: FrameKind) -> Result<WindowFrame, ParseError> {
9813 if matches!(self.peek(), Token::Between) {
9814 self.advance();
9815 let start = self.parse_frame_bound()?;
9816 if !matches!(self.peek(), Token::And) {
9817 return Err(self.err(format!("expected AND in frame spec, got {:?}", self.peek())));
9818 }
9819 self.advance();
9820 let end = self.parse_frame_bound()?;
9821 Ok(WindowFrame {
9822 kind,
9823 start,
9824 end: Some(end),
9825 })
9826 } else {
9827 let start = self.parse_frame_bound()?;
9828 Ok(WindowFrame {
9829 kind,
9830 start,
9831 end: None,
9832 })
9833 }
9834 }
9835
9836 fn parse_frame_bound(&mut self) -> Result<FrameBound, ParseError> {
9839 if let Token::Integer(n) = *self.peek() {
9841 self.advance();
9842 let n: u64 = u64::try_from(n).map_err(|_| {
9843 self.err(format!(
9844 "invalid frame offset {n} — expected non-negative integer"
9845 ))
9846 })?;
9847 let dir = self.expect_ident_like()?;
9848 return if dir.eq_ignore_ascii_case("preceding") {
9849 Ok(FrameBound::OffsetPreceding(n))
9850 } else if dir.eq_ignore_ascii_case("following") {
9851 Ok(FrameBound::OffsetFollowing(n))
9852 } else {
9853 Err(self.err(format!(
9854 "expected PRECEDING or FOLLOWING after offset, got {dir:?}"
9855 )))
9856 };
9857 }
9858 let first = self.expect_ident_like()?;
9859 if first.eq_ignore_ascii_case("unbounded") {
9860 let dir = self.expect_ident_like()?;
9861 return if dir.eq_ignore_ascii_case("preceding") {
9862 Ok(FrameBound::UnboundedPreceding)
9863 } else if dir.eq_ignore_ascii_case("following") {
9864 Ok(FrameBound::UnboundedFollowing)
9865 } else {
9866 Err(self.err(format!(
9867 "expected PRECEDING or FOLLOWING after UNBOUNDED, got {dir:?}"
9868 )))
9869 };
9870 }
9871 if first.eq_ignore_ascii_case("current") {
9872 let row = self.expect_ident_like()?;
9873 if !row.eq_ignore_ascii_case("row") {
9874 return Err(self.err(format!("expected ROW after CURRENT, got {row:?}")));
9875 }
9876 return Ok(FrameBound::CurrentRow);
9877 }
9878 Err(self.err(format!(
9879 "expected frame bound (UNBOUNDED/CURRENT/<n>), got {first:?}"
9880 )))
9881 }
9882
9883 fn finish_ident_atom(&mut self, first: String) -> Result<Expr, ParseError> {
9884 if matches!(self.peek(), Token::Dot) {
9885 self.advance();
9886 let name = self.expect_ident_like()?;
9887 if matches!(self.peek(), Token::LParen) {
9893 return self.finish_ident_atom(name);
9894 }
9895 return Ok(Expr::Column(ColumnName {
9896 qualifier: Some(first),
9897 name,
9898 }));
9899 }
9900 if matches!(self.peek(), Token::LParen) {
9901 self.advance();
9902 if first.eq_ignore_ascii_case("count") && matches!(self.peek(), Token::Star) {
9906 self.advance();
9907 if !matches!(self.peek(), Token::RParen) {
9908 return Err(self.err(format!(
9909 "expected ')' after COUNT(*), got {:?}",
9910 self.peek()
9911 )));
9912 }
9913 self.advance();
9914 let filter = self.parse_filter_clause()?;
9916 let null_treatment = self.parse_null_treatment_modifier();
9918 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
9919 && s.eq_ignore_ascii_case("over")
9920 {
9921 if filter.is_some() {
9922 return Err(
9923 self.err("FILTER on window functions is not supported yet".into())
9924 );
9925 }
9926 self.advance();
9927 let (partition_by, order_by, frame) = self.parse_over_clause()?;
9928 return Ok(Expr::WindowFunction {
9929 name: "count_star".into(),
9930 args: Vec::new(),
9931 partition_by,
9932 order_by,
9933 frame,
9934 null_treatment,
9935 });
9936 }
9937 if let Some(filter) = filter {
9938 return Ok(Expr::AggregateOrdered {
9939 call: Box::new(Expr::FunctionCall {
9940 name: "count_star".into(),
9941 args: Vec::new(),
9942 }),
9943 order_by: Vec::new(),
9944 distinct: false,
9945 filter: Some(filter),
9946 });
9947 }
9948 return Ok(Expr::FunctionCall {
9949 name: "count_star".into(),
9950 args: Vec::new(),
9951 });
9952 }
9953 let mut args = Vec::new();
9955 let mut agg_order_by: Vec<OrderBy> = Vec::new();
9956 let agg_distinct = if matches!(self.peek(), Token::Distinct) {
9960 self.advance();
9961 true
9962 } else if matches!(self.peek(), Token::All) {
9963 self.advance();
9964 false
9965 } else {
9966 false
9967 };
9968 if !matches!(self.peek(), Token::RParen) {
9969 loop {
9970 args.push(self.parse_expr(0)?);
9971 if first.eq_ignore_ascii_case("cast")
9975 && args.len() == 1
9976 && matches!(self.peek(), Token::As)
9977 {
9978 self.advance();
9979 let target = self.parse_cast_target()?;
9980 if !matches!(self.peek(), Token::RParen) {
9981 return Err(self.err(format!(
9982 "expected ')' to close CAST, got {:?}",
9983 self.peek()
9984 )));
9985 }
9986 self.advance();
9987 return Ok(Expr::Cast {
9988 expr: Box::new(args.pop().expect("one arg")),
9989 target,
9990 });
9991 }
9992 if (first.eq_ignore_ascii_case("substring")
9998 || first.eq_ignore_ascii_case("substr"))
9999 && args.len() == 1
10000 && matches!(self.peek(), Token::From)
10001 {
10002 self.advance();
10003 let start = self.parse_expr(0)?;
10004 args.push(start);
10005 if matches!(self.peek(), Token::For) {
10006 self.advance();
10007 let length = self.parse_expr(0)?;
10008 args.push(length);
10009 }
10010 if !matches!(self.peek(), Token::RParen) {
10011 return Err(self.err(format!(
10012 "expected ')' to close substring(... FROM ... [FOR ...]), got {:?}",
10013 self.peek()
10014 )));
10015 }
10016 self.advance();
10017 return Ok(Expr::FunctionCall {
10018 name: first.to_ascii_lowercase(),
10019 args,
10020 });
10021 }
10022 if matches!(self.peek(), Token::Order) {
10026 self.advance();
10027 if !matches!(self.peek(), Token::By) {
10028 return Err(self.err(format!(
10029 "expected BY after ORDER in aggregate args, got {:?}",
10030 self.peek()
10031 )));
10032 }
10033 self.advance();
10034 loop {
10035 let expr = self.parse_expr(0)?;
10036 let desc = if matches!(self.peek(), Token::Desc) {
10037 self.advance();
10038 true
10039 } else if matches!(self.peek(), Token::Asc) {
10040 self.advance();
10041 false
10042 } else {
10043 false
10044 };
10045 let nulls_first = self.parse_optional_nulls_placement()?;
10046 agg_order_by.push(OrderBy {
10047 expr,
10048 desc,
10049 nulls_first,
10050 });
10051 if matches!(self.peek(), Token::Comma) {
10052 self.advance();
10053 } else {
10054 break;
10055 }
10056 }
10057 if !matches!(self.peek(), Token::RParen) {
10058 return Err(self.err(format!(
10059 "expected ')' after aggregate ORDER BY, got {:?}",
10060 self.peek()
10061 )));
10062 }
10063 break;
10064 }
10065 match self.peek() {
10066 Token::Comma => {
10067 self.advance();
10068 }
10069 Token::RParen => break,
10070 other => {
10071 return Err(self.err(format!(
10072 "expected ',' or ')' in function args, got {other:?}"
10073 )));
10074 }
10075 }
10076 }
10077 }
10078 self.advance(); let within_group_order = self.parse_within_group_clause()?;
10087 if !within_group_order.is_empty() && !agg_order_by.is_empty() {
10088 return Err(self.err(
10089 "an aggregate may not carry both an in-argument ORDER BY and WITHIN GROUP"
10090 .into(),
10091 ));
10092 }
10093 let agg_order_by = if within_group_order.is_empty() {
10094 agg_order_by
10095 } else {
10096 within_group_order
10097 };
10098 let filter = self.parse_filter_clause()?;
10100 let null_treatment = self.parse_null_treatment_modifier();
10107 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
10108 && s.eq_ignore_ascii_case("over")
10109 {
10110 if filter.is_some() {
10111 return Err(self.err("FILTER on window functions is not supported yet".into()));
10112 }
10113 self.advance();
10114 let (partition_by, order_by, frame) = self.parse_over_clause()?;
10115 return Ok(Expr::WindowFunction {
10116 name: first,
10117 args,
10118 partition_by,
10119 order_by,
10120 frame,
10121 null_treatment,
10122 });
10123 }
10124 if !agg_order_by.is_empty() || agg_distinct || filter.is_some() {
10125 return Ok(Expr::AggregateOrdered {
10126 call: Box::new(Expr::FunctionCall { name: first, args }),
10127 order_by: agg_order_by,
10128 distinct: agg_distinct,
10129 filter,
10130 });
10131 }
10132 return Ok(Expr::FunctionCall { name: first, args });
10133 }
10134 let lc = first.to_ascii_lowercase();
10140 if matches!(
10141 lc.as_str(),
10142 "current_date" | "current_time" | "current_timestamp" | "localtimestamp" | "localtime"
10143 ) {
10144 return Ok(Expr::FunctionCall {
10145 name: lc,
10146 args: Vec::new(),
10147 });
10148 }
10149 Ok(Expr::Column(ColumnName {
10150 qualifier: None,
10151 name: first,
10152 }))
10153 }
10154}
10155
10156fn extract_first_column(expr: &Expr) -> Option<String> {
10164 match expr {
10165 Expr::Column(cn) => Some(cn.name.clone()),
10166 Expr::FunctionCall { args, .. } => args.iter().find_map(extract_first_column),
10167 Expr::Binary { lhs, rhs, .. } => {
10168 extract_first_column(lhs).or_else(|| extract_first_column(rhs))
10169 }
10170 Expr::Unary { expr: e, .. } => extract_first_column(e),
10171 _ => None,
10172 }
10173}
10174
10175fn maybe_not(expr: Expr, negated: bool) -> Expr {
10176 if negated {
10177 Expr::Unary {
10178 op: UnOp::Not,
10179 expr: Box::new(expr),
10180 }
10181 } else {
10182 expr
10183 }
10184}
10185
10186fn binop_from(tok: &Token) -> Option<(BinOp, u8)> {
10187 let pair = match tok {
10188 Token::Or => (BinOp::Or, 1),
10189 Token::And => (BinOp::And, 2),
10190 Token::Eq => (BinOp::Eq, 4),
10191 Token::NotEq => (BinOp::NotEq, 4),
10192 Token::Lt => (BinOp::Lt, 4),
10193 Token::LtEq => (BinOp::LtEq, 4),
10194 Token::Gt => (BinOp::Gt, 4),
10195 Token::GtEq => (BinOp::GtEq, 4),
10196 Token::L2Distance => (BinOp::L2Distance, 5),
10199 Token::InnerProduct => (BinOp::InnerProduct, 5),
10200 Token::CosineDistance => (BinOp::CosineDistance, 5),
10201 Token::Plus => (BinOp::Add, 6),
10202 Token::Minus => (BinOp::Sub, 6),
10203 Token::Concat => (BinOp::Concat, 6),
10206 Token::Pipe => (BinOp::BitOr, 6),
10219 Token::Amp => (BinOp::BitAnd, 6),
10220 Token::Star => (BinOp::Mul, 7),
10221 Token::Slash => (BinOp::Div, 7),
10222 Token::Percent => (BinOp::Mod, 7),
10223 Token::JsonGet => (BinOp::JsonGet, 7),
10227 Token::JsonGetText => (BinOp::JsonGetText, 7),
10228 Token::JsonGetPath => (BinOp::JsonGetPath, 7),
10229 Token::JsonGetPathText => (BinOp::JsonGetPathText, 7),
10230 Token::JsonContains => (BinOp::JsonContains, 7),
10231 Token::JsonContainedBy => (BinOp::JsonContainedBy, 7),
10232 Token::JsonKeyExists => (BinOp::JsonKeyExists, 7),
10233 Token::JsonKeysAny => (BinOp::JsonKeysAny, 7),
10234 Token::JsonKeysAll => (BinOp::JsonKeysAll, 7),
10235 Token::TsMatch => (BinOp::TsMatch, 4),
10239 Token::InetContainedBy => (BinOp::InetContainedBy, 4),
10243 Token::InetContainedByEq => (BinOp::InetContainedByEq, 4),
10244 Token::InetContains => (BinOp::InetContains, 4),
10245 Token::InetContainsEq => (BinOp::InetContainsEq, 4),
10246 Token::InetOverlap => (BinOp::InetOverlap, 4),
10247 _ => return None,
10248 };
10249 Some(pair)
10250}
10251
10252#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
10253fn is_alias_stopword(s: &str) -> bool {
10265 matches!(
10266 s.to_ascii_lowercase().as_str(),
10267 "with"
10268 | "on"
10269 | "where"
10270 | "having"
10271 | "group"
10272 | "order"
10273 | "limit"
10274 | "offset"
10275 | "union"
10276 | "except"
10277 | "intersect"
10278 | "returning"
10279 | "set"
10280 | "values"
10281 | "for"
10282 | "lateral"
10283 | "left"
10284 | "right"
10285 | "inner"
10286 | "outer"
10287 | "full"
10288 | "cross"
10289 | "join"
10290 | "natural"
10291 | "using"
10292 | "fetch"
10293 )
10294}
10295
10296fn extract_numeric_literal(e: &Expr) -> Option<f32> {
10297 match e {
10298 Expr::Literal(Literal::Integer(n)) => Some(*n as f32),
10299 Expr::Literal(Literal::Float(x)) => Some(*x as f32),
10300 Expr::Unary {
10301 op: UnOp::Neg,
10302 expr,
10303 } => extract_numeric_literal(expr).map(|x| -x),
10304 _ => None,
10305 }
10306}
10307
10308pub fn parse_interval_text(s: &str) -> Option<(i32, i32, i64)> {
10320 let parts: Vec<&str> = s.split_whitespace().collect();
10321 if parts.is_empty() || !parts.len().is_multiple_of(2) {
10322 return None;
10323 }
10324 let mut months: i32 = 0;
10325 let mut days: i32 = 0;
10326 let mut micros: i64 = 0;
10327 let mut i = 0;
10328 while i < parts.len() {
10329 let n: i64 = parts[i].parse().ok()?;
10330 let unit = parts[i + 1].to_ascii_lowercase();
10331 let unit_stripped = unit.strip_suffix('s').unwrap_or(&unit);
10332 match unit_stripped {
10333 "microsecond" => micros = micros.checked_add(n)?,
10334 "millisecond" => micros = micros.checked_add(n.checked_mul(1_000)?)?,
10335 "second" => micros = micros.checked_add(n.checked_mul(1_000_000)?)?,
10336 "minute" => micros = micros.checked_add(n.checked_mul(60_000_000)?)?,
10337 "hour" => micros = micros.checked_add(n.checked_mul(3_600_000_000)?)?,
10338 "day" => {
10339 let n32 = i32::try_from(n).ok()?;
10340 days = days.checked_add(n32)?;
10341 }
10342 "week" => {
10343 let n32 = i32::try_from(n).ok()?;
10344 days = days.checked_add(n32.checked_mul(7)?)?;
10345 }
10346 "month" | "mon" => {
10350 let n32 = i32::try_from(n).ok()?;
10351 months = months.checked_add(n32)?;
10352 }
10353 "year" => {
10354 let n32 = i32::try_from(n).ok()?;
10355 months = months.checked_add(n32.checked_mul(12)?)?;
10356 }
10357 _ => return None,
10358 }
10359 i += 2;
10360 }
10361 Some((months, days, micros))
10362}
10363
10364fn map_type_ident_to_column_type_name(ident: &str) -> Option<ColumnTypeName> {
10375 Some(match ident.to_ascii_lowercase().as_str() {
10376 "smallint" | "tinyint" => ColumnTypeName::SmallInt,
10377 "int" | "integer" | "mediumint" => ColumnTypeName::Int,
10378 "bigint" => ColumnTypeName::BigInt,
10379 "float" | "double" | "real" => ColumnTypeName::Float,
10380 "text" => ColumnTypeName::Text,
10381 "bool" | "boolean" => ColumnTypeName::Bool,
10382 "date" => ColumnTypeName::Date,
10383 "timestamp" | "datetime" => ColumnTypeName::Timestamp,
10384 "timestamptz" => ColumnTypeName::Timestamptz,
10385 "json" => ColumnTypeName::Json,
10386 "jsonb" => ColumnTypeName::Jsonb,
10387 "bytea" | "bytes" => ColumnTypeName::Bytes,
10388 "tsvector" => ColumnTypeName::TsVector,
10389 "tsquery" => ColumnTypeName::TsQuery,
10390 "uuid" => ColumnTypeName::Uuid,
10391 "interval" => ColumnTypeName::Interval,
10392 "time" => ColumnTypeName::Time,
10393 "year" => ColumnTypeName::Year,
10394 "timetz" => ColumnTypeName::TimeTz,
10395 "money" => ColumnTypeName::Money,
10396 _ => return None,
10397 })
10398}
10399
10400pub fn parse_function_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
10427 parse_plpgsql_body(body)
10428}
10429
10430fn parse_plpgsql_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
10431 let tokens = lexer::tokenize(body).map_err(|e| ParseError {
10435 message: alloc::format!("plpgsql body lex error: {e}"),
10436 token_pos: 0,
10437 })?;
10438 let mut parser = Parser::new(tokens);
10439 parser.parse_plpgsql_block()
10440}
10441
10442#[cfg(test)]
10443mod tests {
10444 use super::*;
10445 use alloc::string::ToString;
10446
10447 fn parse(s: &str) -> Statement {
10448 parse_statement(s).expect("parse ok")
10449 }
10450
10451 #[test]
10461 fn release_usable_as_column_name_in_create_table() {
10462 let stmt =
10463 parse("CREATE TABLE events (id INT PRIMARY KEY, release TEXT NOT NULL, payload TEXT)");
10464 if let Statement::CreateTable(t) = stmt {
10465 let names: alloc::vec::Vec<&str> = t.columns.iter().map(|c| c.name.as_str()).collect();
10466 assert_eq!(names, alloc::vec!["id", "release", "payload"]);
10467 } else {
10468 panic!("expected CreateTable");
10469 }
10470 }
10471
10472 #[test]
10473 fn release_usable_as_column_ref_in_select_projection() {
10474 parse("SELECT id, release, payload FROM events WHERE id = 1");
10479 }
10480
10481 #[test]
10482 fn release_usable_as_column_ref_in_insert_column_list() {
10483 parse("INSERT INTO events (id, release, payload) VALUES (1, '1.0.0', 'data')");
10485 }
10486
10487 #[test]
10488 fn alter_column_drop_not_null_uses_keyword_drop_token() {
10489 parse("ALTER TABLE audit_logs ALTER COLUMN org_id DROP NOT NULL");
10494 }
10495
10496 #[test]
10497 fn create_index_accepts_parenthesised_expression_key() {
10498 parse(
10504 "CREATE INDEX IF NOT EXISTS events_bundle_id_idx \
10505 ON events ((payload->'bundle'->>'id'))",
10506 );
10507 }
10508
10509 #[test]
10513 fn nesting_budget_errors_cleanly() {
10514 let depth = MAX_NEST_DEPTH + 50;
10515 let sql = format!("SELECT {}1{}", "(".repeat(depth), ")".repeat(depth));
10516 let err = parse_statement(&sql).expect_err("must reject");
10517 assert!(err.message.contains("nests deeper"), "{err:?}");
10518 let sql = format!("SELECT {}1{}", "(".repeat(48), ")".repeat(48));
10520 parse(&sql);
10521 }
10522
10523 #[test]
10524 fn binary_chain_budget_errors_cleanly() {
10525 let sql = format!("SELECT 1{}", " + 1".repeat(MAX_BINARY_CHAIN + 50));
10526 let err = parse_statement(&sql).expect_err("must reject");
10527 assert!(err.message.contains("chained binary"), "{err:?}");
10528 let sql = format!("SELECT 1{}", " + 1".repeat(200));
10531 parse(&sql);
10532 }
10533
10534 #[test]
10535 fn in_list_unaffected_by_chain_budget() {
10536 let items: alloc::vec::Vec<String> = (0..20_000).map(|k| k.to_string()).collect();
10538 let sql = format!("SELECT 1 WHERE 5 IN ({})", items.join(","));
10539 let Statement::Select(s) = parse(&sql) else {
10540 panic!("expected select")
10541 };
10542 let Some(Expr::InList { list, negated, .. }) = s.where_ else {
10543 panic!("expected flat InList, got {:?}", s.where_)
10544 };
10545 assert_eq!(list.len(), 20_000);
10546 assert!(!negated);
10547 }
10548
10549 fn lit_int(n: i64) -> Expr {
10550 Expr::Literal(Literal::Integer(n))
10551 }
10552
10553 fn col(name: &str) -> Expr {
10554 Expr::Column(ColumnName {
10555 qualifier: None,
10556 name: name.into(),
10557 })
10558 }
10559
10560 #[test]
10561 fn select_single_integer() {
10562 let s = parse("SELECT 1");
10563 let Statement::Select(s) = s else {
10564 panic!("expected SELECT")
10565 };
10566 assert_eq!(s.items.len(), 1);
10567 assert!(s.from.is_none());
10568 assert!(s.where_.is_none());
10569 }
10570
10571 #[test]
10572 fn select_multiple_literal_kinds() {
10573 let s = parse("SELECT 1, 'hi', NULL, TRUE, 1.5");
10574 let Statement::Select(s) = s else {
10575 panic!("expected SELECT")
10576 };
10577 assert_eq!(s.items.len(), 5);
10578 }
10579
10580 #[test]
10581 fn select_wildcard_from_table() {
10582 let s = parse("SELECT * FROM users");
10583 let Statement::Select(s) = s else {
10584 panic!("expected SELECT")
10585 };
10586 assert!(matches!(s.items[..], [SelectItem::Wildcard]));
10587 assert_eq!(s.from.as_ref().unwrap().primary.name, "users");
10588 }
10589
10590 #[test]
10591 fn select_with_table_alias() {
10592 let s = parse("SELECT * FROM users AS u");
10593 let Statement::Select(s) = s else {
10594 panic!("expected SELECT")
10595 };
10596 let t = &s.from.as_ref().unwrap().primary;
10597 assert_eq!(t.name, "users");
10598 assert_eq!(t.alias.as_deref(), Some("u"));
10599 }
10600
10601 #[test]
10602 fn select_with_where_eq() {
10603 let s = parse("SELECT a FROM t WHERE a = 1");
10604 let Statement::Select(s) = s else {
10605 panic!("expected SELECT")
10606 };
10607 let w = s.where_.unwrap();
10608 assert_eq!(
10609 w,
10610 Expr::Binary {
10611 lhs: Box::new(col("a")),
10612 op: BinOp::Eq,
10613 rhs: Box::new(lit_int(1)),
10614 }
10615 );
10616 }
10617
10618 #[test]
10619 fn arithmetic_precedence() {
10620 let s = parse("SELECT 1 + 2 * 3");
10621 let Statement::Select(s) = s else {
10622 panic!("expected SELECT")
10623 };
10624 let SelectItem::Expr { expr, .. } = &s.items[0] else {
10625 panic!("wildcard?")
10626 };
10627 assert_eq!(
10628 expr,
10629 &Expr::Binary {
10630 lhs: Box::new(lit_int(1)),
10631 op: BinOp::Add,
10632 rhs: Box::new(Expr::Binary {
10633 lhs: Box::new(lit_int(2)),
10634 op: BinOp::Mul,
10635 rhs: Box::new(lit_int(3)),
10636 }),
10637 }
10638 );
10639 }
10640
10641 #[test]
10642 fn parentheses_override_precedence() {
10643 let s = parse("SELECT (1 + 2) * 3");
10644 let Statement::Select(s) = s else {
10645 panic!("expected SELECT")
10646 };
10647 let SelectItem::Expr { expr, .. } = &s.items[0] else {
10648 panic!()
10649 };
10650 assert_eq!(
10651 expr,
10652 &Expr::Binary {
10653 lhs: Box::new(Expr::Binary {
10654 lhs: Box::new(lit_int(1)),
10655 op: BinOp::Add,
10656 rhs: Box::new(lit_int(2)),
10657 }),
10658 op: BinOp::Mul,
10659 rhs: Box::new(lit_int(3)),
10660 }
10661 );
10662 }
10663
10664 #[test]
10665 fn not_binds_below_comparison() {
10666 let s = parse("SELECT NOT a = 1 FROM t");
10668 let Statement::Select(s) = s else {
10669 panic!("expected SELECT")
10670 };
10671 let SelectItem::Expr { expr, .. } = &s.items[0] else {
10672 panic!()
10673 };
10674 assert_eq!(
10675 expr,
10676 &Expr::Unary {
10677 op: UnOp::Not,
10678 expr: Box::new(Expr::Binary {
10679 lhs: Box::new(col("a")),
10680 op: BinOp::Eq,
10681 rhs: Box::new(lit_int(1)),
10682 }),
10683 }
10684 );
10685 }
10686
10687 #[test]
10688 fn unary_minus_binds_above_multiplication() {
10689 let s = parse("SELECT -a * 2 FROM t");
10691 let Statement::Select(s) = s else {
10692 panic!("expected SELECT")
10693 };
10694 let SelectItem::Expr { expr, .. } = &s.items[0] else {
10695 panic!()
10696 };
10697 assert_eq!(
10698 expr,
10699 &Expr::Binary {
10700 lhs: Box::new(Expr::Unary {
10701 op: UnOp::Neg,
10702 expr: Box::new(col("a")),
10703 }),
10704 op: BinOp::Mul,
10705 rhs: Box::new(lit_int(2)),
10706 }
10707 );
10708 }
10709
10710 #[test]
10711 fn qualified_column() {
10712 let s = parse("SELECT t.col FROM t");
10713 let Statement::Select(s) = s else {
10714 panic!("expected SELECT")
10715 };
10716 let SelectItem::Expr { expr, .. } = &s.items[0] else {
10717 panic!()
10718 };
10719 assert_eq!(
10720 expr,
10721 &Expr::Column(ColumnName {
10722 qualifier: Some("t".into()),
10723 name: "col".into()
10724 })
10725 );
10726 }
10727
10728 #[test]
10729 fn select_item_alias_with_as() {
10730 let s = parse("SELECT a AS y FROM t");
10731 let Statement::Select(s) = s else {
10732 panic!("expected SELECT")
10733 };
10734 let SelectItem::Expr { alias, .. } = &s.items[0] else {
10735 panic!()
10736 };
10737 assert_eq!(alias.as_deref(), Some("y"));
10738 }
10739
10740 #[test]
10741 fn trailing_semicolon_accepted() {
10742 let s = parse("SELECT 1;");
10743 let Statement::Select(s) = s else {
10744 panic!("expected SELECT")
10745 };
10746 assert_eq!(s.items.len(), 1);
10747 }
10748
10749 #[test]
10750 fn boolean_chain_with_and_or_not() {
10751 let s = parse("SELECT NOT a OR b AND NOT c FROM t");
10753 let Statement::Select(s) = s else {
10754 panic!("expected SELECT")
10755 };
10756 let SelectItem::Expr { expr, .. } = &s.items[0] else {
10757 panic!()
10758 };
10759 let expected = Expr::Binary {
10760 lhs: Box::new(Expr::Unary {
10761 op: UnOp::Not,
10762 expr: Box::new(col("a")),
10763 }),
10764 op: BinOp::Or,
10765 rhs: Box::new(Expr::Binary {
10766 lhs: Box::new(col("b")),
10767 op: BinOp::And,
10768 rhs: Box::new(Expr::Unary {
10769 op: UnOp::Not,
10770 expr: Box::new(col("c")),
10771 }),
10772 }),
10773 };
10774 assert_eq!(expr, &expected);
10775 }
10776
10777 #[test]
10778 fn empty_input_errors() {
10779 assert!(matches!(parse_statement("").unwrap(), Statement::Empty));
10786 assert!(matches!(
10787 parse_statement(" \n\t ").unwrap(),
10788 Statement::Empty
10789 ));
10790 assert!(parse_statement("SELECT FROM WHERE").is_err());
10792 }
10793
10794 #[test]
10795 fn unmatched_paren_errors() {
10796 assert!(parse_statement("SELECT (1 + 2").is_err());
10797 }
10798
10799 #[test]
10800 fn display_round_trip_simple_select() {
10801 let original = parse("SELECT a + 1 FROM t WHERE a > 0");
10802 let text = original.to_string();
10803 let again = parse_statement(&text).expect("re-parse");
10804 assert_eq!(original, again);
10805 }
10806
10807 #[test]
10810 fn create_table_single_column() {
10811 let s = parse("CREATE TABLE foo (a INT)");
10812 let Statement::CreateTable(c) = s else {
10813 panic!("expected CreateTable")
10814 };
10815 assert_eq!(c.name, "foo");
10816 assert_eq!(c.columns.len(), 1);
10817 assert_eq!(c.columns[0].name, "a");
10818 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
10819 assert!(c.columns[0].nullable);
10820 }
10821
10822 #[test]
10823 fn create_table_multi_column_with_not_null_mix() {
10824 let s = parse("CREATE TABLE u (id INT NOT NULL, name TEXT, score FLOAT NOT NULL, ok BOOL)");
10825 let Statement::CreateTable(c) = s else {
10826 panic!()
10827 };
10828 assert_eq!(c.columns.len(), 4);
10829 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
10830 assert!(!c.columns[0].nullable);
10831 assert_eq!(c.columns[1].ty, ColumnTypeName::Text);
10832 assert!(c.columns[1].nullable);
10833 assert_eq!(c.columns[2].ty, ColumnTypeName::Float);
10834 assert!(!c.columns[2].nullable);
10835 assert_eq!(c.columns[3].ty, ColumnTypeName::Bool);
10836 }
10837
10838 #[test]
10839 fn create_table_bigint_supported() {
10840 let s = parse("CREATE TABLE accounts (id BIGINT NOT NULL)");
10841 let Statement::CreateTable(c) = s else {
10842 panic!()
10843 };
10844 assert_eq!(c.columns[0].ty, ColumnTypeName::BigInt);
10845 }
10846
10847 #[test]
10848 fn create_table_vector_default_is_f32() {
10849 let s = parse("CREATE TABLE t (v VECTOR(128))");
10850 let Statement::CreateTable(c) = s else {
10851 panic!()
10852 };
10853 assert_eq!(
10854 c.columns[0].ty,
10855 ColumnTypeName::Vector {
10856 dim: 128,
10857 encoding: VecEncoding::F32,
10858 },
10859 );
10860 }
10861
10862 #[test]
10863 fn create_table_vector_using_sq8() {
10864 for sql in [
10867 "CREATE TABLE t (v VECTOR(128) USING SQ8)",
10868 "CREATE TABLE t (v VECTOR(128) using sq8)",
10869 ] {
10870 let s = parse(sql);
10871 let Statement::CreateTable(c) = s else {
10872 panic!()
10873 };
10874 assert_eq!(
10875 c.columns[0].ty,
10876 ColumnTypeName::Vector {
10877 dim: 128,
10878 encoding: VecEncoding::Sq8,
10879 },
10880 "{sql}",
10881 );
10882 }
10883 }
10884
10885 #[test]
10886 fn create_table_vector_using_unknown_errors() {
10887 let err = parse_statement("CREATE TABLE t (v VECTOR(8) USING PQ8)").unwrap_err();
10896 assert!(
10897 err.message.contains("USING")
10898 || err.message.contains("using")
10899 || err.message.contains("')'")
10900 || err.message.contains("','"),
10901 "expected USING/column-list rejection, got: {}",
10902 err.message
10903 );
10904 }
10905
10906 #[test]
10907 fn vector_using_sq8_display_roundtrips() {
10908 let s = parse("CREATE TABLE t (v VECTOR(64) USING SQ8)");
10911 let Statement::CreateTable(c) = s else {
10912 panic!()
10913 };
10914 assert_eq!(c.columns[0].ty.to_string(), "VECTOR(64) USING SQ8");
10915 }
10916
10917 #[test]
10918 fn parser_recognises_placeholders() {
10919 use crate::ast::{Expr, SelectItem, Statement};
10920 let s = parse("SELECT $1, $2 + 1 FROM t WHERE x = $3");
10922 let Statement::Select(sel) = s else { panic!() };
10923 assert!(matches!(
10924 sel.items[0],
10925 SelectItem::Expr {
10926 expr: Expr::Placeholder(1),
10927 alias: None
10928 }
10929 ));
10930 let SelectItem::Expr {
10932 expr: Expr::Binary { lhs, rhs, .. },
10933 ..
10934 } = &sel.items[1]
10935 else {
10936 panic!()
10937 };
10938 assert!(matches!(**lhs, Expr::Placeholder(2)));
10939 assert!(matches!(**rhs, Expr::Literal(Literal::Integer(1))));
10940 let Some(Expr::Binary { rhs, .. }) = sel.where_.as_ref() else {
10942 panic!()
10943 };
10944 assert!(matches!(**rhs, Expr::Placeholder(3)));
10945 }
10946
10947 #[test]
10948 fn parser_rejects_dollar_zero() {
10949 assert!(parse_statement("SELECT $0").is_err());
10951 }
10952
10953 #[test]
10954 fn placeholder_display_roundtrips() {
10955 let s = parse("SELECT $42 FROM t");
10958 let printed = s.to_string();
10959 assert!(printed.contains("$42"));
10960 let again = parse(&printed);
10961 assert_eq!(s, again);
10962 }
10963
10964 #[test]
10965 fn alter_index_rebuild_bare() {
10966 use crate::ast::{AlterIndexTarget, Statement};
10967 let s = parse("ALTER INDEX my_idx REBUILD");
10968 let Statement::AlterIndex(a) = s else {
10969 panic!("expected AlterIndex, got {s:?}")
10970 };
10971 assert_eq!(a.name, "my_idx");
10972 assert_eq!(a.target, AlterIndexTarget::Rebuild { encoding: None });
10973 }
10974
10975 #[test]
10976 fn alter_index_rebuild_with_encoding() {
10977 use crate::ast::{AlterIndexTarget, Statement};
10978 for (sql, want) in [
10979 (
10980 "ALTER INDEX my_idx REBUILD WITH (encoding = F32)",
10981 VecEncoding::F32,
10982 ),
10983 (
10984 "ALTER INDEX my_idx REBUILD WITH (encoding = sq8)",
10985 VecEncoding::Sq8,
10986 ),
10987 (
10988 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
10989 VecEncoding::F16,
10990 ),
10991 ] {
10992 let s = parse(sql);
10993 let Statement::AlterIndex(a) = s else {
10994 panic!("{sql}: expected AlterIndex")
10995 };
10996 assert_eq!(a.name, "my_idx");
10997 assert_eq!(
10998 a.target,
10999 AlterIndexTarget::Rebuild {
11000 encoding: Some(want)
11001 },
11002 "{sql}"
11003 );
11004 }
11005 }
11006
11007 #[test]
11008 fn alter_index_rebuild_unknown_encoding_errors() {
11009 let err = parse_statement("ALTER INDEX my_idx REBUILD WITH (encoding = PQ8)").unwrap_err();
11010 assert!(
11011 err.message.contains("unknown vector encoding"),
11012 "got: {}",
11013 err.message
11014 );
11015 }
11016
11017 #[test]
11018 fn alter_index_rebuild_display_roundtrips() {
11019 for (input, want) in [
11020 ("ALTER INDEX my_idx REBUILD", "ALTER INDEX my_idx REBUILD"),
11021 (
11022 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
11023 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
11024 ),
11025 (
11026 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
11027 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
11028 ),
11029 ] {
11030 let s = parse(input);
11031 assert_eq!(s.to_string(), want);
11032 }
11033 }
11034
11035 #[test]
11036 fn create_table_unknown_type_defers_to_engine() {
11037 let stmt = parse_statement("CREATE TABLE x (a my_user_type)").unwrap();
11046 let Statement::CreateTable(t) = stmt else {
11047 panic!("expected CreateTable");
11048 };
11049 assert_eq!(t.columns[0].user_type_ref.as_deref(), Some("my_user_type"));
11050 }
11051
11052 #[test]
11053 fn create_table_missing_table_keyword_errors() {
11054 assert!(parse_statement("CREATE x (a INT)").is_err());
11055 }
11056
11057 #[test]
11061 fn parse_create_table_partition_by_range() {
11062 use crate::ast::{PartitionBySpec, PartitionKindAst};
11063 let stmt = parse_statement(
11064 "CREATE TABLE events_partitioned (id BIGINT NOT NULL, ts TIMESTAMPTZ NOT NULL, \
11065 payload JSONB) PARTITION BY RANGE (ts)",
11066 )
11067 .unwrap();
11068 let Statement::CreateTable(t) = stmt else {
11069 panic!("expected CreateTable");
11070 };
11071 assert!(t.partition_of.is_none(), "parent has no partition_of");
11072 assert_eq!(t.columns.len(), 3);
11073 let by = t.partition_by.as_ref().expect("expected PARTITION BY");
11074 assert_eq!(
11075 by,
11076 &PartitionBySpec {
11077 kind: PartitionKindAst::Range,
11078 key_columns: alloc::vec!["ts".to_string()],
11079 }
11080 );
11081 assert!(
11085 t.to_string().contains("PARTITION BY RANGE (ts)"),
11086 "Display lost PARTITION BY suffix: {t}"
11087 );
11088 }
11089
11090 #[test]
11091 fn parse_create_table_partition_of_range() {
11092 use crate::ast::{PartitionOfBoundsAst, PartitionOfSpec};
11093 let stmt = parse_statement(
11094 "CREATE TABLE events_2026_06 PARTITION OF events_partitioned \
11095 FOR VALUES FROM ('2026-06-01 00:00:00+00') TO ('2026-07-01 00:00:00+00')",
11096 )
11097 .unwrap();
11098 let Statement::CreateTable(t) = stmt else {
11099 panic!("expected CreateTable");
11100 };
11101 assert!(t.columns.is_empty(), "child inherits columns from parent");
11102 assert!(t.partition_by.is_none());
11103 let of = t.partition_of.as_ref().expect("expected PARTITION OF");
11104 assert_eq!(of.parent_name, "events_partitioned");
11105 let PartitionOfSpec { bounds, .. } = of.clone();
11106 match bounds {
11107 PartitionOfBoundsAst::Range { lower, upper } => {
11108 assert!(lower.to_string().contains("2026-06-01"));
11109 assert!(upper.to_string().contains("2026-07-01"));
11110 }
11111 PartitionOfBoundsAst::Default => panic!("expected Range, got Default"),
11112 }
11113 let s = t.to_string();
11117 assert!(
11118 s.contains("PARTITION OF events_partitioned"),
11119 "Display lost PARTITION OF: {s}"
11120 );
11121 assert!(s.contains("FOR VALUES FROM"), "Display lost FROM: {s}");
11122 assert!(s.contains(") TO ("), "Display lost TO: {s}");
11123 }
11124
11125 #[test]
11126 fn parse_create_table_partition_of_default() {
11127 use crate::ast::PartitionOfBoundsAst;
11128 let stmt =
11129 parse_statement("CREATE TABLE events_default PARTITION OF events_partitioned DEFAULT")
11130 .unwrap();
11131 let Statement::CreateTable(t) = stmt else {
11132 panic!("expected CreateTable");
11133 };
11134 let of = t.partition_of.as_ref().expect("expected PARTITION OF");
11135 assert_eq!(of.parent_name, "events_partitioned");
11136 assert!(matches!(of.bounds, PartitionOfBoundsAst::Default));
11137 assert!(
11138 t.to_string()
11139 .contains("PARTITION OF events_partitioned DEFAULT"),
11140 "Display lost DEFAULT: {t}"
11141 );
11142 }
11143
11144 #[test]
11145 fn parse_create_table_partition_of_rejects_columns() {
11146 let err = parse_statement(
11150 "CREATE TABLE events_2026_06 PARTITION OF events_partitioned (id BIGINT) \
11151 FOR VALUES FROM ('a') TO ('b')",
11152 );
11153 assert!(err.is_err(), "expected parse error for explicit columns");
11154 let msg = format!("{}", err.unwrap_err());
11155 assert!(
11156 msg.contains("PARTITION OF") && msg.contains("column"),
11157 "error should mention PARTITION OF + columns: {msg}"
11158 );
11159 }
11160
11161 #[test]
11162 fn insert_single_value() {
11163 let s = parse("INSERT INTO foo VALUES (42)");
11164 let Statement::Insert(i) = s else {
11165 panic!("expected Insert")
11166 };
11167 assert_eq!(i.table, "foo");
11168 assert_eq!(i.rows.len(), 1);
11169 assert_eq!(i.rows[0].len(), 1);
11170 assert!(matches!(i.rows[0][0], Expr::Literal(Literal::Integer(42))));
11171 }
11172
11173 #[test]
11174 fn insert_multi_value_with_mixed_literals() {
11175 let s = parse("INSERT INTO foo VALUES (1, 'hi', 3.14, TRUE, NULL)");
11176 let Statement::Insert(i) = s else { panic!() };
11177 assert_eq!(i.rows.len(), 1);
11178 assert_eq!(i.rows[0].len(), 5);
11179 }
11180
11181 #[test]
11182 fn insert_missing_into_errors() {
11183 assert!(parse_statement("INSERT foo VALUES (1)").is_err());
11184 }
11185
11186 #[test]
11187 fn create_table_round_trip() {
11188 let original =
11189 parse("CREATE TABLE foo (id BIGINT NOT NULL, label TEXT, score FLOAT NOT NULL)");
11190 let text = original.to_string();
11191 let again = parse_statement(&text).expect("re-parse");
11192 assert_eq!(original, again);
11193 }
11194
11195 #[test]
11196 fn insert_round_trip_with_negation_and_string() {
11197 let original = parse("INSERT INTO t VALUES (-1, 'it''s', NULL)");
11198 let text = original.to_string();
11199 let again = parse_statement(&text).expect("re-parse");
11200 assert_eq!(original, again);
11201 }
11202
11203 #[test]
11204 fn unknown_keyword_at_statement_start_errors() {
11205 let err = parse_statement("FROBNICATE foo SET x = 1").unwrap_err();
11208 assert!(err.message.contains("expected SELECT"));
11209 }
11210
11211 #[test]
11214 fn create_index_basic() {
11215 let s = parse("CREATE INDEX idx_id ON users (id)");
11216 let Statement::CreateIndex(c) = s else {
11217 panic!("expected CreateIndex")
11218 };
11219 assert_eq!(c.name, "idx_id");
11220 assert_eq!(c.table, "users");
11221 assert_eq!(c.column, "id");
11222 }
11223
11224 #[test]
11225 fn create_index_missing_on_errors() {
11226 assert!(parse_statement("CREATE INDEX foo users (id)").is_err());
11227 }
11228
11229 #[test]
11230 fn create_index_missing_paren_errors() {
11231 assert!(parse_statement("CREATE INDEX foo ON users id").is_err());
11232 }
11233
11234 #[test]
11235 fn create_index_round_trip() {
11236 let original = parse("CREATE INDEX by_name ON users (name)");
11237 let again = parse_statement(&original.to_string()).unwrap();
11238 assert_eq!(original, again);
11239 }
11240
11241 #[test]
11244 fn create_unique_index_basic() {
11245 let s = parse("CREATE UNIQUE INDEX uq_x ON t (a)");
11246 let Statement::CreateIndex(c) = s else {
11247 panic!("expected CreateIndex");
11248 };
11249 assert!(c.is_unique);
11250 assert_eq!(c.column, "a");
11251 assert!(c.partial_predicate.is_none());
11252 }
11253
11254 #[test]
11255 fn create_unique_index_partial() {
11256 let s = parse(
11258 "CREATE UNIQUE INDEX idx_email_templates_user_default \
11259 ON email_templates (user_address) WHERE is_default = true",
11260 );
11261 let Statement::CreateIndex(c) = s else {
11262 panic!("expected CreateIndex");
11263 };
11264 assert!(c.is_unique);
11265 assert_eq!(c.table, "email_templates");
11266 assert_eq!(c.column, "user_address");
11267 assert!(c.partial_predicate.is_some());
11268 }
11269
11270 #[test]
11271 fn create_unique_index_composite_with_predicate() {
11272 let s = parse(
11274 "CREATE UNIQUE INDEX uq_calendar_events_instance \
11275 ON calendar_events (calendar_id, uid, recurrence_id) \
11276 WHERE recurrence_id IS NOT NULL",
11277 );
11278 let Statement::CreateIndex(c) = s else {
11279 panic!("expected CreateIndex");
11280 };
11281 assert!(c.is_unique);
11282 assert_eq!(c.column, "calendar_id");
11283 assert_eq!(
11284 c.extra_columns,
11285 vec!["uid".to_string(), "recurrence_id".to_string()]
11286 );
11287 assert!(c.partial_predicate.is_some());
11288 }
11289
11290 #[test]
11291 fn create_unique_index_using_btree_ok() {
11292 let s = parse("CREATE UNIQUE INDEX uq_x ON t USING btree (a)");
11293 assert!(matches!(s, Statement::CreateIndex(ref c) if c.is_unique));
11294 }
11295
11296 #[test]
11297 fn create_unique_index_using_hnsw_rejected() {
11298 let err =
11299 parse_statement("CREATE UNIQUE INDEX uq_v ON t USING hnsw (embedding)").unwrap_err();
11300 assert!(err.message.contains("UNIQUE"), "{}", err.message);
11301 }
11302
11303 #[test]
11304 fn create_unique_index_round_trip() {
11305 let original = parse(
11306 "CREATE UNIQUE INDEX uq_calendar_events_master \
11307 ON calendar_events (calendar_id, uid) WHERE recurrence_id IS NULL",
11308 );
11309 let again = parse_statement(&original.to_string()).unwrap();
11310 assert_eq!(original, again);
11311 }
11312
11313 #[test]
11314 fn create_unique_without_index_errors() {
11315 let err = parse_statement("CREATE UNIQUE TABLE t (a INT)").unwrap_err();
11316 assert!(err.message.contains("INDEX"), "{}", err.message);
11317 }
11318
11319 #[test]
11322 fn create_table_bytea_column() {
11323 let s = parse("CREATE TABLE t (id INT NOT NULL, payload BYTEA NOT NULL)");
11324 let Statement::CreateTable(c) = s else {
11325 panic!("expected CreateTable");
11326 };
11327 assert_eq!(c.columns.len(), 2);
11328 assert_eq!(c.columns[1].ty, ColumnTypeName::Bytes);
11329 assert!(!c.columns[1].nullable);
11330 }
11331
11332 #[test]
11333 fn create_table_bytes_alias_column() {
11334 let s = parse("CREATE TABLE t (blob BYTES)");
11335 let Statement::CreateTable(c) = s else {
11336 panic!("expected CreateTable");
11337 };
11338 assert_eq!(c.columns[0].ty, ColumnTypeName::Bytes);
11339 }
11340
11341 #[test]
11342 fn bytea_round_trip_display() {
11343 let original = parse("CREATE TABLE t (a BYTEA NOT NULL)");
11344 let again = parse_statement(&original.to_string()).unwrap();
11345 assert_eq!(original, again);
11346 }
11347
11348 #[test]
11351 fn begin_commit_rollback_parse_as_unit_variants() {
11352 assert_eq!(parse("BEGIN"), Statement::Begin);
11353 assert_eq!(parse("COMMIT"), Statement::Commit);
11354 assert_eq!(parse("ROLLBACK"), Statement::Rollback);
11355 assert_eq!(parse("BEGIN;"), Statement::Begin);
11357 }
11358
11359 #[test]
11362 fn inner_product_binop_parses() {
11363 let s = parse("SELECT v <#> [1.0, 2.0] FROM t");
11364 let Statement::Select(s) = s else { panic!() };
11365 let SelectItem::Expr { expr, .. } = &s.items[0] else {
11366 panic!()
11367 };
11368 assert!(matches!(
11369 expr,
11370 Expr::Binary {
11371 op: BinOp::InnerProduct,
11372 ..
11373 }
11374 ));
11375 }
11376
11377 #[test]
11378 fn cosine_distance_binop_parses() {
11379 let s = parse("SELECT v <=> [1.0, 2.0] FROM t");
11380 let Statement::Select(s) = s else { panic!() };
11381 let SelectItem::Expr { expr, .. } = &s.items[0] else {
11382 panic!()
11383 };
11384 assert!(matches!(
11385 expr,
11386 Expr::Binary {
11387 op: BinOp::CosineDistance,
11388 ..
11389 }
11390 ));
11391 }
11392
11393 #[test]
11394 fn vector_cast_postfix_wraps_string_literal() {
11395 let s = parse("SELECT '[1,2,3]'::vector FROM t");
11396 let Statement::Select(s) = s else { panic!() };
11397 let SelectItem::Expr { expr, .. } = &s.items[0] else {
11398 panic!()
11399 };
11400 assert!(matches!(
11401 expr,
11402 Expr::Cast {
11403 target: CastTarget::Vector,
11404 ..
11405 }
11406 ));
11407 }
11408
11409 #[test]
11410 fn unsupported_cast_target_errors() {
11411 let err = parse_statement("SELECT 1::, FROM t").unwrap_err();
11418 assert!(err.message.contains("expected type ident after `::`"));
11419 }
11420
11421 #[test]
11422 fn tx_statements_round_trip() {
11423 for q in ["BEGIN", "COMMIT", "ROLLBACK"] {
11424 let original = parse(q);
11425 let again = parse_statement(&original.to_string()).unwrap();
11426 assert_eq!(original, again);
11427 }
11428 }
11429
11430 #[test]
11431 fn interval_text_parsing_units() {
11432 assert_eq!(parse_interval_text("1 day"), Some((0, 1, 0)));
11436 assert_eq!(
11437 parse_interval_text("24 hours"),
11438 Some((0, 0, 86_400_000_000))
11439 );
11440 assert_eq!(parse_interval_text("1 second"), Some((0, 0, 1_000_000)));
11441 assert_eq!(parse_interval_text("1 month"), Some((1, 0, 0)));
11442 assert_eq!(parse_interval_text("2 years"), Some((24, 0, 0)));
11443 assert_eq!(parse_interval_text("1 week"), Some((0, 7, 0)));
11444 assert_eq!(parse_interval_text("1 year 6 months"), Some((18, 0, 0)));
11446 assert_eq!(
11447 parse_interval_text("1 day 2 hours"),
11448 Some((0, 1, 7_200_000_000))
11449 );
11450 assert_eq!(parse_interval_text("-1 day"), Some((0, -1, 0)));
11452 assert_eq!(parse_interval_text(""), None);
11454 assert_eq!(parse_interval_text("garbage"), None);
11455 assert_eq!(parse_interval_text("1 fortnight"), None);
11456 assert_eq!(parse_interval_text("1"), None);
11457 }
11458
11459 #[test]
11460 fn interval_literal_roundtrips_via_display() {
11461 let parsed = parse("SELECT INTERVAL '1 day 2 hours'");
11462 let s = parsed.to_string();
11463 assert!(s.contains("INTERVAL '1 day 2 hours'"), "got: {s}");
11465 let again = parse_statement(&s).unwrap();
11467 assert_eq!(parsed, again);
11468 }
11469
11470 #[test]
11473 fn parser_recognises_create_publication_bare() {
11474 let s = parse("CREATE PUBLICATION pub_a");
11475 let Statement::CreatePublication(p) = s else {
11476 panic!("expected CreatePublication, got {s:?}")
11477 };
11478 assert_eq!(p.name, "pub_a");
11479 assert_eq!(p.scope, PublicationScope::AllTables);
11480 }
11481
11482 #[test]
11483 fn parser_recognises_create_publication_for_all_tables() {
11484 let s = parse("CREATE PUBLICATION pub_a FOR ALL TABLES");
11485 let Statement::CreatePublication(p) = s else {
11486 panic!("expected CreatePublication, got {s:?}")
11487 };
11488 assert_eq!(p.name, "pub_a");
11489 assert_eq!(p.scope, PublicationScope::AllTables);
11490 }
11491
11492 #[test]
11493 fn parser_recognises_drop_publication() {
11494 let s = parse("DROP PUBLICATION pub_a");
11495 let Statement::DropPublication(name) = s else {
11496 panic!("expected DropPublication, got {s:?}")
11497 };
11498 assert_eq!(name, "pub_a");
11499 }
11500
11501 #[test]
11502 fn parser_recognises_for_table_list() {
11503 let s = parse("CREATE PUBLICATION pub_a FOR TABLE t1, t2, t3");
11504 let Statement::CreatePublication(p) = s else {
11505 panic!("expected CreatePublication, got {s:?}")
11506 };
11507 assert_eq!(p.name, "pub_a");
11508 let PublicationScope::ForTables(ts) = p.scope else {
11509 panic!("expected ForTables scope")
11510 };
11511 assert_eq!(ts, alloc::vec!["t1", "t2", "t3"]);
11512 }
11513
11514 #[test]
11515 fn parser_recognises_for_tables_plural() {
11516 let s = parse("CREATE PUBLICATION pub_a FOR TABLES t1, t2");
11518 let Statement::CreatePublication(p) = s else {
11519 panic!("expected CreatePublication, got {s:?}")
11520 };
11521 let PublicationScope::ForTables(ts) = p.scope else {
11522 panic!("expected ForTables")
11523 };
11524 assert_eq!(ts, alloc::vec!["t1", "t2"]);
11525 }
11526
11527 #[test]
11528 fn parser_recognises_for_all_tables_except_list() {
11529 let s = parse("CREATE PUBLICATION p FOR ALL TABLES EXCEPT t1, t2");
11530 let Statement::CreatePublication(p) = s else {
11531 panic!()
11532 };
11533 let PublicationScope::AllTablesExcept(ts) = p.scope else {
11534 panic!("expected AllTablesExcept")
11535 };
11536 assert_eq!(ts, alloc::vec!["t1", "t2"]);
11537 }
11538
11539 #[test]
11540 fn parser_rejects_for_table_with_empty_list() {
11541 let err = parse_statement("CREATE PUBLICATION p FOR TABLE")
11543 .expect_err("must error on empty list");
11544 assert!(!err.message.is_empty());
11547 }
11548
11549 #[test]
11550 fn parser_recognises_show_publications() {
11551 let s = parse("SHOW PUBLICATIONS");
11554 assert!(matches!(s, Statement::ShowPublications));
11555 }
11556
11557 #[test]
11560 fn parser_recognises_create_subscription_single_publication() {
11561 let s = parse(
11562 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=127.0.0.1 port=20002' PUBLICATION pub_a",
11563 );
11564 let Statement::CreateSubscription(c) = s else {
11565 panic!("expected CreateSubscription, got {s:?}")
11566 };
11567 assert_eq!(c.name, "sub_a");
11568 assert_eq!(c.conn_str, "host=127.0.0.1 port=20002");
11569 assert_eq!(c.publications, alloc::vec!["pub_a"]);
11570 }
11571
11572 #[test]
11573 fn parser_recognises_create_subscription_multi_publication() {
11574 let s = parse("CREATE SUBSCRIPTION sub_a CONNECTION 'host=h' PUBLICATION p1, p2, p3");
11575 let Statement::CreateSubscription(c) = s else {
11576 panic!()
11577 };
11578 assert_eq!(c.publications, alloc::vec!["p1", "p2", "p3"]);
11579 }
11580
11581 #[test]
11582 fn parser_rejects_create_subscription_missing_connection() {
11583 let err = parse_statement("CREATE SUBSCRIPTION s PUBLICATION p")
11584 .expect_err("must error on missing CONNECTION");
11585 assert!(err.message.contains("CONNECTION"), "got: {}", err.message);
11586 }
11587
11588 #[test]
11589 fn parser_rejects_create_subscription_missing_publication() {
11590 let err = parse_statement("CREATE SUBSCRIPTION s CONNECTION 'host=x'")
11591 .expect_err("must error on missing PUBLICATION");
11592 assert!(err.message.contains("PUBLICATION"), "got: {}", err.message);
11593 }
11594
11595 #[test]
11596 fn parser_recognises_drop_subscription() {
11597 let s = parse("DROP SUBSCRIPTION sub_a");
11598 let Statement::DropSubscription(name) = s else {
11599 panic!("expected DropSubscription, got {s:?}")
11600 };
11601 assert_eq!(name, "sub_a");
11602 }
11603
11604 #[test]
11605 fn parser_recognises_show_subscriptions() {
11606 let s = parse("SHOW SUBSCRIPTIONS");
11607 assert!(matches!(s, Statement::ShowSubscriptions));
11608 }
11609
11610 #[test]
11611 fn parser_recognises_wait_for_wal_position_no_timeout() {
11612 let s = parse("WAIT FOR WAL POSITION 12345");
11613 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
11614 panic!("expected WaitForWalPosition, got {s:?}")
11615 };
11616 assert_eq!(pos, 12345);
11617 assert!(timeout_ms.is_none());
11618 }
11619
11620 #[test]
11621 fn parser_recognises_wait_for_wal_position_with_timeout() {
11622 let s = parse("WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000");
11623 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
11624 panic!()
11625 };
11626 assert_eq!(pos, 67890);
11627 assert_eq!(timeout_ms, Some(5000));
11628 }
11629
11630 #[test]
11631 fn parser_rejects_wait_with_negative_position() {
11632 let err = parse_statement("WAIT FOR WAL POSITION -1").unwrap_err();
11638 assert!(!err.message.is_empty());
11639 }
11640
11641 #[test]
11642 fn parser_recognises_bare_analyze() {
11643 let s = parse("ANALYZE");
11644 assert!(matches!(s, Statement::Analyze(None)));
11645 }
11646
11647 #[test]
11648 fn parser_recognises_analyze_with_table() {
11649 let s = parse("ANALYZE users");
11650 let Statement::Analyze(Some(name)) = s else {
11651 panic!("expected Analyze, got {s:?}")
11652 };
11653 assert_eq!(name, "users");
11654 }
11655
11656 #[test]
11657 fn parser_recognises_analyze_with_quoted_table() {
11658 let s = parse("ANALYZE \"Mixed Case\"");
11659 let Statement::Analyze(Some(name)) = s else {
11660 panic!()
11661 };
11662 assert_eq!(name, "Mixed Case");
11663 }
11664
11665 #[test]
11666 fn parser_rejects_analyze_with_garbage_token() {
11667 let err = parse_statement("ANALYZE 42").expect_err("must error");
11668 assert!(!err.message.is_empty());
11669 }
11670
11671 #[test]
11672 fn analyze_display_roundtrips() {
11673 for sql in ["ANALYZE", "ANALYZE users"] {
11674 let s = parse(sql);
11675 let printed = s.to_string();
11676 let again = parse_statement(&printed)
11677 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
11678 assert_eq!(s, again);
11679 }
11680 }
11681
11682 #[test]
11683 fn wait_for_display_roundtrips() {
11684 for sql in [
11685 "WAIT FOR WAL POSITION 12345",
11686 "WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000",
11687 ] {
11688 let s = parse(sql);
11689 let printed = s.to_string();
11690 let again = parse_statement(&printed)
11691 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
11692 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
11693 }
11694 }
11695
11696 #[test]
11697 fn subscription_ddl_display_roundtrips() {
11698 for sql in [
11699 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=h port=20002' PUBLICATION pub_a",
11700 "CREATE SUBSCRIPTION sub_b CONNECTION 'host=h' PUBLICATION p1, p2",
11701 "DROP SUBSCRIPTION sub_a",
11702 "SHOW SUBSCRIPTIONS",
11703 ] {
11704 let s = parse(sql);
11705 let printed = s.to_string();
11706 let again = parse_statement(&printed)
11707 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
11708 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
11709 }
11710 }
11711
11712 #[test]
11713 fn parser_drop_dispatches_user_vs_publication() {
11714 let s = parse("DROP USER 'alice'");
11717 let Statement::DropUser(name) = s else {
11718 panic!("expected DropUser, got {s:?}")
11719 };
11720 assert_eq!(name, "alice");
11721 let s = parse("DROP PUBLICATION p1");
11723 assert!(matches!(s, Statement::DropPublication(_)));
11724 }
11725
11726 #[test]
11727 fn publication_ddl_display_roundtrips() {
11728 for sql in [
11731 "CREATE PUBLICATION pub_a",
11732 "CREATE PUBLICATION pub_a FOR ALL TABLES",
11733 "CREATE PUBLICATION pub_a FOR TABLE t1, t2",
11734 "CREATE PUBLICATION pub_a FOR ALL TABLES EXCEPT t1",
11735 "DROP PUBLICATION pub_a",
11736 "SHOW PUBLICATIONS",
11737 ] {
11738 let s = parse(sql);
11739 let printed = s.to_string();
11740 let again = parse_statement(&printed)
11741 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
11742 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
11743 }
11744 }
11745
11746 #[test]
11749 fn create_function_returns_trigger_plpgsql_minimal() {
11750 let sql = "CREATE FUNCTION noop() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RETURN NEW; END; $$";
11751 let s = parse(sql);
11752 let Statement::CreateFunction(f) = s else {
11753 panic!("expected CreateFunction");
11754 };
11755 assert_eq!(f.name, "noop");
11756 assert!(!f.or_replace);
11757 assert!(f.args.is_empty());
11758 assert!(matches!(f.returns, FunctionReturn::Trigger));
11759 assert_eq!(f.language, "plpgsql");
11760 let FunctionBody::PlPgSql(block) = f.body else {
11761 panic!("expected PlPgSql body");
11762 };
11763 assert_eq!(block.statements.len(), 1);
11764 assert!(matches!(
11765 block.statements[0],
11766 PlPgSqlStmt::Return(ReturnTarget::New)
11767 ));
11768 }
11769
11770 #[test]
11771 fn create_function_or_replace_with_assignment() {
11772 let sql = "CREATE OR REPLACE FUNCTION update_sv() RETURNS TRIGGER LANGUAGE plpgsql AS $$
11775BEGIN
11776 NEW.search_vector := to_tsvector('english', NEW.subject);
11777 RETURN NEW;
11778END;
11779$$";
11780 let s = parse(sql);
11781 let Statement::CreateFunction(f) = s else {
11782 panic!("expected CreateFunction");
11783 };
11784 assert!(f.or_replace);
11785 let FunctionBody::PlPgSql(block) = &f.body else {
11786 panic!("expected PlPgSql body");
11787 };
11788 assert_eq!(block.statements.len(), 2);
11789 let PlPgSqlStmt::Assign { target, .. } = &block.statements[0] else {
11791 panic!("expected Assign as first stmt");
11792 };
11793 match target {
11794 AssignTarget::NewColumn(c) => assert_eq!(c, "search_vector"),
11795 other => panic!("expected NEW.col, got {other:?}"),
11796 }
11797 assert!(matches!(
11799 block.statements[1],
11800 PlPgSqlStmt::Return(ReturnTarget::New)
11801 ));
11802 }
11803
11804 #[test]
11805 fn create_trigger_after_insert_or_update() {
11806 let sql = "CREATE TRIGGER tg AFTER INSERT OR UPDATE ON messages FOR EACH ROW EXECUTE FUNCTION update_sv()";
11807 let s = parse(sql);
11808 let Statement::CreateTrigger(t) = s else {
11809 panic!("expected CreateTrigger");
11810 };
11811 assert_eq!(t.name, "tg");
11812 assert_eq!(t.table, "messages");
11813 assert_eq!(t.timing, TriggerTiming::After);
11814 assert_eq!(t.events, vec![TriggerEvent::Insert, TriggerEvent::Update]);
11815 assert_eq!(t.for_each, TriggerForEach::Row);
11816 assert_eq!(t.function, "update_sv");
11817 }
11818
11819 #[test]
11820 fn create_trigger_before_delete_execute_procedure_alias() {
11821 let sql =
11823 "CREATE TRIGGER guard BEFORE DELETE ON t FOR EACH ROW EXECUTE PROCEDURE block_delete()";
11824 let s = parse(sql);
11825 let Statement::CreateTrigger(t) = s else {
11826 panic!("expected CreateTrigger");
11827 };
11828 assert_eq!(t.timing, TriggerTiming::Before);
11829 assert_eq!(t.events, vec![TriggerEvent::Delete]);
11830 }
11831
11832 #[test]
11833 fn drop_trigger_if_exists_round_trips() {
11834 let s = Statement::DropTrigger {
11839 name: "tg".into(),
11840 table: "messages".into(),
11841 if_exists: true,
11842 };
11843 assert_eq!(s.to_string(), "DROP TRIGGER IF EXISTS tg ON messages");
11844 }
11845
11846 #[test]
11847 fn trigger_ddl_display_roundtrips_through_parser() {
11848 for sql in [
11852 "CREATE TRIGGER tg AFTER INSERT ON t FOR EACH ROW EXECUTE FUNCTION f()",
11853 "CREATE TRIGGER tg2 BEFORE UPDATE OR DELETE ON t FOR EACH ROW EXECUTE FUNCTION g()",
11854 ] {
11855 let s = parse(sql);
11856 let printed = s.to_string();
11857 let again = parse_statement(&printed)
11858 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
11859 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
11860 }
11861 }
11862}