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 BinOp, CastTarget, ColumnDef, ColumnName, ColumnTypeName, CreateIndexStatement,
22 CreatePublicationStatement, CreateSubscriptionStatement, CreateTableStatement, Expr,
23 ExtractField, FkAction, ForeignKeyConstraint, FrameBound, FrameKind, FromClause, FromJoin,
24 IndexMethod, InsertStatement, JoinKind, Literal, NullTreatment, OrderBy, PublicationScope,
25 SelectItem, SelectStatement, Statement, TableRef, UnOp, UnionKind, VecEncoding, WindowFrame,
26};
27use crate::lexer::{self, LexError, Token};
28
29fn is_vector_opclass_name(name: &str) -> bool {
34 let lc = name.to_ascii_lowercase();
35 matches!(
36 lc.as_str(),
37 "vector_cosine_ops"
38 | "vector_l2_ops"
39 | "vector_ip_ops"
40 | "halfvec_cosine_ops"
41 | "halfvec_l2_ops"
42 | "halfvec_ip_ops"
43 | "sq8_cosine_ops"
44 | "sq8_l2_ops"
45 | "sq8_ip_ops"
46 )
47}
48
49#[derive(Debug, Clone, PartialEq, Eq)]
50pub struct ParseError {
51 pub message: String,
52 pub token_pos: usize,
54}
55
56impl fmt::Display for ParseError {
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 write!(
59 f,
60 "parse error at token #{}: {}",
61 self.token_pos, self.message
62 )
63 }
64}
65
66impl From<LexError> for ParseError {
67 fn from(e: LexError) -> Self {
68 Self {
69 message: format!("lex: {e}"),
70 token_pos: 0,
71 }
72 }
73}
74
75pub fn parse_expression(input: &str) -> Result<Expr, ParseError> {
81 let tokens = lexer::tokenize(input)?;
82 let mut p = Parser::new(tokens);
83 let expr = p.parse_expr(0)?;
84 p.expect_eof()?;
85 Ok(expr)
86}
87
88pub fn parse_statement(input: &str) -> Result<Statement, ParseError> {
91 let tokens = lexer::tokenize(input)?;
92 let mut p = Parser::new(tokens);
93 let stmt = p.parse_one_statement()?;
94 if matches!(p.peek(), Token::Semicolon) {
95 p.advance();
96 }
97 p.expect_eof()?;
98 Ok(stmt)
99}
100
101struct Parser {
102 tokens: Vec<Token>,
103 pos: usize,
104}
105
106impl Parser {
107 fn new(tokens: Vec<Token>) -> Self {
108 Self { tokens, pos: 0 }
109 }
110
111 fn peek(&self) -> &Token {
112 &self.tokens[self.pos]
114 }
115
116 fn advance(&mut self) -> Token {
117 let t = mem::replace(&mut self.tokens[self.pos], Token::Eof);
118 if self.pos + 1 < self.tokens.len() {
119 self.pos += 1;
120 }
121 t
122 }
123
124 fn err(&self, message: String) -> ParseError {
125 ParseError {
126 message,
127 token_pos: self.pos,
128 }
129 }
130
131 fn expect_eof(&self) -> Result<(), ParseError> {
132 if matches!(self.peek(), Token::Eof) {
133 Ok(())
134 } else {
135 Err(self.err(format!("expected end of input, got {:?}", self.peek())))
136 }
137 }
138
139 fn expect_ident_like(&mut self) -> Result<String, ParseError> {
140 match self.advance() {
141 Token::Ident(s) | Token::QuotedIdent(s) => Ok(s),
142 other => Err(ParseError {
143 message: format!("expected identifier, got {other:?}"),
144 token_pos: self.pos.saturating_sub(1),
145 }),
146 }
147 }
148
149 #[allow(clippy::too_many_lines)]
150 fn parse_one_statement(&mut self) -> Result<Statement, ParseError> {
151 match self.peek() {
152 Token::Select => self.parse_select_stmt(),
153 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {
159 self.advance();
160 match self.advance() {
163 Token::String(_) => {}
164 other => {
165 return Err(self.err(alloc::format!(
166 "expected dollar-quoted body after DO, got {other:?}"
167 )));
168 }
169 }
170 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("language")) {
172 self.advance();
173 let _ = self.expect_ident_like()?;
174 }
175 Ok(Statement::DoBlock)
176 }
177 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with") => {
181 self.advance();
182 self.parse_with_cte_then_select()
183 }
184 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("explain") => {
187 self.advance();
188 let mut analyze = false;
189 let mut suggest = false;
190 if matches!(self.peek(), Token::LParen) {
192 self.advance();
193 let opt = match self.peek().clone() {
194 Token::Ident(s) | Token::QuotedIdent(s) => s,
195 other => {
196 return Err(self.err(format!(
197 "expected option keyword inside EXPLAIN (…), got {other:?}"
198 )));
199 }
200 };
201 if !opt.eq_ignore_ascii_case("suggest") {
202 return Err(self.err(format!(
203 "unknown EXPLAIN option {opt:?}; v6.8.3 supports SUGGEST"
204 )));
205 }
206 self.advance();
207 if !matches!(self.peek(), Token::RParen) {
208 return Err(self.err(format!(
209 "expected ')' after EXPLAIN option, got {:?}",
210 self.peek()
211 )));
212 }
213 self.advance();
214 suggest = true;
215 } else if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
216 && (s.eq_ignore_ascii_case("analyze") || s.eq_ignore_ascii_case("analyse"))
217 {
218 self.advance();
219 analyze = true;
220 }
221 let inner = self.parse_select_stmt()?;
222 let Statement::Select(s) = inner else {
223 return Err(self.err(format!("EXPLAIN body must be a SELECT, got {inner:?}")));
224 };
225 Ok(Statement::Explain(crate::ast::ExplainStatement {
226 analyze,
227 inner: Box::new(s),
228 suggest,
229 }))
230 }
231 Token::Create => self.parse_create_stmt(),
232 Token::Insert => self.parse_insert_stmt(),
233 Token::Begin => {
234 self.advance();
235 Ok(Statement::Begin)
236 }
237 Token::Commit => {
238 self.advance();
239 Ok(Statement::Commit)
240 }
241 Token::Rollback => {
242 self.advance();
243 if matches!(self.peek(), Token::To) {
247 self.advance();
248 if matches!(self.peek(), Token::Savepoint) {
249 self.advance();
250 }
251 let name = self.expect_ident_like()?;
252 Ok(Statement::RollbackToSavepoint(name))
253 } else {
254 Ok(Statement::Rollback)
255 }
256 }
257 Token::Savepoint => {
258 self.advance();
259 let name = self.expect_ident_like()?;
260 Ok(Statement::Savepoint(name))
261 }
262 Token::Release => {
263 self.advance();
264 if matches!(self.peek(), Token::Savepoint) {
267 self.advance();
268 }
269 let name = self.expect_ident_like()?;
270 Ok(Statement::ReleaseSavepoint(name))
271 }
272 Token::Show => {
273 self.advance();
274 let target = match self.advance() {
280 Token::Tables => "tables".to_string(),
281 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
282 other => {
283 return Err(self.err(format!(
284 "expected SHOW target, got {other:?}"
285 )));
286 }
287 };
288 match target.as_str() {
289 "tables" => Ok(Statement::ShowTables),
290 "users" => Ok(Statement::ShowUsers),
291 "publications" => Ok(Statement::ShowPublications),
296 "subscriptions" => Ok(Statement::ShowSubscriptions),
298 "columns" => {
299 if !matches!(self.peek(), Token::From) {
300 return Err(self.err(format!(
301 "expected FROM after SHOW COLUMNS, got {:?}",
302 self.peek()
303 )));
304 }
305 self.advance();
306 let table = self.expect_ident_like()?;
307 Ok(Statement::ShowColumns(table))
308 }
309 other => Err(self.err(format!(
310 "unknown SHOW target {other:?}; supported: TABLES, COLUMNS, USERS, PUBLICATIONS"
311 ))),
312 }
313 }
314 Token::Drop => {
320 self.advance();
321 match self.peek() {
322 Token::Publication => {
323 self.advance();
324 let name = self.expect_ident_or_string()?;
325 Ok(Statement::DropPublication(name))
326 }
327 Token::Subscription => {
328 self.advance();
329 let name = self.expect_ident_or_string()?;
330 Ok(Statement::DropSubscription(name))
331 }
332 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("user") => {
333 self.advance();
334 let name = self.expect_ident_or_string()?;
335 Ok(Statement::DropUser(name))
336 }
337 other => Err(self.err(format!(
338 "expected USER / PUBLICATION / SUBSCRIPTION after DROP, got {other:?}"
339 ))),
340 }
341 }
342 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
343 self.advance();
344 self.parse_update_after_keyword()
345 }
346 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
347 self.advance();
348 self.parse_delete_after_keyword()
349 }
350 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("alter") => {
354 self.advance();
355 self.parse_alter_after_keyword()
356 }
357 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("wait") => {
361 self.advance();
362 self.parse_wait_after_keyword()
363 }
364 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("compact") => {
375 self.advance();
376 let next = self.peek().clone();
377 let cold = match next {
378 Token::Ident(s) | Token::QuotedIdent(s) => s,
379 _ => {
380 return Err(
381 self.err(format!("expected COLD after COMPACT, got {:?}", self.peek()))
382 );
383 }
384 };
385 if !cold.eq_ignore_ascii_case("cold") {
386 return Err(self.err(format!("expected COLD after COMPACT, got {cold:?}")));
387 }
388 self.advance();
389 let next = self.peek().clone();
390 let segments = match next {
391 Token::Ident(s) | Token::QuotedIdent(s) => s,
392 _ => {
393 return Err(self.err(format!(
394 "expected SEGMENTS after COMPACT COLD, got {:?}",
395 self.peek()
396 )));
397 }
398 };
399 if !segments.eq_ignore_ascii_case("segments") {
400 return Err(self.err(format!(
401 "expected SEGMENTS after COMPACT COLD, got {segments:?}"
402 )));
403 }
404 self.advance();
405 Ok(Statement::CompactColdSegments)
406 }
407 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("analyze") => {
408 self.advance();
409 let target = match self.peek() {
410 Token::Eof | Token::Semicolon => None,
411 Token::Ident(_) | Token::QuotedIdent(_) => {
412 Some(self.expect_ident_like()?)
413 }
414 other => {
415 return Err(self.err(format!(
416 "expected table name or end of statement after ANALYZE, got {other:?}"
417 )));
418 }
419 };
420 Ok(Statement::Analyze(target))
421 }
422 other => Err(self.err(format!(
423 "expected SELECT / CREATE / DROP / INSERT / UPDATE / DELETE / ALTER / BEGIN / COMMIT / \
424 ROLLBACK / SAVEPOINT / RELEASE / SHOW at start of statement, got {other:?}"
425 ))),
426 }
427 }
428
429 fn parse_create_stmt(&mut self) -> Result<Statement, ParseError> {
430 debug_assert!(matches!(self.peek(), Token::Create));
431 self.advance();
432 match self.peek() {
433 Token::Table => self.parse_create_table_stmt_after_create(),
434 Token::Index => self.parse_create_index_stmt_after_create(false),
435 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unique") => {
442 self.advance();
443 if !matches!(self.peek(), Token::Index) {
444 return Err(self.err(alloc::format!(
445 "expected INDEX after CREATE UNIQUE, got {:?}",
446 self.peek()
447 )));
448 }
449 self.parse_create_index_stmt_after_create(true)
450 }
451 Token::Publication => {
452 self.advance();
453 self.parse_create_publication_after_keyword()
454 }
455 Token::Subscription => {
456 self.advance();
457 self.parse_create_subscription_after_keyword()
458 }
459 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("user") => {
463 self.advance();
464 self.parse_create_user_after_keyword()
465 }
466 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("extension") => {
470 self.advance();
471 self.parse_create_extension_after_keyword()
472 }
473 other => Err(self.err(format!(
474 "expected TABLE / INDEX / USER / EXTENSION / PUBLICATION / SUBSCRIPTION after CREATE, got {other:?}"
475 ))),
476 }
477 }
478
479 fn parse_create_extension_after_keyword(&mut self) -> Result<Statement, ParseError> {
484 self.consume_if_not_exists();
486 let name = self.expect_ident_like()?;
487 loop {
490 match self.peek() {
491 Token::Ident(s) if s.eq_ignore_ascii_case("with") => {
492 self.advance();
493 continue;
494 }
495 Token::Ident(s) if s.eq_ignore_ascii_case("schema") => {
496 self.advance();
497 let _ = self.expect_ident_like()?;
498 continue;
499 }
500 Token::Ident(s) if s.eq_ignore_ascii_case("version") => {
501 self.advance();
502 let _ = self.advance();
504 continue;
505 }
506 Token::Ident(s) if s.eq_ignore_ascii_case("from") => {
507 self.advance();
508 let _ = self.advance();
509 continue;
510 }
511 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => {
512 self.advance();
513 continue;
514 }
515 _ => break,
516 }
517 }
518 Ok(Statement::CreateExtension(name))
519 }
520
521 fn parse_create_publication_after_keyword(&mut self) -> Result<Statement, ParseError> {
528 let name = self.expect_ident_or_string()?;
529 let scope = if matches!(self.peek(), Token::For) {
532 self.advance();
533 if matches!(self.peek(), Token::All) {
534 self.advance();
535 if !matches!(self.peek(), Token::Tables) {
536 return Err(self.err(format!(
537 "expected TABLES after FOR ALL, got {:?}",
538 self.peek()
539 )));
540 }
541 self.advance();
542 if matches!(self.peek(), Token::Except) {
543 self.advance();
544 let tables = self.parse_publication_table_list()?;
545 PublicationScope::AllTablesExcept(tables)
546 } else {
547 PublicationScope::AllTables
548 }
549 } else if matches!(self.peek(), Token::Table | Token::Tables) {
550 self.advance();
553 let tables = self.parse_publication_table_list()?;
554 PublicationScope::ForTables(tables)
555 } else {
556 return Err(self.err(format!(
557 "expected ALL TABLES or TABLE <list> after FOR, got {:?}",
558 self.peek()
559 )));
560 }
561 } else {
562 PublicationScope::AllTables
563 };
564 Ok(Statement::CreatePublication(CreatePublicationStatement {
565 name,
566 scope,
567 }))
568 }
569
570 fn parse_publication_table_list(&mut self) -> Result<Vec<String>, ParseError> {
575 let first = self.expect_ident_like()?;
576 let mut out = alloc::vec![first];
577 while matches!(self.peek(), Token::Comma) {
578 self.advance();
579 out.push(self.expect_ident_like()?);
580 }
581 Ok(out)
582 }
583
584 fn parse_create_subscription_after_keyword(&mut self) -> Result<Statement, ParseError> {
592 let name = self.expect_ident_or_string()?;
593 if !matches!(self.peek(), Token::Connection) {
594 return Err(self.err(format!(
595 "expected CONNECTION after CREATE SUBSCRIPTION <name>, got {:?}",
596 self.peek()
597 )));
598 }
599 self.advance();
600 let conn_str = self.expect_string_literal()?;
601 if !matches!(self.peek(), Token::Publication) {
602 return Err(self.err(format!(
603 "expected PUBLICATION after CONNECTION '<conn>', got {:?}",
604 self.peek()
605 )));
606 }
607 self.advance();
608 let first = self.expect_ident_like()?;
611 let mut publications = alloc::vec![first];
612 while matches!(self.peek(), Token::Comma) {
613 self.advance();
614 publications.push(self.expect_ident_like()?);
615 }
616 Ok(Statement::CreateSubscription(
617 CreateSubscriptionStatement {
618 name,
619 conn_str,
620 publications,
621 },
622 ))
623 }
624
625 fn parse_wait_after_keyword(&mut self) -> Result<Statement, ParseError> {
630 if !matches!(self.peek(), Token::For) {
634 return Err(self.err(format!(
635 "expected FOR after WAIT, got {:?}",
636 self.peek()
637 )));
638 }
639 self.advance();
640 self.expect_keyword_ident("wal")?;
641 self.expect_keyword_ident("position")?;
642 let pos = self.expect_u64_literal()?;
643 let timeout_ms = if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with"))
644 {
645 self.advance();
646 self.expect_keyword_ident("timeout")?;
647 Some(self.expect_u64_literal()?)
648 } else {
649 None
650 };
651 Ok(Statement::WaitForWalPosition { pos, timeout_ms })
652 }
653
654 fn expect_u64_literal(&mut self) -> Result<u64, ParseError> {
658 match self.advance() {
659 Token::Integer(n) if n >= 0 => Ok(n as u64),
660 Token::Integer(n) => Err(ParseError {
661 message: format!("expected non-negative integer, got {n}"),
662 token_pos: self.pos.saturating_sub(1),
663 }),
664 other => Err(ParseError {
665 message: format!("expected integer literal, got {other:?}"),
666 token_pos: self.pos.saturating_sub(1),
667 }),
668 }
669 }
670
671 fn parse_create_user_after_keyword(&mut self) -> Result<Statement, ParseError> {
675 let name = self.expect_ident_or_string()?;
676 self.expect_keyword_ident("with")?;
677 self.expect_keyword_ident("password")?;
678 let password = self.expect_string_literal()?;
679 let role = if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
680 && s.eq_ignore_ascii_case("role")
681 {
682 self.advance();
683 self.expect_string_literal()?
684 } else {
685 "readonly".to_string()
686 };
687 Ok(Statement::CreateUser(crate::ast::CreateUserStatement {
688 name,
689 password,
690 role,
691 }))
692 }
693
694 fn parse_update_after_keyword(&mut self) -> Result<Statement, ParseError> {
697 let table = self.expect_ident_like()?;
698 self.expect_keyword_ident("set")?;
699 let mut assignments = Vec::new();
700 loop {
701 let col = self.expect_ident_like()?;
702 if !matches!(self.peek(), Token::Eq) {
703 return Err(self.err(format!(
704 "expected `=` after column name in UPDATE SET, got {:?}",
705 self.peek()
706 )));
707 }
708 self.advance();
709 let value = self.parse_expr(0)?;
710 assignments.push((col, value));
711 if matches!(self.peek(), Token::Comma) {
712 self.advance();
713 continue;
714 }
715 break;
716 }
717 let where_ = if matches!(self.peek(), Token::Where) {
718 self.advance();
719 Some(self.parse_expr(0)?)
720 } else {
721 None
722 };
723 let returning = self.parse_optional_returning()?;
724 Ok(Statement::Update(crate::ast::UpdateStatement {
725 table,
726 assignments,
727 where_,
728 returning,
729 }))
730 }
731
732 fn parse_delete_after_keyword(&mut self) -> Result<Statement, ParseError> {
735 if !matches!(self.peek(), Token::From) {
736 return Err(self.err(format!("expected FROM after DELETE, got {:?}", self.peek())));
737 }
738 self.advance();
739 let table = self.expect_ident_like()?;
740 let where_ = if matches!(self.peek(), Token::Where) {
741 self.advance();
742 Some(self.parse_expr(0)?)
743 } else {
744 None
745 };
746 let returning = self.parse_optional_returning()?;
747 Ok(Statement::Delete(crate::ast::DeleteStatement {
748 table,
749 where_,
750 returning,
751 }))
752 }
753
754 fn parse_optional_returning(&mut self) -> Result<Option<Vec<crate::ast::SelectItem>>, ParseError> {
759 let is_returning_kw = matches!(
760 self.peek(),
761 Token::Ident(s) if s.eq_ignore_ascii_case("returning")
762 );
763 if !is_returning_kw {
764 return Ok(None);
765 }
766 self.advance();
767 let mut items = Vec::new();
768 loop {
769 items.push(self.parse_select_item()?);
770 if matches!(self.peek(), Token::Comma) {
771 self.advance();
772 continue;
773 }
774 break;
775 }
776 Ok(Some(items))
777 }
778
779 fn parse_alter_after_keyword(&mut self) -> Result<Statement, ParseError> {
787 match self.advance() {
789 Token::Index => {}
790 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("index") => {}
791 Token::Table => return self.parse_alter_table_after_keyword(),
793 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("table") => {
794 return self.parse_alter_table_after_keyword();
795 }
796 other => {
797 return Err(self.err(format!("expected INDEX or TABLE after ALTER, got {other:?}")));
798 }
799 }
800 let name = self.expect_ident_like()?;
801 self.expect_keyword_ident("rebuild")?;
803 let encoding = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
805 self.advance();
806 if !matches!(self.peek(), Token::LParen) {
807 return Err(self.err(format!(
808 "expected '(' after WITH in ALTER INDEX REBUILD, got {:?}",
809 self.peek()
810 )));
811 }
812 self.advance();
813 self.expect_keyword_ident("encoding")?;
814 if !matches!(self.peek(), Token::Eq) {
815 return Err(self.err(format!(
816 "expected '=' after encoding in ALTER INDEX REBUILD, got {:?}",
817 self.peek()
818 )));
819 }
820 self.advance();
821 let enc_ident = match self.advance() {
822 Token::Ident(s) | Token::QuotedIdent(s) => s,
823 other => {
824 return Err(self.err(format!("expected encoding name after =, got {other:?}")));
825 }
826 };
827 let enc = match enc_ident.to_ascii_lowercase().as_str() {
828 "f32" => VecEncoding::F32,
829 "sq8" => VecEncoding::Sq8,
830 "half" => VecEncoding::F16,
831 other => {
832 return Err(self.err(format!(
833 "unknown vector encoding {other:?} in ALTER INDEX REBUILD; supported: F32, SQ8, HALF"
834 )));
835 }
836 };
837 if !matches!(self.peek(), Token::RParen) {
838 return Err(self.err(format!(
839 "expected ')' after encoding value, got {:?}",
840 self.peek()
841 )));
842 }
843 self.advance();
844 Some(enc)
845 } else {
846 None
847 };
848 Ok(Statement::AlterIndex(crate::ast::AlterIndexStatement {
849 name,
850 target: crate::ast::AlterIndexTarget::Rebuild { encoding },
851 }))
852 }
853
854 fn parse_alter_table_after_keyword(&mut self) -> Result<Statement, ParseError> {
858 let table_name = self.expect_ident_like()?;
859 match self.peek() {
863 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
864 self.advance();
865 let setting = self.expect_ident_like()?;
866 if !setting.eq_ignore_ascii_case("hot_tier_bytes") {
867 return Err(self.err(alloc::format!(
868 "ALTER TABLE SET: unknown setting {setting:?}; supported: hot_tier_bytes"
869 )));
870 }
871 if !matches!(self.peek(), Token::Eq) {
872 return Err(self.err(alloc::format!(
873 "expected '=' after hot_tier_bytes, got {:?}",
874 self.peek()
875 )));
876 }
877 self.advance();
878 let n = self.expect_u64_literal()?;
879 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
880 name: table_name,
881 target: crate::ast::AlterTableTarget::SetHotTierBytes(n),
882 }))
883 }
884 Token::Ident(s) if s.eq_ignore_ascii_case("add") => {
885 self.advance();
886 let fk = self.parse_table_level_fk()?;
889 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
890 name: table_name,
891 target: crate::ast::AlterTableTarget::AddForeignKey(fk),
892 }))
893 }
894 Token::Drop => {
895 self.advance();
896 match self.advance() {
897 Token::Ident(s) if s.eq_ignore_ascii_case("constraint") => {}
898 other => {
899 return Err(self.err(alloc::format!(
900 "expected CONSTRAINT after DROP in ALTER TABLE, got {other:?}"
901 )));
902 }
903 }
904 let cname = self.expect_ident_like()?;
905 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
906 name: table_name,
907 target: crate::ast::AlterTableTarget::DropForeignKey(cname),
908 }))
909 }
910 other => Err(self.err(alloc::format!(
911 "expected SET / ADD / DROP in ALTER TABLE, got {other:?}"
912 ))),
913 }
914 }
915
916 fn expect_keyword_ident(&mut self, kw: &str) -> Result<(), ParseError> {
918 match self.advance() {
919 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case(kw) => Ok(()),
920 other => Err(ParseError {
921 message: format!("expected {kw:?}, got {other:?}"),
922 token_pos: self.pos.saturating_sub(1),
923 }),
924 }
925 }
926
927 fn expect_ident_or_string(&mut self) -> Result<String, ParseError> {
931 match self.advance() {
932 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => Ok(s),
933 other => Err(ParseError {
934 message: format!("expected identifier or string, got {other:?}"),
935 token_pos: self.pos.saturating_sub(1),
936 }),
937 }
938 }
939
940 fn expect_string_literal(&mut self) -> Result<String, ParseError> {
941 match self.advance() {
942 Token::String(s) => Ok(s),
943 other => Err(ParseError {
944 message: format!("expected quoted string, got {other:?}"),
945 token_pos: self.pos.saturating_sub(1),
946 }),
947 }
948 }
949
950 fn parse_select_stmt(&mut self) -> Result<Statement, ParseError> {
951 let mut head = self.parse_bare_select()?;
956 while matches!(self.peek(), Token::Union) {
957 self.advance();
958 let kind = if matches!(self.peek(), Token::All) {
959 self.advance();
960 UnionKind::All
961 } else {
962 UnionKind::Distinct
963 };
964 let peer = self.parse_bare_select()?;
965 head.unions.push((kind, peer));
966 }
967 head.order_by = if matches!(self.peek(), Token::Order) {
968 self.advance();
969 if !matches!(self.peek(), Token::By) {
970 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
971 }
972 self.advance();
973 let mut keys = Vec::new();
976 loop {
977 let expr = self.parse_expr(0)?;
978 let desc = if matches!(self.peek(), Token::Desc) {
979 self.advance();
980 true
981 } else if matches!(self.peek(), Token::Asc) {
982 self.advance();
983 false
984 } else {
985 false
986 };
987 keys.push(OrderBy { expr, desc });
988 if matches!(self.peek(), Token::Comma) {
989 self.advance();
990 } else {
991 break;
992 }
993 }
994 keys
995 } else {
996 Vec::new()
997 };
998 head.limit = if matches!(self.peek(), Token::Limit) {
999 self.advance();
1000 Some(self.parse_limit_expr("LIMIT")?)
1001 } else {
1002 None
1003 };
1004 head.offset = if matches!(self.peek(), Token::Offset) {
1005 self.advance();
1006 Some(self.parse_limit_expr("OFFSET")?)
1007 } else {
1008 None
1009 };
1010 Ok(Statement::Select(head))
1011 }
1012
1013 fn parse_limit_expr(&mut self, label: &str) -> Result<crate::ast::LimitExpr, ParseError> {
1018 match self.advance() {
1019 Token::Integer(n) if n >= 0 => u32::try_from(n)
1020 .map(crate::ast::LimitExpr::Literal)
1021 .map_err(|_| ParseError {
1022 message: alloc::format!("{label} value too large: {n}"),
1023 token_pos: self.pos.saturating_sub(1),
1024 }),
1025 Token::Placeholder(n) => Ok(crate::ast::LimitExpr::Placeholder(n)),
1026 other => Err(ParseError {
1027 message: alloc::format!(
1028 "expected non-negative integer or $N placeholder after {label}, got {other:?}"
1029 ),
1030 token_pos: self.pos.saturating_sub(1),
1031 }),
1032 }
1033 }
1034
1035 fn expect_u32_literal(&mut self, label: &str) -> Result<u32, ParseError> {
1036 match self.advance() {
1037 Token::Integer(n) if n >= 0 => u32::try_from(n).map_err(|_| ParseError {
1038 message: format!("{label} value too large: {n}"),
1039 token_pos: self.pos.saturating_sub(1),
1040 }),
1041 other => Err(ParseError {
1042 message: format!("expected non-negative integer after {label}, got {other:?}"),
1043 token_pos: self.pos.saturating_sub(1),
1044 }),
1045 }
1046 }
1047
1048 fn parse_bare_select(&mut self) -> Result<SelectStatement, ParseError> {
1053 if !matches!(self.peek(), Token::Select) {
1054 return Err(self.err(format!(
1055 "expected SELECT to start a query block, got {:?}",
1056 self.peek()
1057 )));
1058 }
1059 self.advance();
1060 let distinct = if matches!(self.peek(), Token::Distinct) {
1061 self.advance();
1062 true
1063 } else {
1064 false
1065 };
1066 let items = self.parse_select_list()?;
1067 let from = if matches!(self.peek(), Token::From) {
1068 self.advance();
1069 Some(self.parse_from_clause()?)
1070 } else {
1071 None
1072 };
1073 let where_ = if matches!(self.peek(), Token::Where) {
1074 self.advance();
1075 Some(self.parse_expr(0)?)
1076 } else {
1077 None
1078 };
1079 let mut group_by_all = false;
1080 let group_by = if matches!(self.peek(), Token::Group) {
1081 self.advance();
1082 if !matches!(self.peek(), Token::By) {
1083 return Err(self.err(format!("expected BY after GROUP, got {:?}", self.peek())));
1084 }
1085 self.advance();
1086 if matches!(self.peek(), Token::All) {
1089 self.advance();
1090 group_by_all = true;
1091 None
1092 } else {
1093 let mut groups = Vec::new();
1094 loop {
1095 groups.push(self.parse_expr(0)?);
1096 if matches!(self.peek(), Token::Comma) {
1097 self.advance();
1098 } else {
1099 break;
1100 }
1101 }
1102 Some(groups)
1103 }
1104 } else {
1105 None
1106 };
1107 let having = if matches!(self.peek(), Token::Having) {
1108 self.advance();
1109 Some(self.parse_expr(0)?)
1110 } else {
1111 None
1112 };
1113 Ok(SelectStatement {
1114 ctes: Vec::new(),
1115 distinct,
1116 items,
1117 from,
1118 where_,
1119 group_by,
1120 group_by_all,
1121 having,
1122 unions: Vec::new(),
1123 order_by: Vec::new(),
1124 limit: None,
1125 offset: None,
1126 })
1127 }
1128
1129 fn parse_create_table_stmt_after_create(&mut self) -> Result<Statement, ParseError> {
1130 debug_assert!(matches!(self.peek(), Token::Table));
1132 self.advance();
1133 let if_not_exists = self.consume_if_not_exists();
1134 let name = self.expect_ident_like()?;
1135 if !matches!(self.peek(), Token::LParen) {
1136 return Err(self.err(format!(
1137 "expected '(' after table name, got {:?}",
1138 self.peek()
1139 )));
1140 }
1141 self.advance();
1142 let mut columns = Vec::new();
1143 let mut foreign_keys: Vec<ForeignKeyConstraint> = Vec::new();
1144 let mut table_constraints: Vec<crate::ast::TableConstraint> = Vec::new();
1145 loop {
1146 if self.peek_table_level_pk_start() {
1152 table_constraints.push(self.parse_table_level_primary_key()?);
1153 } else if self.peek_table_level_unique_start() {
1154 table_constraints.push(self.parse_table_level_unique()?);
1155 } else if self.peek_constraint_or_fk_start() {
1156 foreign_keys.push(self.parse_table_level_fk()?);
1157 } else {
1158 let (col, col_level_fk) = self.parse_column_def_with_fk()?;
1159 columns.push(col);
1160 if let Some(fk) = col_level_fk {
1161 foreign_keys.push(fk);
1162 }
1163 }
1164 match self.peek() {
1165 Token::Comma => {
1166 self.advance();
1167 }
1168 Token::RParen => {
1169 self.advance();
1170 break;
1171 }
1172 other => {
1173 return Err(
1174 self.err(format!("expected ',' or ')' in column list, got {other:?}"))
1175 );
1176 }
1177 }
1178 }
1179 if columns.is_empty() {
1180 return Err(self.err("CREATE TABLE requires at least one column".into()));
1181 }
1182 Ok(Statement::CreateTable(CreateTableStatement {
1183 name,
1184 columns,
1185 if_not_exists,
1186 foreign_keys,
1187 table_constraints,
1188 }))
1189 }
1190
1191 fn peek_table_level_pk_start(&self) -> bool {
1196 let cur = self.peek();
1197 let nxt = self.tokens.get(self.pos + 1);
1198 let nxt2 = self.tokens.get(self.pos + 2);
1199 let is_primary = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("primary"));
1200 let is_key = matches!(nxt, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("key"));
1201 let is_lparen = matches!(nxt2, Some(Token::LParen));
1202 is_primary && is_key && is_lparen
1203 }
1204
1205 fn peek_table_level_unique_start(&self) -> bool {
1207 let cur = self.peek();
1208 let nxt = self.tokens.get(self.pos + 1);
1209 let is_unique = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("unique"));
1210 let is_lparen = matches!(nxt, Some(Token::LParen));
1211 is_unique && is_lparen
1212 }
1213
1214 fn parse_table_level_primary_key(
1215 &mut self,
1216 ) -> Result<crate::ast::TableConstraint, ParseError> {
1217 self.advance(); self.advance(); let columns = self.parse_paren_ident_list("PRIMARY KEY")?;
1220 Ok(crate::ast::TableConstraint::PrimaryKey {
1221 name: None,
1222 columns,
1223 })
1224 }
1225
1226 fn parse_table_level_unique(
1227 &mut self,
1228 ) -> Result<crate::ast::TableConstraint, ParseError> {
1229 self.advance(); let columns = self.parse_paren_ident_list("UNIQUE")?;
1231 Ok(crate::ast::TableConstraint::Unique {
1232 name: None,
1233 columns,
1234 })
1235 }
1236
1237 fn parse_paren_ident_list(
1238 &mut self,
1239 ctx: &str,
1240 ) -> Result<Vec<String>, ParseError> {
1241 if !matches!(self.peek(), Token::LParen) {
1242 return Err(self.err(alloc::format!(
1243 "expected '(' after {ctx}, got {:?}",
1244 self.peek()
1245 )));
1246 }
1247 self.advance();
1248 let mut out = Vec::new();
1249 loop {
1250 out.push(self.expect_ident_like()?);
1251 match self.peek() {
1252 Token::Comma => {
1253 self.advance();
1254 }
1255 Token::RParen => {
1256 self.advance();
1257 break;
1258 }
1259 other => {
1260 return Err(self.err(alloc::format!(
1261 "expected ',' or ')' in {ctx} list, got {other:?}"
1262 )));
1263 }
1264 }
1265 }
1266 if out.is_empty() {
1267 return Err(self.err(alloc::format!("{ctx} requires at least one column")));
1268 }
1269 Ok(out)
1270 }
1271
1272 fn peek_constraint_or_fk_start(&self) -> bool {
1277 let is_constraint_kw = matches!(
1278 self.peek(),
1279 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
1280 );
1281 let is_foreign_kw = matches!(
1282 self.peek(),
1283 Token::Ident(s) if s.eq_ignore_ascii_case("foreign")
1284 );
1285 is_constraint_kw || is_foreign_kw
1286 }
1287
1288 fn parse_table_level_fk(&mut self) -> Result<ForeignKeyConstraint, ParseError> {
1292 let mut name: Option<String> = None;
1293 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
1294 self.advance();
1295 name = Some(self.expect_ident_like()?);
1296 }
1297 match self.advance() {
1299 Token::Ident(s) if s.eq_ignore_ascii_case("foreign") => {}
1300 other => return Err(self.err(format!("expected FOREIGN, got {other:?}"))),
1301 }
1302 match self.advance() {
1304 Token::Ident(s) if s.eq_ignore_ascii_case("key") => {}
1305 other => return Err(self.err(format!("expected KEY after FOREIGN, got {other:?}"))),
1306 }
1307 if !matches!(self.peek(), Token::LParen) {
1309 return Err(self.err(format!("expected '(' after FOREIGN KEY, got {:?}", self.peek())));
1310 }
1311 self.advance();
1312 let mut columns = Vec::new();
1313 loop {
1314 columns.push(self.expect_ident_like()?);
1315 match self.peek() {
1316 Token::Comma => {
1317 self.advance();
1318 }
1319 Token::RParen => {
1320 self.advance();
1321 break;
1322 }
1323 other => return Err(self.err(format!("expected ',' or ')' in FK column list, got {other:?}"))),
1324 }
1325 }
1326 if columns.is_empty() {
1327 return Err(self.err("FOREIGN KEY requires at least one column".into()));
1328 }
1329 let (parent_table, parent_columns, on_delete, on_update) =
1330 self.parse_references_tail(columns.len())?;
1331 Ok(ForeignKeyConstraint {
1332 name,
1333 columns,
1334 parent_table,
1335 parent_columns,
1336 on_delete,
1337 on_update,
1338 })
1339 }
1340
1341 fn parse_references_tail(
1346 &mut self,
1347 expected_arity: usize,
1348 ) -> Result<(String, Vec<String>, FkAction, FkAction), ParseError> {
1349 match self.advance() {
1350 Token::Ident(s) if s.eq_ignore_ascii_case("references") => {}
1351 other => return Err(self.err(format!("expected REFERENCES, got {other:?}"))),
1352 }
1353 let parent_table = self.expect_ident_like()?;
1354 let mut parent_columns: Vec<String> = Vec::new();
1355 if matches!(self.peek(), Token::LParen) {
1356 self.advance();
1357 loop {
1358 parent_columns.push(self.expect_ident_like()?);
1359 match self.peek() {
1360 Token::Comma => {
1361 self.advance();
1362 }
1363 Token::RParen => {
1364 self.advance();
1365 break;
1366 }
1367 other => return Err(self.err(format!("expected ',' or ')' in REFERENCES column list, got {other:?}"))),
1368 }
1369 }
1370 }
1371 if !parent_columns.is_empty() && parent_columns.len() != expected_arity {
1372 return Err(self.err(format!(
1373 "FK arity mismatch: {} local column(s) vs {} parent column(s)",
1374 expected_arity,
1375 parent_columns.len()
1376 )));
1377 }
1378 loop {
1384 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("deferrable")) {
1385 return Err(self.err(
1386 "DEFERRABLE constraints are not supported (SPG is single-writer; \
1387 constraints are always evaluated immediately at commit)"
1388 .into(),
1389 ));
1390 }
1391 if matches!(self.peek(), Token::Not) {
1392 let look = self.tokens.get(self.pos + 1);
1393 if matches!(look, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("deferrable")) {
1394 self.advance();
1397 self.advance();
1398 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("initially"))
1400 {
1401 self.advance();
1402 match self.advance() {
1403 Token::Ident(s) if s.eq_ignore_ascii_case("immediate") => {}
1404 other => {
1405 return Err(self.err(format!(
1406 "expected IMMEDIATE after INITIALLY for NOT DEFERRABLE, \
1407 got {other:?}"
1408 )));
1409 }
1410 }
1411 }
1412 continue;
1413 }
1414 break;
1415 }
1416 break;
1417 }
1418 let mut on_delete = FkAction::Restrict;
1421 let mut on_update = FkAction::Restrict;
1422 let mut seen_on_delete = false;
1423 let mut seen_on_update = false;
1424 loop {
1425 if !matches!(self.peek(), Token::On) {
1426 break;
1427 }
1428 self.advance();
1429 let which = self.advance();
1430 let action = self.parse_fk_action()?;
1431 match which {
1432 Token::Ident(ref s) if s.eq_ignore_ascii_case("delete") => {
1433 if seen_on_delete {
1434 return Err(self.err("ON DELETE specified twice".into()));
1435 }
1436 seen_on_delete = true;
1437 on_delete = action;
1438 }
1439 Token::Ident(ref s) if s.eq_ignore_ascii_case("update") => {
1440 if seen_on_update {
1441 return Err(self.err("ON UPDATE specified twice".into()));
1442 }
1443 seen_on_update = true;
1444 on_update = action;
1445 }
1446 other => {
1447 return Err(self.err(format!(
1448 "expected DELETE or UPDATE after ON, got {other:?}"
1449 )));
1450 }
1451 }
1452 }
1453 Ok((parent_table, parent_columns, on_delete, on_update))
1454 }
1455
1456 fn parse_fk_action(&mut self) -> Result<FkAction, ParseError> {
1459 match self.advance() {
1460 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => Ok(FkAction::Cascade),
1461 Token::Ident(s) if s.eq_ignore_ascii_case("restrict") => Ok(FkAction::Restrict),
1462 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
1463 match self.advance() {
1464 Token::Null => Ok(FkAction::SetNull),
1465 Token::Default => Ok(FkAction::SetDefault),
1466 other => Err(self.err(format!(
1467 "expected NULL or DEFAULT after SET in FK action, got {other:?}"
1468 ))),
1469 }
1470 }
1471 Token::Ident(s) if s.eq_ignore_ascii_case("no") => {
1472 match self.advance() {
1473 Token::Ident(s) if s.eq_ignore_ascii_case("action") => Ok(FkAction::NoAction),
1474 other => Err(self.err(format!(
1475 "expected ACTION after NO in FK action, got {other:?}"
1476 ))),
1477 }
1478 }
1479 other => Err(self.err(format!(
1480 "expected CASCADE | RESTRICT | SET NULL | SET DEFAULT | NO ACTION, got {other:?}"
1481 ))),
1482 }
1483 }
1484
1485 fn consume_if_not_exists(&mut self) -> bool {
1488 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
1492 if !looks_like_if {
1493 return false;
1494 }
1495 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Not)) {
1498 return false;
1499 }
1500 if !matches!(
1501 self.tokens.get(self.pos + 2),
1502 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
1503 ) {
1504 return false;
1505 }
1506 self.advance(); self.advance(); self.advance(); true
1510 }
1511
1512 fn consume_optional_index_column_qualifiers(&mut self) {
1518 loop {
1519 match self.peek() {
1520 Token::Asc | Token::Desc => {
1521 self.advance();
1522 }
1523 Token::Ident(s) if s.eq_ignore_ascii_case("nulls") => {
1524 let look = self.tokens.get(self.pos + 1);
1525 if matches!(
1526 look,
1527 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("first")
1528 || k.eq_ignore_ascii_case("last")
1529 ) {
1530 self.advance();
1531 self.advance();
1532 } else {
1533 break;
1534 }
1535 }
1536 _ => break,
1537 }
1538 }
1539 }
1540
1541 fn parse_create_index_stmt_after_create(
1542 &mut self,
1543 is_unique: bool,
1544 ) -> Result<Statement, ParseError> {
1545 debug_assert!(matches!(self.peek(), Token::Index));
1547 self.advance();
1548 let if_not_exists = self.consume_if_not_exists();
1549 let name = self.expect_ident_like()?;
1550 if !matches!(self.peek(), Token::On) {
1551 return Err(self.err(format!(
1552 "expected ON after CREATE INDEX <name>, got {:?}",
1553 self.peek()
1554 )));
1555 }
1556 self.advance();
1557 let table = self.expect_ident_like()?;
1558 let method = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
1563 self.advance();
1564 let m = self.expect_ident_like()?;
1565 match m.to_ascii_lowercase().as_str() {
1566 "hnsw" => IndexMethod::Hnsw,
1567 "btree" => IndexMethod::BTree,
1568 "brin" => IndexMethod::Brin,
1569 "gin" | "gist" | "spgist" | "hash" => IndexMethod::BTree,
1578 other => {
1579 return Err(self.err(alloc::format!(
1580 "unknown index method {other:?}; supported: hnsw, btree, brin (gin/gist/spgist/hash accepted as BTree fallback)"
1581 )));
1582 }
1583 }
1584 } else {
1585 IndexMethod::BTree
1586 };
1587 if !matches!(self.peek(), Token::LParen) {
1588 return Err(self.err(format!(
1589 "expected '(' before indexed column, got {:?}",
1590 self.peek()
1591 )));
1592 }
1593 self.advance();
1594 let (column, expression): (String, Option<Expr>) = match self.peek().clone() {
1603 Token::Ident(s) | Token::QuotedIdent(s)
1610 if matches!(
1611 self.tokens.get(self.pos + 1),
1612 Some(Token::RParen | Token::Comma)
1613 ) =>
1614 {
1615 self.advance();
1616 (s, None)
1617 }
1618 Token::Ident(s) | Token::QuotedIdent(s)
1626 if matches!(
1627 self.tokens.get(self.pos + 1),
1628 Some(Token::Ident(op) | Token::QuotedIdent(op))
1629 if is_vector_opclass_name(op)
1630 ) =>
1631 {
1632 self.advance(); self.advance(); (s, None)
1635 }
1636 Token::Ident(_) | Token::QuotedIdent(_) => {
1637 let key_expr = self.parse_expr(0)?;
1638 let primary = extract_first_column(&key_expr).ok_or_else(|| {
1639 self.err(
1640 "expression index key must reference at least one column".into(),
1641 )
1642 })?;
1643 (primary, Some(key_expr))
1644 }
1645 other => {
1646 return Err(self.err(format!(
1647 "expected column ident or expression, got {other:?}"
1648 )));
1649 }
1650 };
1651 let mut extra_columns: Vec<String> = Vec::new();
1660 self.consume_optional_index_column_qualifiers();
1662 while matches!(self.peek(), Token::Comma) {
1663 self.advance();
1664 let extra = self.expect_ident_like()?;
1665 self.consume_optional_index_column_qualifiers();
1666 extra_columns.push(extra);
1667 }
1668 if !matches!(self.peek(), Token::RParen) {
1669 return Err(self.err(format!(
1670 "expected ')' after indexed column / expression, got {:?}",
1671 self.peek()
1672 )));
1673 }
1674 self.advance();
1675 let included_columns =
1679 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("include")) {
1680 self.advance();
1681 if !matches!(self.peek(), Token::LParen) {
1682 return Err(self.err(format!(
1683 "expected '(' after INCLUDE, got {:?}",
1684 self.peek()
1685 )));
1686 }
1687 self.advance();
1688 let mut cols = Vec::new();
1689 loop {
1690 cols.push(self.expect_ident_like()?);
1691 match self.peek() {
1692 Token::Comma => {
1693 self.advance();
1694 }
1695 Token::RParen => {
1696 self.advance();
1697 break;
1698 }
1699 other => {
1700 return Err(self.err(format!(
1701 "expected ',' or ')' in INCLUDE list, got {other:?}"
1702 )));
1703 }
1704 }
1705 }
1706 cols
1707 } else {
1708 Vec::new()
1709 };
1710 let partial_predicate = if matches!(self.peek(), Token::Where) {
1712 self.advance();
1713 Some(self.parse_expr(0)?)
1714 } else {
1715 None
1716 };
1717 if is_unique && !matches!(method, IndexMethod::BTree) {
1722 return Err(self.err(alloc::format!(
1723 "UNIQUE is only supported on BTree indexes, got USING {:?}",
1724 method
1725 )));
1726 }
1727 Ok(Statement::CreateIndex(CreateIndexStatement {
1728 name,
1729 table,
1730 column,
1731 method,
1732 if_not_exists,
1733 included_columns,
1734 partial_predicate,
1735 extra_columns: extra_columns.clone(),
1736 expression,
1737 is_unique,
1738 }))
1739 }
1740
1741 fn parse_column_def_with_fk(
1746 &mut self,
1747 ) -> Result<(ColumnDef, Option<ForeignKeyConstraint>), ParseError> {
1748 let col = self.parse_column_def()?;
1749 let inline_references = matches!(
1751 self.peek(),
1752 Token::Ident(s) if s.eq_ignore_ascii_case("references")
1753 );
1754 if !inline_references {
1755 return Ok((col, None));
1756 }
1757 let (parent_table, parent_columns, on_delete, on_update) =
1758 self.parse_references_tail(1)?;
1759 let fk = ForeignKeyConstraint {
1760 name: None,
1761 columns: vec![col.name.clone()],
1762 parent_table,
1763 parent_columns,
1764 on_delete,
1765 on_update,
1766 };
1767 Ok((col, Some(fk)))
1768 }
1769
1770 fn parse_column_def(&mut self) -> Result<ColumnDef, ParseError> {
1771 let name = self.expect_ident_like()?;
1772 let ty_ident = match self.advance() {
1775 Token::Ident(s) => s,
1776 other => {
1777 return Err(ParseError {
1778 message: format!("expected column type, got {other:?}"),
1779 token_pos: self.pos.saturating_sub(1),
1780 });
1781 }
1782 };
1783 let mut implied_auto_increment = false;
1790 let mut implied_not_null = false;
1791 let mut ty = match ty_ident.as_str() {
1792 "smallserial" | "serial2" => {
1794 implied_auto_increment = true;
1795 implied_not_null = true;
1796 ColumnTypeName::SmallInt
1797 }
1798 "serial" | "serial4" => {
1799 implied_auto_increment = true;
1800 implied_not_null = true;
1801 ColumnTypeName::Int
1802 }
1803 "bigserial" | "serial8" => {
1804 implied_auto_increment = true;
1805 implied_not_null = true;
1806 ColumnTypeName::BigInt
1807 }
1808 "smallint" | "tinyint" => ColumnTypeName::SmallInt,
1814 "int" | "integer" | "mediumint" => ColumnTypeName::Int,
1816 "bigint" => ColumnTypeName::BigInt,
1817 "float" | "double" | "real" => ColumnTypeName::Float,
1819 "text" => ColumnTypeName::Text,
1820 "bool" | "boolean" => ColumnTypeName::Bool,
1821 "varchar" => ColumnTypeName::Varchar(self.parse_paren_size("VARCHAR")?),
1822 "char" => ColumnTypeName::Char(self.parse_paren_size("CHAR")?),
1823 "vector" => {
1824 let dim = self.parse_paren_size("VECTOR")?;
1825 let encoding = self.parse_optional_vector_encoding()?;
1826 ColumnTypeName::Vector { dim, encoding }
1827 }
1828 "numeric" => {
1829 let (precision, scale) = self.parse_optional_numeric_params()?;
1830 ColumnTypeName::Numeric(precision, scale)
1831 }
1832 "date" => ColumnTypeName::Date,
1833 "timestamp" | "datetime" => ColumnTypeName::Timestamp,
1836 "timestamptz" => ColumnTypeName::Timestamptz,
1840 "json" => ColumnTypeName::Json,
1845 "jsonb" => ColumnTypeName::Jsonb,
1846 "bytea" | "bytes" => ColumnTypeName::Bytes,
1852 other => {
1853 return Err(ParseError {
1854 message: format!("unsupported column type {other:?}"),
1855 token_pos: self.pos.saturating_sub(1),
1856 });
1857 }
1858 };
1859 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unsigned")) {
1864 self.advance();
1865 }
1866 if matches!(self.peek(), Token::LBracket) {
1871 self.advance();
1872 if !matches!(self.peek(), Token::RBracket) {
1873 return Err(self.err(alloc::format!(
1874 "TEXT[] takes no dimension; got {:?}",
1875 self.peek()
1876 )));
1877 }
1878 self.advance();
1879 if !matches!(ty, ColumnTypeName::Text) {
1880 return Err(self.err(alloc::format!(
1881 "v7.10 only supports TEXT[]; got {ty:?}[]"
1882 )));
1883 }
1884 ty = ColumnTypeName::TextArray;
1885 }
1886 let mut default: Option<Expr> = None;
1890 let mut nullable = !implied_not_null;
1891 let mut nullability_seen = implied_not_null;
1892 let mut auto_increment = implied_auto_increment;
1893 let mut is_primary_key = false;
1894 loop {
1895 if matches!(self.peek(), Token::Default) {
1896 if default.is_some() {
1897 return Err(self.err("DEFAULT specified twice".into()));
1898 }
1899 self.advance();
1900 default = Some(self.parse_expr(0)?);
1901 continue;
1902 }
1903 if matches!(self.peek(), Token::Not) {
1904 if nullability_seen {
1905 return Err(self.err("NOT NULL specified twice".into()));
1906 }
1907 self.advance();
1908 if !matches!(self.peek(), Token::Null) {
1909 return Err(self.err(format!(
1910 "expected NULL after NOT in column def, got {:?}",
1911 self.peek()
1912 )));
1913 }
1914 self.advance();
1915 nullable = false;
1916 nullability_seen = true;
1917 continue;
1918 }
1919 if let Token::Ident(s) = self.peek()
1922 && (s.eq_ignore_ascii_case("auto_increment")
1923 || s.eq_ignore_ascii_case("autoincrement"))
1924 {
1925 if auto_increment {
1926 return Err(self.err("AUTO_INCREMENT specified twice".into()));
1927 }
1928 self.advance();
1929 auto_increment = true;
1930 continue;
1931 }
1932 if let Token::Ident(s) = self.peek()
1937 && s.eq_ignore_ascii_case("primary")
1938 {
1939 if is_primary_key {
1940 return Err(self.err("PRIMARY KEY specified twice".into()));
1941 }
1942 let next = self.tokens.get(self.pos + 1);
1944 let next_is_key = matches!(
1945 next,
1946 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("key")
1947 );
1948 if !next_is_key {
1949 return Err(self.err(format!(
1950 "expected KEY after PRIMARY in column def, got {:?}",
1951 next
1952 )));
1953 }
1954 self.advance(); self.advance(); is_primary_key = true;
1957 if nullability_seen && nullable {
1958 return Err(self.err(
1959 "column declared NULL but inline PRIMARY KEY implies NOT NULL".into(),
1960 ));
1961 }
1962 nullable = false;
1963 nullability_seen = true;
1964 continue;
1965 }
1966 break;
1967 }
1968 Ok(ColumnDef {
1969 name,
1970 ty,
1971 nullable,
1972 default,
1973 auto_increment,
1974 is_primary_key,
1975 })
1976 }
1977
1978 fn parse_optional_numeric_params(&mut self) -> Result<(u8, u8), ParseError> {
1982 if !matches!(self.peek(), Token::LParen) {
1983 return Ok((0, 0));
1987 }
1988 self.advance();
1989 let precision = match self.advance() {
1990 Token::Integer(n) if (1..=38).contains(&n) => u8::try_from(n).expect("range-checked"),
1991 other => {
1992 return Err(ParseError {
1993 message: format!(
1994 "NUMERIC precision must be an integer in 1..=38, got {other:?}"
1995 ),
1996 token_pos: self.pos.saturating_sub(1),
1997 });
1998 }
1999 };
2000 let scale = if matches!(self.peek(), Token::Comma) {
2001 self.advance();
2002 match self.advance() {
2003 Token::Integer(n) if (0..=i64::from(precision)).contains(&n) => {
2004 u8::try_from(n).expect("range-checked")
2005 }
2006 other => {
2007 return Err(ParseError {
2008 message: format!(
2009 "NUMERIC scale must be a non-negative integer ≤ precision, got {other:?}"
2010 ),
2011 token_pos: self.pos.saturating_sub(1),
2012 });
2013 }
2014 }
2015 } else {
2016 0
2017 };
2018 if !matches!(self.peek(), Token::RParen) {
2019 return Err(self.err(format!(
2020 "expected ')' to close NUMERIC params, got {:?}",
2021 self.peek()
2022 )));
2023 }
2024 self.advance();
2025 Ok((precision, scale))
2026 }
2027
2028 fn parse_optional_vector_encoding(&mut self) -> Result<VecEncoding, ParseError> {
2036 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
2037 return Ok(VecEncoding::F32);
2038 }
2039 self.advance();
2040 let enc_ident = match self.advance() {
2041 Token::Ident(s) => s,
2042 other => {
2043 return Err(self.err(format!(
2044 "expected vector encoding after USING, got {other:?}"
2045 )));
2046 }
2047 };
2048 match enc_ident.to_ascii_lowercase().as_str() {
2049 "sq8" => Ok(VecEncoding::Sq8),
2050 "half" => Ok(VecEncoding::F16),
2053 other => Err(self.err(format!(
2054 "unknown vector encoding {other:?}; supported: SQ8, HALF"
2055 ))),
2056 }
2057 }
2058
2059 fn parse_paren_size(&mut self, label: &str) -> Result<u32, ParseError> {
2060 if !matches!(self.peek(), Token::LParen) {
2061 return Err(self.err(format!("{label} type requires (N), got {:?}", self.peek())));
2062 }
2063 self.advance();
2064 let n = match self.advance() {
2065 Token::Integer(n) if n > 0 => u32::try_from(n).map_err(|_| ParseError {
2066 message: format!("{label} size too large: {n}"),
2067 token_pos: self.pos.saturating_sub(1),
2068 })?,
2069 other => {
2070 return Err(ParseError {
2071 message: format!("expected positive integer {label} size, got {other:?}"),
2072 token_pos: self.pos.saturating_sub(1),
2073 });
2074 }
2075 };
2076 if !matches!(self.peek(), Token::RParen) {
2077 return Err(self.err(format!(
2078 "expected ')' after {label} size, got {:?}",
2079 self.peek()
2080 )));
2081 }
2082 self.advance();
2083 Ok(n)
2084 }
2085
2086 fn parse_insert_stmt(&mut self) -> Result<Statement, ParseError> {
2087 debug_assert!(matches!(self.peek(), Token::Insert));
2088 self.advance();
2089 if !matches!(self.peek(), Token::Into) {
2090 return Err(self.err(format!("expected INTO after INSERT, got {:?}", self.peek())));
2091 }
2092 self.advance();
2093 let table = self.expect_ident_like()?;
2094 let columns = if matches!(self.peek(), Token::LParen) {
2096 self.advance();
2097 let mut names = Vec::new();
2098 loop {
2099 names.push(self.expect_ident_like()?);
2100 match self.peek() {
2101 Token::Comma => {
2102 self.advance();
2103 }
2104 Token::RParen => {
2105 self.advance();
2106 break;
2107 }
2108 other => {
2109 return Err(self.err(format!(
2110 "expected ',' or ')' in INSERT column list, got {other:?}"
2111 )));
2112 }
2113 }
2114 }
2115 Some(names)
2116 } else {
2117 None
2118 };
2119 if !matches!(self.peek(), Token::Values) {
2120 return Err(self.err(format!(
2121 "expected VALUES after table name, got {:?}",
2122 self.peek()
2123 )));
2124 }
2125 self.advance();
2126 if !matches!(self.peek(), Token::LParen) {
2127 return Err(self.err(format!("expected '(' after VALUES, got {:?}", self.peek())));
2128 }
2129 let mut rows = Vec::new();
2130 loop {
2131 if !matches!(self.peek(), Token::LParen) {
2133 return Err(self.err(format!(
2134 "expected '(' for next VALUES tuple, got {:?}",
2135 self.peek()
2136 )));
2137 }
2138 self.advance();
2139 let mut tuple = Vec::new();
2140 loop {
2141 tuple.push(self.parse_expr(0)?);
2142 match self.peek() {
2143 Token::Comma => {
2144 self.advance();
2145 }
2146 Token::RParen => {
2147 self.advance();
2148 break;
2149 }
2150 other => {
2151 return Err(self.err(format!(
2152 "expected ',' or ')' in VALUES tuple, got {other:?}"
2153 )));
2154 }
2155 }
2156 }
2157 if tuple.is_empty() {
2158 return Err(self.err("INSERT VALUES tuple requires at least one value".into()));
2159 }
2160 rows.push(tuple);
2161 if matches!(self.peek(), Token::Comma) {
2163 self.advance();
2164 } else {
2165 break;
2166 }
2167 }
2168 let on_conflict = self.parse_optional_on_conflict()?;
2169 let returning = self.parse_optional_returning()?;
2170 Ok(Statement::Insert(InsertStatement {
2171 table,
2172 columns,
2173 rows,
2174 on_conflict,
2175 returning,
2176 }))
2177 }
2178
2179 fn parse_optional_on_conflict(
2184 &mut self,
2185 ) -> Result<Option<crate::ast::OnConflictClause>, ParseError> {
2186 if !matches!(self.peek(), Token::On) {
2187 return Ok(None);
2188 }
2189 let next_is_conflict = matches!(
2192 self.tokens.get(self.pos + 1),
2193 Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("conflict")
2194 );
2195 if !next_is_conflict {
2196 return Ok(None);
2197 }
2198 self.advance(); self.advance(); let mut target_columns: Vec<String> = Vec::new();
2202 if matches!(self.peek(), Token::LParen) {
2203 self.advance();
2204 loop {
2205 target_columns.push(self.expect_ident_like()?);
2206 match self.peek() {
2207 Token::Comma => {
2208 self.advance();
2209 }
2210 Token::RParen => {
2211 self.advance();
2212 break;
2213 }
2214 other => {
2215 return Err(self.err(alloc::format!(
2216 "expected ',' or ')' in ON CONFLICT target list, got {other:?}"
2217 )));
2218 }
2219 }
2220 }
2221 }
2222 match self.advance() {
2224 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {}
2225 other => {
2226 return Err(self.err(alloc::format!(
2227 "expected DO after ON CONFLICT [(…)], got {other:?}"
2228 )));
2229 }
2230 }
2231 let action = match self.advance() {
2233 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing") => {
2234 crate::ast::OnConflictAction::Nothing
2235 }
2236 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
2237 self.parse_on_conflict_update_action()?
2238 }
2239 other => {
2240 return Err(self.err(alloc::format!(
2241 "expected NOTHING or UPDATE after ON CONFLICT DO, got {other:?}"
2242 )));
2243 }
2244 };
2245 Ok(Some(crate::ast::OnConflictClause {
2246 target_columns,
2247 action,
2248 }))
2249 }
2250
2251 fn parse_on_conflict_update_action(
2255 &mut self,
2256 ) -> Result<crate::ast::OnConflictAction, ParseError> {
2257 match self.advance() {
2259 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {}
2260 other => {
2261 return Err(self.err(alloc::format!(
2262 "expected SET after ON CONFLICT DO UPDATE, got {other:?}"
2263 )));
2264 }
2265 }
2266 let mut assignments: Vec<(String, Expr)> = Vec::new();
2267 loop {
2268 let col = self.expect_ident_like()?;
2269 if !matches!(self.peek(), Token::Eq) {
2270 return Err(self.err(alloc::format!(
2271 "expected `=` after column in ON CONFLICT DO UPDATE SET, got {:?}",
2272 self.peek()
2273 )));
2274 }
2275 self.advance();
2276 let value = self.parse_expr(0)?;
2277 assignments.push((col, value));
2278 if matches!(self.peek(), Token::Comma) {
2279 self.advance();
2280 continue;
2281 }
2282 break;
2283 }
2284 let where_ = if matches!(self.peek(), Token::Where) {
2285 self.advance();
2286 Some(self.parse_expr(0)?)
2287 } else {
2288 None
2289 };
2290 Ok(crate::ast::OnConflictAction::Update {
2291 assignments,
2292 where_,
2293 })
2294 }
2295
2296 fn parse_select_list(&mut self) -> Result<Vec<SelectItem>, ParseError> {
2297 let mut items = Vec::new();
2298 loop {
2299 items.push(self.parse_select_item()?);
2300 if matches!(self.peek(), Token::Comma) {
2301 self.advance();
2302 } else {
2303 break;
2304 }
2305 }
2306 Ok(items)
2307 }
2308
2309 fn parse_select_item(&mut self) -> Result<SelectItem, ParseError> {
2310 if matches!(self.peek(), Token::Star) {
2311 self.advance();
2312 return Ok(SelectItem::Wildcard);
2313 }
2314 let expr = self.parse_expr(0)?;
2315 let alias = self.parse_optional_alias();
2316 Ok(SelectItem::Expr { expr, alias })
2317 }
2318
2319 fn parse_table_ref(&mut self) -> Result<TableRef, ParseError> {
2320 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unnest"))
2324 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
2325 {
2326 self.advance(); self.advance(); let expr = self.parse_expr(0)?;
2329 if !matches!(self.peek(), Token::RParen) {
2330 return Err(self.err(alloc::format!(
2331 "expected ')' after unnest() argument, got {:?}",
2332 self.peek()
2333 )));
2334 }
2335 self.advance();
2336 let alias_ident = self.parse_optional_alias();
2337 let name = alias_ident.clone().unwrap_or_else(|| "unnest".to_string());
2338 return Ok(TableRef {
2339 name,
2340 alias: alias_ident,
2341 as_of_segment: None,
2342 unnest_expr: Some(Box::new(expr)),
2343 });
2344 }
2345 let name = self.expect_ident_like()?;
2346 let as_of_segment = if matches!(self.peek(), Token::As)
2352 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("of"))
2353 {
2354 self.advance(); self.advance(); let kw = match self.peek().clone() {
2357 Token::Ident(s) | Token::QuotedIdent(s) => s,
2358 other => {
2359 return Err(self.err(format!(
2360 "expected SEGMENT after AS OF, got {other:?}"
2361 )));
2362 }
2363 };
2364 if !kw.eq_ignore_ascii_case("segment") {
2365 return Err(self.err(format!(
2366 "expected SEGMENT after AS OF, got {kw:?}; v6.10.2 supports SEGMENT only"
2367 )));
2368 }
2369 self.advance();
2370 let id = match self.advance() {
2373 Token::String(s) => s
2374 .parse::<u32>()
2375 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
2376 Token::Integer(n) => u32::try_from(n).map_err(|e| {
2377 self.err(format!("AS OF SEGMENT id parse: {e}"))
2378 })?,
2379 other => {
2380 return Err(self.err(format!(
2381 "expected segment id literal after AS OF SEGMENT, got {other:?}"
2382 )));
2383 }
2384 };
2385 Some(id)
2386 } else {
2387 None
2388 };
2389 let alias = self.parse_optional_alias();
2390 Ok(TableRef {
2391 name,
2392 alias,
2393 as_of_segment,
2394 unnest_expr: None,
2395 })
2396 }
2397
2398 fn parse_from_clause(&mut self) -> Result<FromClause, ParseError> {
2403 let primary = self.parse_table_ref()?;
2404 let mut joins = Vec::new();
2405 loop {
2406 if matches!(self.peek(), Token::Comma) {
2408 self.advance();
2409 let table = self.parse_table_ref()?;
2410 joins.push(FromJoin {
2411 kind: JoinKind::Cross,
2412 table,
2413 on: None,
2414 });
2415 continue;
2416 }
2417 let kind =
2420 match self.peek() {
2421 Token::Inner => {
2422 self.advance();
2423 if !matches!(self.peek(), Token::Join) {
2424 return Err(self
2425 .err(format!("expected JOIN after INNER, got {:?}", self.peek())));
2426 }
2427 self.advance();
2428 JoinKind::Inner
2429 }
2430 Token::Left => {
2431 self.advance();
2432 if matches!(self.peek(), Token::Outer) {
2433 self.advance();
2434 }
2435 if !matches!(self.peek(), Token::Join) {
2436 return Err(self.err(format!(
2437 "expected JOIN after LEFT [OUTER], got {:?}",
2438 self.peek()
2439 )));
2440 }
2441 self.advance();
2442 JoinKind::Left
2443 }
2444 Token::Cross => {
2445 self.advance();
2446 if !matches!(self.peek(), Token::Join) {
2447 return Err(self
2448 .err(format!("expected JOIN after CROSS, got {:?}", self.peek())));
2449 }
2450 self.advance();
2451 JoinKind::Cross
2452 }
2453 Token::Join => {
2454 self.advance();
2455 JoinKind::Inner
2456 }
2457 _ => break,
2458 };
2459 let table = self.parse_table_ref()?;
2460 let on = if matches!(self.peek(), Token::On) {
2461 self.advance();
2462 Some(self.parse_expr(0)?)
2463 } else if kind == JoinKind::Cross {
2464 None
2465 } else {
2466 return Err(self.err(format!(
2467 "expected ON after {:?} JOIN, got {:?}",
2468 kind,
2469 self.peek()
2470 )));
2471 };
2472 joins.push(FromJoin { kind, table, on });
2473 }
2474 Ok(FromClause { primary, joins })
2475 }
2476
2477 fn parse_optional_alias(&mut self) -> Option<String> {
2482 if matches!(self.peek(), Token::As) {
2483 self.advance();
2484 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
2489 return self.expect_ident_like().ok();
2490 }
2491 return None;
2492 }
2493 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
2494 return self.expect_ident_like().ok();
2495 }
2496 None
2497 }
2498
2499 fn parse_expr(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
2501 let mut lhs = self.parse_unary()?;
2502 while let Some((op, prec)) = binop_from(self.peek()) {
2503 if prec < min_prec {
2504 break;
2505 }
2506 self.advance();
2507 let any_kind = match self.peek() {
2512 Token::All if matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) => {
2513 Some(false)
2514 }
2515 Token::Ident(s) | Token::QuotedIdent(s)
2516 if (s.eq_ignore_ascii_case("any") || s.eq_ignore_ascii_case("all"))
2517 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) =>
2518 {
2519 Some(s.eq_ignore_ascii_case("any"))
2520 }
2521 _ => None,
2522 };
2523 if let Some(is_any) = any_kind {
2524 self.advance(); self.advance(); let arr = self.parse_expr(0)?;
2527 if !matches!(self.peek(), Token::RParen) {
2528 return Err(self.err(alloc::format!(
2529 "expected ')' after ANY/ALL argument, got {:?}",
2530 self.peek()
2531 )));
2532 }
2533 self.advance();
2534 lhs = Expr::AnyAll {
2535 expr: Box::new(lhs),
2536 op,
2537 array: Box::new(arr),
2538 is_any,
2539 };
2540 continue;
2541 }
2542 let rhs = self.parse_expr(prec + 1)?;
2543 lhs = Expr::Binary {
2544 lhs: Box::new(lhs),
2545 op,
2546 rhs: Box::new(rhs),
2547 };
2548 }
2549 Ok(lhs)
2550 }
2551
2552 fn parse_unary(&mut self) -> Result<Expr, ParseError> {
2553 match self.peek() {
2554 Token::Not => {
2555 self.advance();
2556 let e = self.parse_expr(3)?;
2559 Ok(Expr::Unary {
2560 op: UnOp::Not,
2561 expr: Box::new(e),
2562 })
2563 }
2564 Token::Minus => {
2565 self.advance();
2566 let e = self.parse_expr(8)?;
2569 Ok(Expr::Unary {
2570 op: UnOp::Neg,
2571 expr: Box::new(e),
2572 })
2573 }
2574 _ => self.parse_atom(),
2575 }
2576 }
2577
2578 fn parse_atom(&mut self) -> Result<Expr, ParseError> {
2579 let tok_pos = self.pos;
2580 match self.advance() {
2581 Token::Integer(n) => Ok(Expr::Literal(Literal::Integer(n))),
2582 Token::Float(x) => Ok(Expr::Literal(Literal::Float(x))),
2583 Token::String(s) => Ok(Expr::Literal(Literal::String(s))),
2584 Token::True => Ok(Expr::Literal(Literal::Bool(true))),
2585 Token::False => Ok(Expr::Literal(Literal::Bool(false))),
2586 Token::Null => Ok(Expr::Literal(Literal::Null)),
2587 Token::Placeholder(n) => Ok(Expr::Placeholder(n)),
2591 Token::LParen => {
2592 if matches!(self.peek(), Token::Select) {
2596 let inner = self.parse_select_stmt()?;
2597 match self.advance() {
2598 Token::RParen => {
2599 let Statement::Select(s) = inner else {
2600 unreachable!("parse_select_stmt returns Select")
2601 };
2602 Ok(Expr::ScalarSubquery(Box::new(s)))
2603 }
2604 other => Err(ParseError {
2605 message: format!("expected ')' after scalar subquery, got {other:?}"),
2606 token_pos: self.pos.saturating_sub(1),
2607 }),
2608 }
2609 } else {
2610 let e = self.parse_expr(0)?;
2611 match self.advance() {
2612 Token::RParen => Ok(e),
2613 other => Err(ParseError {
2614 message: format!("expected ')', got {other:?}"),
2615 token_pos: self.pos.saturating_sub(1),
2616 }),
2617 }
2618 }
2619 }
2620 Token::LBracket => self.parse_vector_literal_body(),
2621 Token::Extract => self.parse_extract_atom(),
2622 Token::Interval => self.parse_interval_atom(),
2623 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists") => {
2628 self.parse_exists_atom(false)
2629 }
2630 Token::Ident(s) | Token::QuotedIdent(s)
2634 if s.eq_ignore_ascii_case("array") && matches!(self.peek(), Token::LBracket) =>
2635 {
2636 self.advance(); let mut items: Vec<Expr> = Vec::new();
2638 if !matches!(self.peek(), Token::RBracket) {
2639 loop {
2640 items.push(self.parse_expr(0)?);
2641 match self.peek() {
2642 Token::Comma => {
2643 self.advance();
2644 }
2645 Token::RBracket => break,
2646 other => {
2647 return Err(self.err(alloc::format!(
2648 "expected ',' or ']' in ARRAY literal, got {other:?}"
2649 )));
2650 }
2651 }
2652 }
2653 }
2654 self.advance(); Ok(Expr::Array(items))
2656 }
2657 Token::Ident(s) | Token::QuotedIdent(s) => self.finish_ident_atom(s),
2658 other => Err(ParseError {
2659 message: format!("unexpected token {other:?} in expression"),
2660 token_pos: tok_pos,
2661 }),
2662 }
2663 .and_then(|atom| self.finish_postfix_casts(atom))
2665 }
2666
2667 fn finish_postfix_casts(&mut self, mut expr: Expr) -> Result<Expr, ParseError> {
2670 loop {
2671 if matches!(self.peek(), Token::DoubleColon) {
2672 self.advance();
2673 let target = match self.advance() {
2678 Token::Ident(s) => match s.to_ascii_lowercase().as_str() {
2679 "int" | "integer" | "int4" => CastTarget::Int,
2680 "bigint" | "int8" => CastTarget::BigInt,
2681 "float" | "double" | "real" => CastTarget::Float,
2682 "text" => {
2683 if matches!(self.peek(), Token::LBracket)
2685 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
2686 {
2687 self.advance();
2688 self.advance();
2689 CastTarget::TextArray
2690 } else {
2691 CastTarget::Text
2692 }
2693 }
2694 "bool" | "boolean" => CastTarget::Bool,
2695 "vector" => CastTarget::Vector,
2696 "date" => CastTarget::Date,
2697 "timestamp" | "datetime" => CastTarget::Timestamp,
2698 "timestamptz" => CastTarget::Timestamptz,
2699 "interval" => CastTarget::Interval,
2700 "json" => CastTarget::Json,
2701 "jsonb" => CastTarget::Jsonb,
2702 "regtype" => CastTarget::RegType,
2703 "regclass" => CastTarget::RegClass,
2704 other => {
2705 return Err(ParseError {
2706 message: format!("unsupported cast target `::{other}`"),
2707 token_pos: self.pos.saturating_sub(1),
2708 });
2709 }
2710 },
2711 Token::Interval => CastTarget::Interval,
2712 other => {
2713 return Err(ParseError {
2714 message: format!("expected type ident after `::`, got {other:?}"),
2715 token_pos: self.pos.saturating_sub(1),
2716 });
2717 }
2718 };
2719 expr = Expr::Cast {
2720 expr: Box::new(expr),
2721 target,
2722 };
2723 continue;
2724 }
2725 if matches!(self.peek(), Token::Is) {
2726 self.advance();
2727 let negated = if matches!(self.peek(), Token::Not) {
2728 self.advance();
2729 true
2730 } else {
2731 false
2732 };
2733 if matches!(self.peek(), Token::Distinct) {
2736 self.advance();
2737 if !matches!(self.peek(), Token::From) {
2738 return Err(self.err(format!(
2739 "expected FROM after IS{} DISTINCT, got {:?}",
2740 if negated { " NOT" } else { "" },
2741 self.peek()
2742 )));
2743 }
2744 self.advance();
2745 let rhs = self.parse_expr(20)?;
2749 let op = if negated {
2750 BinOp::IsNotDistinctFrom
2751 } else {
2752 BinOp::IsDistinctFrom
2753 };
2754 expr = Expr::Binary {
2755 op,
2756 lhs: Box::new(expr),
2757 rhs: Box::new(rhs),
2758 };
2759 continue;
2760 }
2761 if !matches!(self.peek(), Token::Null) {
2762 return Err(self.err(format!(
2763 "expected NULL or DISTINCT after IS{}, got {:?}",
2764 if negated { " NOT" } else { "" },
2765 self.peek()
2766 )));
2767 }
2768 self.advance();
2769 expr = Expr::IsNull {
2770 expr: Box::new(expr),
2771 negated,
2772 };
2773 continue;
2774 }
2775 let negated = if matches!(self.peek(), Token::Not) {
2779 let next = self.tokens.get(self.pos + 1);
2780 matches!(next, Some(Token::Between | Token::In | Token::Like))
2781 } else {
2782 false
2783 };
2784 if negated {
2785 self.advance();
2786 }
2787 if matches!(self.peek(), Token::Between) {
2788 expr = self.parse_between_tail(expr, negated)?;
2789 continue;
2790 }
2791 if matches!(self.peek(), Token::In) {
2792 expr = self.parse_in_tail(expr, negated)?;
2793 continue;
2794 }
2795 if matches!(self.peek(), Token::Like) {
2796 self.advance();
2797 let pattern = self.parse_expr(5)?;
2800 expr = Expr::Like {
2801 expr: Box::new(expr),
2802 pattern: Box::new(pattern),
2803 negated,
2804 };
2805 continue;
2806 }
2807 if matches!(self.peek(), Token::LBracket) {
2811 self.advance();
2812 let index = self.parse_expr(0)?;
2813 if !matches!(self.peek(), Token::RBracket) {
2814 return Err(self.err(alloc::format!(
2815 "expected ']' after array index, got {:?}",
2816 self.peek()
2817 )));
2818 }
2819 self.advance();
2820 expr = Expr::ArraySubscript {
2821 target: Box::new(expr),
2822 index: Box::new(index),
2823 };
2824 continue;
2825 }
2826 return Ok(expr);
2827 }
2828 }
2829
2830 fn parse_between_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
2834 self.advance(); let low = self.parse_expr(5)?;
2836 if !matches!(self.peek(), Token::And) {
2837 return Err(self.err(format!(
2838 "expected AND after BETWEEN low bound, got {:?}",
2839 self.peek()
2840 )));
2841 }
2842 self.advance();
2843 let high = self.parse_expr(5)?;
2844 let target = Box::new(expr);
2845 let combined = Expr::Binary {
2846 lhs: Box::new(Expr::Binary {
2847 lhs: target.clone(),
2848 op: BinOp::GtEq,
2849 rhs: Box::new(low),
2850 }),
2851 op: BinOp::And,
2852 rhs: Box::new(Expr::Binary {
2853 lhs: target,
2854 op: BinOp::LtEq,
2855 rhs: Box::new(high),
2856 }),
2857 };
2858 Ok(maybe_not(combined, negated))
2859 }
2860
2861 fn parse_with_cte_then_select(&mut self) -> Result<Statement, ParseError> {
2866 let mut recursive = false;
2871 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
2872 && s.eq_ignore_ascii_case("recursive")
2873 {
2874 self.advance();
2875 recursive = true;
2876 }
2877 let mut ctes = Vec::new();
2878 loop {
2879 let name = self.expect_ident_like()?;
2880 let column_overrides: Vec<String> = if matches!(self.peek(), Token::LParen) {
2884 self.advance();
2885 let mut names = Vec::new();
2886 loop {
2887 names.push(self.expect_ident_like()?);
2888 if matches!(self.peek(), Token::Comma) {
2889 self.advance();
2890 continue;
2891 }
2892 break;
2893 }
2894 if !matches!(self.peek(), Token::RParen) {
2895 return Err(self.err(format!(
2896 "expected ')' to close CTE column list, got {:?}",
2897 self.peek()
2898 )));
2899 }
2900 self.advance();
2901 names
2902 } else {
2903 Vec::new()
2904 };
2905 if !matches!(self.peek(), Token::As) {
2909 return Err(self.err(format!(
2910 "expected AS after CTE name {name:?}, got {:?}",
2911 self.peek()
2912 )));
2913 }
2914 self.advance();
2915 if !matches!(self.peek(), Token::LParen) {
2916 return Err(self.err(format!(
2917 "expected '(' after AS in WITH clause, got {:?}",
2918 self.peek()
2919 )));
2920 }
2921 self.advance();
2922 if !matches!(self.peek(), Token::Select) {
2923 return Err(self.err(format!("WITH body must be a SELECT, got {:?}", self.peek())));
2924 }
2925 let inner = self.parse_select_stmt()?;
2926 if !matches!(self.peek(), Token::RParen) {
2927 return Err(self.err(format!(
2928 "expected ')' after CTE body, got {:?}",
2929 self.peek()
2930 )));
2931 }
2932 self.advance();
2933 let Statement::Select(body) = inner else {
2934 unreachable!("parse_select_stmt returns Select")
2935 };
2936 ctes.push(crate::ast::Cte {
2937 name,
2938 body,
2939 recursive,
2940 column_overrides,
2941 });
2942 if matches!(self.peek(), Token::Comma) {
2943 self.advance();
2944 continue;
2945 }
2946 break;
2947 }
2948 if !matches!(self.peek(), Token::Select) {
2950 return Err(self.err(format!(
2951 "expected SELECT after WITH clause, got {:?}",
2952 self.peek()
2953 )));
2954 }
2955 let body_stmt = self.parse_select_stmt()?;
2956 let Statement::Select(mut body) = body_stmt else {
2957 unreachable!()
2958 };
2959 body.ctes = ctes;
2960 Ok(Statement::Select(body))
2961 }
2962
2963 fn parse_exists_atom(&mut self, negated: bool) -> Result<Expr, ParseError> {
2967 if !matches!(self.peek(), Token::LParen) {
2968 return Err(self.err(format!("expected '(' after EXISTS, got {:?}", self.peek())));
2969 }
2970 self.advance();
2971 let inner = self.parse_select_stmt()?;
2972 if !matches!(self.peek(), Token::RParen) {
2973 return Err(self.err(format!(
2974 "expected ')' after EXISTS-subquery, got {:?}",
2975 self.peek()
2976 )));
2977 }
2978 self.advance();
2979 let Statement::Select(s) = inner else {
2980 unreachable!("parse_select_stmt returns Select")
2981 };
2982 Ok(Expr::Exists {
2983 subquery: Box::new(s),
2984 negated,
2985 })
2986 }
2987
2988 fn parse_in_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
2989 self.advance(); if !matches!(self.peek(), Token::LParen) {
2991 return Err(self.err(format!("expected '(' after IN, got {:?}", self.peek())));
2992 }
2993 self.advance();
2994 if matches!(self.peek(), Token::Select) {
2996 let inner = self.parse_select_stmt()?;
2997 if !matches!(self.peek(), Token::RParen) {
2998 return Err(self.err(format!(
2999 "expected ')' after IN-subquery, got {:?}",
3000 self.peek()
3001 )));
3002 }
3003 self.advance();
3004 let Statement::Select(s) = inner else {
3005 unreachable!("parse_select_stmt always returns Statement::Select")
3006 };
3007 return Ok(Expr::InSubquery {
3008 expr: Box::new(expr),
3009 subquery: Box::new(s),
3010 negated,
3011 });
3012 }
3013 let mut elements = Vec::new();
3014 if !matches!(self.peek(), Token::RParen) {
3015 loop {
3016 elements.push(self.parse_expr(0)?);
3017 match self.peek() {
3018 Token::Comma => {
3019 self.advance();
3020 }
3021 Token::RParen => break,
3022 other => {
3023 return Err(
3024 self.err(format!("expected ',' or ')' in IN list, got {other:?}"))
3025 );
3026 }
3027 }
3028 }
3029 }
3030 self.advance(); let target = Box::new(expr);
3032 let combined = if elements.is_empty() {
3033 Expr::Literal(Literal::Bool(false))
3034 } else {
3035 let mut iter = elements.into_iter();
3036 let first = iter.next().unwrap();
3037 let mut acc = Expr::Binary {
3038 lhs: target.clone(),
3039 op: BinOp::Eq,
3040 rhs: Box::new(first),
3041 };
3042 for elt in iter {
3043 acc = Expr::Binary {
3044 lhs: Box::new(acc),
3045 op: BinOp::Or,
3046 rhs: Box::new(Expr::Binary {
3047 lhs: target.clone(),
3048 op: BinOp::Eq,
3049 rhs: Box::new(elt),
3050 }),
3051 };
3052 }
3053 acc
3054 };
3055 Ok(maybe_not(combined, negated))
3056 }
3057
3058 fn parse_extract_atom(&mut self) -> Result<Expr, ParseError> {
3066 if !matches!(self.peek(), Token::LParen) {
3067 return Err(self.err(format!("expected '(' after EXTRACT, got {:?}", self.peek())));
3068 }
3069 self.advance();
3070 let field_name = self.expect_ident_like()?;
3071 let field = match field_name.to_ascii_lowercase().as_str() {
3072 "year" => ExtractField::Year,
3073 "month" => ExtractField::Month,
3074 "day" => ExtractField::Day,
3075 "hour" => ExtractField::Hour,
3076 "minute" => ExtractField::Minute,
3077 "second" => ExtractField::Second,
3078 "microsecond" | "microseconds" => ExtractField::Microsecond,
3079 other => {
3080 return Err(self.err(format!(
3081 "unknown EXTRACT field {other:?}; \
3082 supported: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MICROSECOND"
3083 )));
3084 }
3085 };
3086 if !matches!(self.peek(), Token::From) {
3087 return Err(self.err(format!(
3088 "expected FROM after EXTRACT field, got {:?}",
3089 self.peek()
3090 )));
3091 }
3092 self.advance();
3093 let source = self.parse_expr(0)?;
3094 if !matches!(self.peek(), Token::RParen) {
3095 return Err(self.err(format!(
3096 "expected ')' to close EXTRACT, got {:?}",
3097 self.peek()
3098 )));
3099 }
3100 self.advance();
3101 Ok(Expr::Extract {
3102 field,
3103 source: Box::new(source),
3104 })
3105 }
3106
3107 fn parse_interval_atom(&mut self) -> Result<Expr, ParseError> {
3112 let tok = self.advance();
3113 let Token::String(text) = tok else {
3114 return Err(self.err(format!(
3115 "expected string literal after INTERVAL, got {tok:?}"
3116 )));
3117 };
3118 let (months, micros) = parse_interval_text(&text).ok_or_else(|| ParseError {
3119 message: format!(
3120 "cannot parse INTERVAL {text:?}; \
3121 expected `<n> <unit> [<n> <unit> ...]` with units \
3122 microsecond[s], millisecond[s], second[s], minute[s], \
3123 hour[s], day[s], week[s], month[s], year[s]"
3124 ),
3125 token_pos: self.pos.saturating_sub(1),
3126 })?;
3127 Ok(Expr::Literal(Literal::Interval {
3128 months,
3129 micros,
3130 text,
3131 }))
3132 }
3133
3134 fn parse_vector_literal_body(&mut self) -> Result<Expr, ParseError> {
3135 let mut elems = Vec::new();
3136 if matches!(self.peek(), Token::RBracket) {
3137 self.advance();
3138 return Ok(Expr::Literal(Literal::Vector(elems)));
3139 }
3140 loop {
3141 let e = self.parse_expr(0)?;
3142 let x = extract_numeric_literal(&e).ok_or_else(|| ParseError {
3143 message: format!("vector element must be a numeric literal, got {e:?}"),
3144 token_pos: self.pos,
3145 })?;
3146 elems.push(x);
3147 match self.peek() {
3148 Token::Comma => {
3149 self.advance();
3150 }
3151 Token::RBracket => {
3152 self.advance();
3153 break;
3154 }
3155 other => {
3156 return Err(self.err(format!("expected ',' or ']' in vector, got {other:?}")));
3157 }
3158 }
3159 }
3160 Ok(Expr::Literal(Literal::Vector(elems)))
3161 }
3162
3163 fn parse_null_treatment_modifier(&mut self) -> NullTreatment {
3172 let Token::Ident(s) = self.peek().clone() else {
3173 return NullTreatment::Respect;
3174 };
3175 let is_ignore = s.eq_ignore_ascii_case("ignore");
3176 let is_respect = s.eq_ignore_ascii_case("respect");
3177 if !is_ignore && !is_respect {
3178 return NullTreatment::Respect;
3179 }
3180 if self.pos + 1 < self.tokens.len()
3183 && let Token::Ident(s2) = &self.tokens[self.pos + 1]
3184 && s2.eq_ignore_ascii_case("nulls")
3185 {
3186 self.advance();
3187 self.advance();
3188 return if is_ignore {
3189 NullTreatment::Ignore
3190 } else {
3191 NullTreatment::Respect
3192 };
3193 }
3194 NullTreatment::Respect
3195 }
3196
3197 #[allow(clippy::type_complexity)] fn parse_over_clause(
3200 &mut self,
3201 ) -> Result<(Vec<Expr>, Vec<(Expr, bool)>, Option<WindowFrame>), ParseError> {
3202 if !matches!(self.peek(), Token::LParen) {
3203 return Err(self.err(format!("expected '(' after OVER, got {:?}", self.peek())));
3204 }
3205 self.advance();
3206 let mut partition_by = Vec::new();
3207 let mut order_by = Vec::new();
3208 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
3210 && s.eq_ignore_ascii_case("partition")
3211 {
3212 self.advance();
3213 if !matches!(self.peek(), Token::By) {
3214 return Err(self.err(format!(
3215 "expected BY after PARTITION, got {:?}",
3216 self.peek()
3217 )));
3218 }
3219 self.advance();
3220 loop {
3221 partition_by.push(self.parse_expr(0)?);
3222 if matches!(self.peek(), Token::Comma) {
3223 self.advance();
3224 continue;
3225 }
3226 break;
3227 }
3228 }
3229 if matches!(self.peek(), Token::Order) {
3231 self.advance();
3232 if !matches!(self.peek(), Token::By) {
3233 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
3234 }
3235 self.advance();
3236 loop {
3237 let e = self.parse_expr(0)?;
3238 let desc = if matches!(self.peek(), Token::Desc) {
3239 self.advance();
3240 true
3241 } else if matches!(self.peek(), Token::Asc) {
3242 self.advance();
3243 false
3244 } else {
3245 false
3246 };
3247 order_by.push((e, desc));
3248 if matches!(self.peek(), Token::Comma) {
3249 self.advance();
3250 continue;
3251 }
3252 break;
3253 }
3254 }
3255 let mut frame: Option<WindowFrame> = None;
3259 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
3260 let kind = if s.eq_ignore_ascii_case("rows") {
3261 Some(FrameKind::Rows)
3262 } else if s.eq_ignore_ascii_case("range") {
3263 Some(FrameKind::Range)
3264 } else {
3265 None
3266 };
3267 if let Some(kind) = kind {
3268 self.advance();
3269 frame = Some(self.parse_frame_tail(kind)?);
3270 }
3271 }
3272 if !matches!(self.peek(), Token::RParen) {
3273 return Err(self.err(format!(
3274 "expected ')' to close OVER clause, got {:?}",
3275 self.peek()
3276 )));
3277 }
3278 self.advance();
3279 Ok((partition_by, order_by, frame))
3280 }
3281
3282 fn parse_frame_tail(&mut self, kind: FrameKind) -> Result<WindowFrame, ParseError> {
3288 if matches!(self.peek(), Token::Between) {
3289 self.advance();
3290 let start = self.parse_frame_bound()?;
3291 if !matches!(self.peek(), Token::And) {
3292 return Err(self.err(format!("expected AND in frame spec, got {:?}", self.peek())));
3293 }
3294 self.advance();
3295 let end = self.parse_frame_bound()?;
3296 Ok(WindowFrame {
3297 kind,
3298 start,
3299 end: Some(end),
3300 })
3301 } else {
3302 let start = self.parse_frame_bound()?;
3303 Ok(WindowFrame {
3304 kind,
3305 start,
3306 end: None,
3307 })
3308 }
3309 }
3310
3311 fn parse_frame_bound(&mut self) -> Result<FrameBound, ParseError> {
3314 if let Token::Integer(n) = *self.peek() {
3316 self.advance();
3317 let n: u64 = u64::try_from(n).map_err(|_| {
3318 self.err(format!(
3319 "invalid frame offset {n} — expected non-negative integer"
3320 ))
3321 })?;
3322 let dir = self.expect_ident_like()?;
3323 return if dir.eq_ignore_ascii_case("preceding") {
3324 Ok(FrameBound::OffsetPreceding(n))
3325 } else if dir.eq_ignore_ascii_case("following") {
3326 Ok(FrameBound::OffsetFollowing(n))
3327 } else {
3328 Err(self.err(format!(
3329 "expected PRECEDING or FOLLOWING after offset, got {dir:?}"
3330 )))
3331 };
3332 }
3333 let first = self.expect_ident_like()?;
3334 if first.eq_ignore_ascii_case("unbounded") {
3335 let dir = self.expect_ident_like()?;
3336 return if dir.eq_ignore_ascii_case("preceding") {
3337 Ok(FrameBound::UnboundedPreceding)
3338 } else if dir.eq_ignore_ascii_case("following") {
3339 Ok(FrameBound::UnboundedFollowing)
3340 } else {
3341 Err(self.err(format!(
3342 "expected PRECEDING or FOLLOWING after UNBOUNDED, got {dir:?}"
3343 )))
3344 };
3345 }
3346 if first.eq_ignore_ascii_case("current") {
3347 let row = self.expect_ident_like()?;
3348 if !row.eq_ignore_ascii_case("row") {
3349 return Err(self.err(format!("expected ROW after CURRENT, got {row:?}")));
3350 }
3351 return Ok(FrameBound::CurrentRow);
3352 }
3353 Err(self.err(format!(
3354 "expected frame bound (UNBOUNDED/CURRENT/<n>), got {first:?}"
3355 )))
3356 }
3357
3358 fn finish_ident_atom(&mut self, first: String) -> Result<Expr, ParseError> {
3359 if matches!(self.peek(), Token::Dot) {
3360 self.advance();
3361 let name = self.expect_ident_like()?;
3362 return Ok(Expr::Column(ColumnName {
3363 qualifier: Some(first),
3364 name,
3365 }));
3366 }
3367 if matches!(self.peek(), Token::LParen) {
3368 self.advance();
3369 if first.eq_ignore_ascii_case("count") && matches!(self.peek(), Token::Star) {
3373 self.advance();
3374 if !matches!(self.peek(), Token::RParen) {
3375 return Err(self.err(format!(
3376 "expected ')' after COUNT(*), got {:?}",
3377 self.peek()
3378 )));
3379 }
3380 self.advance();
3381 let null_treatment = self.parse_null_treatment_modifier();
3383 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
3384 && s.eq_ignore_ascii_case("over")
3385 {
3386 self.advance();
3387 let (partition_by, order_by, frame) = self.parse_over_clause()?;
3388 return Ok(Expr::WindowFunction {
3389 name: "count_star".into(),
3390 args: Vec::new(),
3391 partition_by,
3392 order_by,
3393 frame,
3394 null_treatment,
3395 });
3396 }
3397 return Ok(Expr::FunctionCall {
3398 name: "count_star".into(),
3399 args: Vec::new(),
3400 });
3401 }
3402 let mut args = Vec::new();
3404 if !matches!(self.peek(), Token::RParen) {
3405 loop {
3406 args.push(self.parse_expr(0)?);
3407 match self.peek() {
3408 Token::Comma => {
3409 self.advance();
3410 }
3411 Token::RParen => break,
3412 other => {
3413 return Err(self.err(format!(
3414 "expected ',' or ')' in function args, got {other:?}"
3415 )));
3416 }
3417 }
3418 }
3419 }
3420 self.advance(); let null_treatment = self.parse_null_treatment_modifier();
3428 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
3429 && s.eq_ignore_ascii_case("over")
3430 {
3431 self.advance();
3432 let (partition_by, order_by, frame) = self.parse_over_clause()?;
3433 return Ok(Expr::WindowFunction {
3434 name: first,
3435 args,
3436 partition_by,
3437 order_by,
3438 frame,
3439 null_treatment,
3440 });
3441 }
3442 return Ok(Expr::FunctionCall { name: first, args });
3443 }
3444 let lc = first.to_ascii_lowercase();
3450 if matches!(
3451 lc.as_str(),
3452 "current_date"
3453 | "current_time"
3454 | "current_timestamp"
3455 | "localtimestamp"
3456 | "localtime"
3457 ) {
3458 return Ok(Expr::FunctionCall {
3459 name: lc,
3460 args: Vec::new(),
3461 });
3462 }
3463 Ok(Expr::Column(ColumnName {
3464 qualifier: None,
3465 name: first,
3466 }))
3467 }
3468}
3469
3470fn extract_first_column(expr: &Expr) -> Option<String> {
3478 match expr {
3479 Expr::Column(cn) => Some(cn.name.clone()),
3480 Expr::FunctionCall { args, .. } => args.iter().find_map(extract_first_column),
3481 Expr::Binary { lhs, rhs, .. } => {
3482 extract_first_column(lhs).or_else(|| extract_first_column(rhs))
3483 }
3484 Expr::Unary { expr: e, .. } => extract_first_column(e),
3485 _ => None,
3486 }
3487}
3488
3489fn maybe_not(expr: Expr, negated: bool) -> Expr {
3490 if negated {
3491 Expr::Unary {
3492 op: UnOp::Not,
3493 expr: Box::new(expr),
3494 }
3495 } else {
3496 expr
3497 }
3498}
3499
3500fn binop_from(tok: &Token) -> Option<(BinOp, u8)> {
3501 let pair = match tok {
3502 Token::Or => (BinOp::Or, 1),
3503 Token::And => (BinOp::And, 2),
3504 Token::Eq => (BinOp::Eq, 4),
3505 Token::NotEq => (BinOp::NotEq, 4),
3506 Token::Lt => (BinOp::Lt, 4),
3507 Token::LtEq => (BinOp::LtEq, 4),
3508 Token::Gt => (BinOp::Gt, 4),
3509 Token::GtEq => (BinOp::GtEq, 4),
3510 Token::L2Distance => (BinOp::L2Distance, 5),
3513 Token::InnerProduct => (BinOp::InnerProduct, 5),
3514 Token::CosineDistance => (BinOp::CosineDistance, 5),
3515 Token::Plus => (BinOp::Add, 6),
3516 Token::Minus => (BinOp::Sub, 6),
3517 Token::Concat => (BinOp::Concat, 6),
3520 Token::Star => (BinOp::Mul, 7),
3521 Token::Slash => (BinOp::Div, 7),
3522 Token::JsonGet => (BinOp::JsonGet, 7),
3526 Token::JsonGetText => (BinOp::JsonGetText, 7),
3527 Token::JsonGetPath => (BinOp::JsonGetPath, 7),
3528 Token::JsonGetPathText => (BinOp::JsonGetPathText, 7),
3529 Token::JsonContains => (BinOp::JsonContains, 7),
3530 _ => return None,
3531 };
3532 Some(pair)
3533}
3534
3535#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
3536fn extract_numeric_literal(e: &Expr) -> Option<f32> {
3541 match e {
3542 Expr::Literal(Literal::Integer(n)) => Some(*n as f32),
3543 Expr::Literal(Literal::Float(x)) => Some(*x as f32),
3544 Expr::Unary {
3545 op: UnOp::Neg,
3546 expr,
3547 } => extract_numeric_literal(expr).map(|x| -x),
3548 _ => None,
3549 }
3550}
3551
3552pub fn parse_interval_text(s: &str) -> Option<(i32, i64)> {
3560 let parts: Vec<&str> = s.split_whitespace().collect();
3561 if parts.is_empty() || !parts.len().is_multiple_of(2) {
3562 return None;
3563 }
3564 let mut months: i32 = 0;
3565 let mut micros: i64 = 0;
3566 let mut i = 0;
3567 while i < parts.len() {
3568 let n: i64 = parts[i].parse().ok()?;
3569 let unit = parts[i + 1].to_ascii_lowercase();
3570 let unit_stripped = unit.strip_suffix('s').unwrap_or(&unit);
3571 match unit_stripped {
3572 "microsecond" => micros = micros.checked_add(n)?,
3573 "millisecond" => micros = micros.checked_add(n.checked_mul(1_000)?)?,
3574 "second" => micros = micros.checked_add(n.checked_mul(1_000_000)?)?,
3575 "minute" => micros = micros.checked_add(n.checked_mul(60_000_000)?)?,
3576 "hour" => micros = micros.checked_add(n.checked_mul(3_600_000_000)?)?,
3577 "day" => micros = micros.checked_add(n.checked_mul(86_400_000_000)?)?,
3578 "week" => micros = micros.checked_add(n.checked_mul(604_800_000_000)?)?,
3579 "month" => {
3580 let n32 = i32::try_from(n).ok()?;
3581 months = months.checked_add(n32)?;
3582 }
3583 "year" => {
3584 let n32 = i32::try_from(n).ok()?;
3585 months = months.checked_add(n32.checked_mul(12)?)?;
3586 }
3587 _ => return None,
3588 }
3589 i += 2;
3590 }
3591 Some((months, micros))
3592}
3593
3594#[cfg(test)]
3595mod tests {
3596 use super::*;
3597 use alloc::string::ToString;
3598
3599 fn parse(s: &str) -> Statement {
3600 parse_statement(s).expect("parse ok")
3601 }
3602
3603 fn lit_int(n: i64) -> Expr {
3604 Expr::Literal(Literal::Integer(n))
3605 }
3606
3607 fn col(name: &str) -> Expr {
3608 Expr::Column(ColumnName {
3609 qualifier: None,
3610 name: name.into(),
3611 })
3612 }
3613
3614 #[test]
3615 fn select_single_integer() {
3616 let s = parse("SELECT 1");
3617 let Statement::Select(s) = s else {
3618 panic!("expected SELECT")
3619 };
3620 assert_eq!(s.items.len(), 1);
3621 assert!(s.from.is_none());
3622 assert!(s.where_.is_none());
3623 }
3624
3625 #[test]
3626 fn select_multiple_literal_kinds() {
3627 let s = parse("SELECT 1, 'hi', NULL, TRUE, 1.5");
3628 let Statement::Select(s) = s else {
3629 panic!("expected SELECT")
3630 };
3631 assert_eq!(s.items.len(), 5);
3632 }
3633
3634 #[test]
3635 fn select_wildcard_from_table() {
3636 let s = parse("SELECT * FROM users");
3637 let Statement::Select(s) = s else {
3638 panic!("expected SELECT")
3639 };
3640 assert!(matches!(s.items[..], [SelectItem::Wildcard]));
3641 assert_eq!(s.from.as_ref().unwrap().primary.name, "users");
3642 }
3643
3644 #[test]
3645 fn select_with_table_alias() {
3646 let s = parse("SELECT * FROM users AS u");
3647 let Statement::Select(s) = s else {
3648 panic!("expected SELECT")
3649 };
3650 let t = &s.from.as_ref().unwrap().primary;
3651 assert_eq!(t.name, "users");
3652 assert_eq!(t.alias.as_deref(), Some("u"));
3653 }
3654
3655 #[test]
3656 fn select_with_where_eq() {
3657 let s = parse("SELECT a FROM t WHERE a = 1");
3658 let Statement::Select(s) = s else {
3659 panic!("expected SELECT")
3660 };
3661 let w = s.where_.unwrap();
3662 assert_eq!(
3663 w,
3664 Expr::Binary {
3665 lhs: Box::new(col("a")),
3666 op: BinOp::Eq,
3667 rhs: Box::new(lit_int(1)),
3668 }
3669 );
3670 }
3671
3672 #[test]
3673 fn arithmetic_precedence() {
3674 let s = parse("SELECT 1 + 2 * 3");
3675 let Statement::Select(s) = s else {
3676 panic!("expected SELECT")
3677 };
3678 let SelectItem::Expr { expr, .. } = &s.items[0] else {
3679 panic!("wildcard?")
3680 };
3681 assert_eq!(
3682 expr,
3683 &Expr::Binary {
3684 lhs: Box::new(lit_int(1)),
3685 op: BinOp::Add,
3686 rhs: Box::new(Expr::Binary {
3687 lhs: Box::new(lit_int(2)),
3688 op: BinOp::Mul,
3689 rhs: Box::new(lit_int(3)),
3690 }),
3691 }
3692 );
3693 }
3694
3695 #[test]
3696 fn parentheses_override_precedence() {
3697 let s = parse("SELECT (1 + 2) * 3");
3698 let Statement::Select(s) = s else {
3699 panic!("expected SELECT")
3700 };
3701 let SelectItem::Expr { expr, .. } = &s.items[0] else {
3702 panic!()
3703 };
3704 assert_eq!(
3705 expr,
3706 &Expr::Binary {
3707 lhs: Box::new(Expr::Binary {
3708 lhs: Box::new(lit_int(1)),
3709 op: BinOp::Add,
3710 rhs: Box::new(lit_int(2)),
3711 }),
3712 op: BinOp::Mul,
3713 rhs: Box::new(lit_int(3)),
3714 }
3715 );
3716 }
3717
3718 #[test]
3719 fn not_binds_below_comparison() {
3720 let s = parse("SELECT NOT a = 1 FROM t");
3722 let Statement::Select(s) = s else {
3723 panic!("expected SELECT")
3724 };
3725 let SelectItem::Expr { expr, .. } = &s.items[0] else {
3726 panic!()
3727 };
3728 assert_eq!(
3729 expr,
3730 &Expr::Unary {
3731 op: UnOp::Not,
3732 expr: Box::new(Expr::Binary {
3733 lhs: Box::new(col("a")),
3734 op: BinOp::Eq,
3735 rhs: Box::new(lit_int(1)),
3736 }),
3737 }
3738 );
3739 }
3740
3741 #[test]
3742 fn unary_minus_binds_above_multiplication() {
3743 let s = parse("SELECT -a * 2 FROM t");
3745 let Statement::Select(s) = s else {
3746 panic!("expected SELECT")
3747 };
3748 let SelectItem::Expr { expr, .. } = &s.items[0] else {
3749 panic!()
3750 };
3751 assert_eq!(
3752 expr,
3753 &Expr::Binary {
3754 lhs: Box::new(Expr::Unary {
3755 op: UnOp::Neg,
3756 expr: Box::new(col("a")),
3757 }),
3758 op: BinOp::Mul,
3759 rhs: Box::new(lit_int(2)),
3760 }
3761 );
3762 }
3763
3764 #[test]
3765 fn qualified_column() {
3766 let s = parse("SELECT t.col FROM t");
3767 let Statement::Select(s) = s else {
3768 panic!("expected SELECT")
3769 };
3770 let SelectItem::Expr { expr, .. } = &s.items[0] else {
3771 panic!()
3772 };
3773 assert_eq!(
3774 expr,
3775 &Expr::Column(ColumnName {
3776 qualifier: Some("t".into()),
3777 name: "col".into()
3778 })
3779 );
3780 }
3781
3782 #[test]
3783 fn select_item_alias_with_as() {
3784 let s = parse("SELECT a AS y FROM t");
3785 let Statement::Select(s) = s else {
3786 panic!("expected SELECT")
3787 };
3788 let SelectItem::Expr { alias, .. } = &s.items[0] else {
3789 panic!()
3790 };
3791 assert_eq!(alias.as_deref(), Some("y"));
3792 }
3793
3794 #[test]
3795 fn trailing_semicolon_accepted() {
3796 let s = parse("SELECT 1;");
3797 let Statement::Select(s) = s else {
3798 panic!("expected SELECT")
3799 };
3800 assert_eq!(s.items.len(), 1);
3801 }
3802
3803 #[test]
3804 fn boolean_chain_with_and_or_not() {
3805 let s = parse("SELECT NOT a OR b AND NOT c FROM t");
3807 let Statement::Select(s) = s else {
3808 panic!("expected SELECT")
3809 };
3810 let SelectItem::Expr { expr, .. } = &s.items[0] else {
3811 panic!()
3812 };
3813 let expected = Expr::Binary {
3814 lhs: Box::new(Expr::Unary {
3815 op: UnOp::Not,
3816 expr: Box::new(col("a")),
3817 }),
3818 op: BinOp::Or,
3819 rhs: Box::new(Expr::Binary {
3820 lhs: Box::new(col("b")),
3821 op: BinOp::And,
3822 rhs: Box::new(Expr::Unary {
3823 op: UnOp::Not,
3824 expr: Box::new(col("c")),
3825 }),
3826 }),
3827 };
3828 assert_eq!(expr, &expected);
3829 }
3830
3831 #[test]
3832 fn empty_input_errors() {
3833 let err = parse_statement("").unwrap_err();
3834 assert!(err.message.contains("SELECT"));
3835 }
3836
3837 #[test]
3838 fn unmatched_paren_errors() {
3839 assert!(parse_statement("SELECT (1 + 2").is_err());
3840 }
3841
3842 #[test]
3843 fn display_round_trip_simple_select() {
3844 let original = parse("SELECT a + 1 FROM t WHERE a > 0");
3845 let text = original.to_string();
3846 let again = parse_statement(&text).expect("re-parse");
3847 assert_eq!(original, again);
3848 }
3849
3850 #[test]
3853 fn create_table_single_column() {
3854 let s = parse("CREATE TABLE foo (a INT)");
3855 let Statement::CreateTable(c) = s else {
3856 panic!("expected CreateTable")
3857 };
3858 assert_eq!(c.name, "foo");
3859 assert_eq!(c.columns.len(), 1);
3860 assert_eq!(c.columns[0].name, "a");
3861 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
3862 assert!(c.columns[0].nullable);
3863 }
3864
3865 #[test]
3866 fn create_table_multi_column_with_not_null_mix() {
3867 let s = parse("CREATE TABLE u (id INT NOT NULL, name TEXT, score FLOAT NOT NULL, ok BOOL)");
3868 let Statement::CreateTable(c) = s else {
3869 panic!()
3870 };
3871 assert_eq!(c.columns.len(), 4);
3872 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
3873 assert!(!c.columns[0].nullable);
3874 assert_eq!(c.columns[1].ty, ColumnTypeName::Text);
3875 assert!(c.columns[1].nullable);
3876 assert_eq!(c.columns[2].ty, ColumnTypeName::Float);
3877 assert!(!c.columns[2].nullable);
3878 assert_eq!(c.columns[3].ty, ColumnTypeName::Bool);
3879 }
3880
3881 #[test]
3882 fn create_table_bigint_supported() {
3883 let s = parse("CREATE TABLE accounts (id BIGINT NOT NULL)");
3884 let Statement::CreateTable(c) = s else {
3885 panic!()
3886 };
3887 assert_eq!(c.columns[0].ty, ColumnTypeName::BigInt);
3888 }
3889
3890 #[test]
3891 fn create_table_vector_default_is_f32() {
3892 let s = parse("CREATE TABLE t (v VECTOR(128))");
3893 let Statement::CreateTable(c) = s else {
3894 panic!()
3895 };
3896 assert_eq!(
3897 c.columns[0].ty,
3898 ColumnTypeName::Vector {
3899 dim: 128,
3900 encoding: VecEncoding::F32,
3901 },
3902 );
3903 }
3904
3905 #[test]
3906 fn create_table_vector_using_sq8() {
3907 for sql in [
3910 "CREATE TABLE t (v VECTOR(128) USING SQ8)",
3911 "CREATE TABLE t (v VECTOR(128) using sq8)",
3912 ] {
3913 let s = parse(sql);
3914 let Statement::CreateTable(c) = s else {
3915 panic!()
3916 };
3917 assert_eq!(
3918 c.columns[0].ty,
3919 ColumnTypeName::Vector {
3920 dim: 128,
3921 encoding: VecEncoding::Sq8,
3922 },
3923 "{sql}",
3924 );
3925 }
3926 }
3927
3928 #[test]
3929 fn create_table_vector_using_unknown_errors() {
3930 let err = parse_statement("CREATE TABLE t (v VECTOR(8) USING PQ8)").unwrap_err();
3931 assert!(
3932 err.message.contains("unknown vector encoding"),
3933 "got: {}",
3934 err.message
3935 );
3936 }
3937
3938 #[test]
3939 fn vector_using_sq8_display_roundtrips() {
3940 let s = parse("CREATE TABLE t (v VECTOR(64) USING SQ8)");
3943 let Statement::CreateTable(c) = s else {
3944 panic!()
3945 };
3946 assert_eq!(c.columns[0].ty.to_string(), "VECTOR(64) USING SQ8");
3947 }
3948
3949 #[test]
3950 fn parser_recognises_placeholders() {
3951 use crate::ast::{Expr, SelectItem, Statement};
3952 let s = parse("SELECT $1, $2 + 1 FROM t WHERE x = $3");
3954 let Statement::Select(sel) = s else { panic!() };
3955 assert!(matches!(
3956 sel.items[0],
3957 SelectItem::Expr {
3958 expr: Expr::Placeholder(1),
3959 alias: None
3960 }
3961 ));
3962 let SelectItem::Expr {
3964 expr: Expr::Binary { lhs, rhs, .. },
3965 ..
3966 } = &sel.items[1]
3967 else {
3968 panic!()
3969 };
3970 assert!(matches!(**lhs, Expr::Placeholder(2)));
3971 assert!(matches!(**rhs, Expr::Literal(Literal::Integer(1))));
3972 let Some(Expr::Binary { rhs, .. }) = sel.where_.as_ref() else {
3974 panic!()
3975 };
3976 assert!(matches!(**rhs, Expr::Placeholder(3)));
3977 }
3978
3979 #[test]
3980 fn parser_rejects_dollar_zero() {
3981 assert!(parse_statement("SELECT $0").is_err());
3983 }
3984
3985 #[test]
3986 fn placeholder_display_roundtrips() {
3987 let s = parse("SELECT $42 FROM t");
3990 let printed = s.to_string();
3991 assert!(printed.contains("$42"));
3992 let again = parse(&printed);
3993 assert_eq!(s, again);
3994 }
3995
3996 #[test]
3997 fn alter_index_rebuild_bare() {
3998 use crate::ast::{AlterIndexTarget, Statement};
3999 let s = parse("ALTER INDEX my_idx REBUILD");
4000 let Statement::AlterIndex(a) = s else {
4001 panic!("expected AlterIndex, got {s:?}")
4002 };
4003 assert_eq!(a.name, "my_idx");
4004 assert_eq!(a.target, AlterIndexTarget::Rebuild { encoding: None });
4005 }
4006
4007 #[test]
4008 fn alter_index_rebuild_with_encoding() {
4009 use crate::ast::{AlterIndexTarget, Statement};
4010 for (sql, want) in [
4011 (
4012 "ALTER INDEX my_idx REBUILD WITH (encoding = F32)",
4013 VecEncoding::F32,
4014 ),
4015 (
4016 "ALTER INDEX my_idx REBUILD WITH (encoding = sq8)",
4017 VecEncoding::Sq8,
4018 ),
4019 (
4020 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
4021 VecEncoding::F16,
4022 ),
4023 ] {
4024 let s = parse(sql);
4025 let Statement::AlterIndex(a) = s else {
4026 panic!("{sql}: expected AlterIndex")
4027 };
4028 assert_eq!(a.name, "my_idx");
4029 assert_eq!(
4030 a.target,
4031 AlterIndexTarget::Rebuild {
4032 encoding: Some(want)
4033 },
4034 "{sql}"
4035 );
4036 }
4037 }
4038
4039 #[test]
4040 fn alter_index_rebuild_unknown_encoding_errors() {
4041 let err = parse_statement("ALTER INDEX my_idx REBUILD WITH (encoding = PQ8)").unwrap_err();
4042 assert!(
4043 err.message.contains("unknown vector encoding"),
4044 "got: {}",
4045 err.message
4046 );
4047 }
4048
4049 #[test]
4050 fn alter_index_rebuild_display_roundtrips() {
4051 for (input, want) in [
4052 ("ALTER INDEX my_idx REBUILD", "ALTER INDEX my_idx REBUILD"),
4053 (
4054 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
4055 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
4056 ),
4057 (
4058 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
4059 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
4060 ),
4061 ] {
4062 let s = parse(input);
4063 assert_eq!(s.to_string(), want);
4064 }
4065 }
4066
4067 #[test]
4068 fn create_table_unknown_type_errors() {
4069 let err = parse_statement("CREATE TABLE x (a xml)").unwrap_err();
4072 assert!(err.message.contains("unsupported column type"));
4073 }
4074
4075 #[test]
4076 fn create_table_missing_table_keyword_errors() {
4077 assert!(parse_statement("CREATE x (a INT)").is_err());
4078 }
4079
4080 #[test]
4081 fn insert_single_value() {
4082 let s = parse("INSERT INTO foo VALUES (42)");
4083 let Statement::Insert(i) = s else {
4084 panic!("expected Insert")
4085 };
4086 assert_eq!(i.table, "foo");
4087 assert_eq!(i.rows.len(), 1);
4088 assert_eq!(i.rows[0].len(), 1);
4089 assert!(matches!(i.rows[0][0], Expr::Literal(Literal::Integer(42))));
4090 }
4091
4092 #[test]
4093 fn insert_multi_value_with_mixed_literals() {
4094 let s = parse("INSERT INTO foo VALUES (1, 'hi', 3.14, TRUE, NULL)");
4095 let Statement::Insert(i) = s else { panic!() };
4096 assert_eq!(i.rows.len(), 1);
4097 assert_eq!(i.rows[0].len(), 5);
4098 }
4099
4100 #[test]
4101 fn insert_missing_into_errors() {
4102 assert!(parse_statement("INSERT foo VALUES (1)").is_err());
4103 }
4104
4105 #[test]
4106 fn create_table_round_trip() {
4107 let original =
4108 parse("CREATE TABLE foo (id BIGINT NOT NULL, label TEXT, score FLOAT NOT NULL)");
4109 let text = original.to_string();
4110 let again = parse_statement(&text).expect("re-parse");
4111 assert_eq!(original, again);
4112 }
4113
4114 #[test]
4115 fn insert_round_trip_with_negation_and_string() {
4116 let original = parse("INSERT INTO t VALUES (-1, 'it''s', NULL)");
4117 let text = original.to_string();
4118 let again = parse_statement(&text).expect("re-parse");
4119 assert_eq!(original, again);
4120 }
4121
4122 #[test]
4123 fn unknown_keyword_at_statement_start_errors() {
4124 let err = parse_statement("FROBNICATE foo SET x = 1").unwrap_err();
4127 assert!(err.message.contains("expected SELECT"));
4128 }
4129
4130 #[test]
4133 fn create_index_basic() {
4134 let s = parse("CREATE INDEX idx_id ON users (id)");
4135 let Statement::CreateIndex(c) = s else {
4136 panic!("expected CreateIndex")
4137 };
4138 assert_eq!(c.name, "idx_id");
4139 assert_eq!(c.table, "users");
4140 assert_eq!(c.column, "id");
4141 }
4142
4143 #[test]
4144 fn create_index_missing_on_errors() {
4145 assert!(parse_statement("CREATE INDEX foo users (id)").is_err());
4146 }
4147
4148 #[test]
4149 fn create_index_missing_paren_errors() {
4150 assert!(parse_statement("CREATE INDEX foo ON users id").is_err());
4151 }
4152
4153 #[test]
4154 fn create_index_round_trip() {
4155 let original = parse("CREATE INDEX by_name ON users (name)");
4156 let again = parse_statement(&original.to_string()).unwrap();
4157 assert_eq!(original, again);
4158 }
4159
4160 #[test]
4163 fn create_unique_index_basic() {
4164 let s = parse("CREATE UNIQUE INDEX uq_x ON t (a)");
4165 let Statement::CreateIndex(c) = s else {
4166 panic!("expected CreateIndex");
4167 };
4168 assert!(c.is_unique);
4169 assert_eq!(c.column, "a");
4170 assert!(c.partial_predicate.is_none());
4171 }
4172
4173 #[test]
4174 fn create_unique_index_partial() {
4175 let s = parse(
4177 "CREATE UNIQUE INDEX idx_email_templates_user_default \
4178 ON email_templates (user_address) WHERE is_default = true",
4179 );
4180 let Statement::CreateIndex(c) = s else {
4181 panic!("expected CreateIndex");
4182 };
4183 assert!(c.is_unique);
4184 assert_eq!(c.table, "email_templates");
4185 assert_eq!(c.column, "user_address");
4186 assert!(c.partial_predicate.is_some());
4187 }
4188
4189 #[test]
4190 fn create_unique_index_composite_with_predicate() {
4191 let s = parse(
4193 "CREATE UNIQUE INDEX uq_calendar_events_instance \
4194 ON calendar_events (calendar_id, uid, recurrence_id) \
4195 WHERE recurrence_id IS NOT NULL",
4196 );
4197 let Statement::CreateIndex(c) = s else {
4198 panic!("expected CreateIndex");
4199 };
4200 assert!(c.is_unique);
4201 assert_eq!(c.column, "calendar_id");
4202 assert_eq!(c.extra_columns, vec!["uid".to_string(), "recurrence_id".to_string()]);
4203 assert!(c.partial_predicate.is_some());
4204 }
4205
4206 #[test]
4207 fn create_unique_index_using_btree_ok() {
4208 let s = parse("CREATE UNIQUE INDEX uq_x ON t USING btree (a)");
4209 assert!(matches!(s, Statement::CreateIndex(ref c) if c.is_unique));
4210 }
4211
4212 #[test]
4213 fn create_unique_index_using_hnsw_rejected() {
4214 let err = parse_statement(
4215 "CREATE UNIQUE INDEX uq_v ON t USING hnsw (embedding)",
4216 )
4217 .unwrap_err();
4218 assert!(err.message.contains("UNIQUE"), "{}", err.message);
4219 }
4220
4221 #[test]
4222 fn create_unique_index_round_trip() {
4223 let original = parse(
4224 "CREATE UNIQUE INDEX uq_calendar_events_master \
4225 ON calendar_events (calendar_id, uid) WHERE recurrence_id IS NULL",
4226 );
4227 let again = parse_statement(&original.to_string()).unwrap();
4228 assert_eq!(original, again);
4229 }
4230
4231 #[test]
4232 fn create_unique_without_index_errors() {
4233 let err = parse_statement("CREATE UNIQUE TABLE t (a INT)").unwrap_err();
4234 assert!(err.message.contains("INDEX"), "{}", err.message);
4235 }
4236
4237 #[test]
4240 fn create_table_bytea_column() {
4241 let s = parse("CREATE TABLE t (id INT NOT NULL, payload BYTEA NOT NULL)");
4242 let Statement::CreateTable(c) = s else {
4243 panic!("expected CreateTable");
4244 };
4245 assert_eq!(c.columns.len(), 2);
4246 assert_eq!(c.columns[1].ty, ColumnTypeName::Bytes);
4247 assert!(!c.columns[1].nullable);
4248 }
4249
4250 #[test]
4251 fn create_table_bytes_alias_column() {
4252 let s = parse("CREATE TABLE t (blob BYTES)");
4253 let Statement::CreateTable(c) = s else {
4254 panic!("expected CreateTable");
4255 };
4256 assert_eq!(c.columns[0].ty, ColumnTypeName::Bytes);
4257 }
4258
4259 #[test]
4260 fn bytea_round_trip_display() {
4261 let original = parse("CREATE TABLE t (a BYTEA NOT NULL)");
4262 let again = parse_statement(&original.to_string()).unwrap();
4263 assert_eq!(original, again);
4264 }
4265
4266 #[test]
4269 fn begin_commit_rollback_parse_as_unit_variants() {
4270 assert_eq!(parse("BEGIN"), Statement::Begin);
4271 assert_eq!(parse("COMMIT"), Statement::Commit);
4272 assert_eq!(parse("ROLLBACK"), Statement::Rollback);
4273 assert_eq!(parse("BEGIN;"), Statement::Begin);
4275 }
4276
4277 #[test]
4280 fn inner_product_binop_parses() {
4281 let s = parse("SELECT v <#> [1.0, 2.0] FROM t");
4282 let Statement::Select(s) = s else { panic!() };
4283 let SelectItem::Expr { expr, .. } = &s.items[0] else {
4284 panic!()
4285 };
4286 assert!(matches!(
4287 expr,
4288 Expr::Binary {
4289 op: BinOp::InnerProduct,
4290 ..
4291 }
4292 ));
4293 }
4294
4295 #[test]
4296 fn cosine_distance_binop_parses() {
4297 let s = parse("SELECT v <=> [1.0, 2.0] FROM t");
4298 let Statement::Select(s) = s else { panic!() };
4299 let SelectItem::Expr { expr, .. } = &s.items[0] else {
4300 panic!()
4301 };
4302 assert!(matches!(
4303 expr,
4304 Expr::Binary {
4305 op: BinOp::CosineDistance,
4306 ..
4307 }
4308 ));
4309 }
4310
4311 #[test]
4312 fn vector_cast_postfix_wraps_string_literal() {
4313 let s = parse("SELECT '[1,2,3]'::vector FROM t");
4314 let Statement::Select(s) = s else { panic!() };
4315 let SelectItem::Expr { expr, .. } = &s.items[0] else {
4316 panic!()
4317 };
4318 assert!(matches!(
4319 expr,
4320 Expr::Cast {
4321 target: CastTarget::Vector,
4322 ..
4323 }
4324 ));
4325 }
4326
4327 #[test]
4328 fn unsupported_cast_target_errors() {
4329 let err = parse_statement("SELECT 1::numeric FROM t").unwrap_err();
4331 assert!(err.message.contains("unsupported cast target"));
4332 }
4333
4334 #[test]
4335 fn tx_statements_round_trip() {
4336 for q in ["BEGIN", "COMMIT", "ROLLBACK"] {
4337 let original = parse(q);
4338 let again = parse_statement(&original.to_string()).unwrap();
4339 assert_eq!(original, again);
4340 }
4341 }
4342
4343 #[test]
4344 fn interval_text_parsing_units() {
4345 assert_eq!(parse_interval_text("1 day"), Some((0, 86_400_000_000)));
4347 assert_eq!(parse_interval_text("1 second"), Some((0, 1_000_000)));
4348 assert_eq!(parse_interval_text("1 month"), Some((1, 0)));
4349 assert_eq!(parse_interval_text("2 years"), Some((24, 0)));
4350 assert_eq!(parse_interval_text("1 year 6 months"), Some((18, 0)));
4352 assert_eq!(
4353 parse_interval_text("1 day 2 hours"),
4354 Some((0, 86_400_000_000 + 7_200_000_000))
4355 );
4356 assert_eq!(parse_interval_text("-1 day"), Some((0, -86_400_000_000)));
4358 assert_eq!(parse_interval_text(""), None);
4360 assert_eq!(parse_interval_text("garbage"), None);
4361 assert_eq!(parse_interval_text("1 fortnight"), None);
4362 assert_eq!(parse_interval_text("1"), None);
4363 }
4364
4365 #[test]
4366 fn interval_literal_roundtrips_via_display() {
4367 let parsed = parse("SELECT INTERVAL '1 day 2 hours'");
4368 let s = parsed.to_string();
4369 assert!(s.contains("INTERVAL '1 day 2 hours'"), "got: {s}");
4371 let again = parse_statement(&s).unwrap();
4373 assert_eq!(parsed, again);
4374 }
4375
4376 #[test]
4379 fn parser_recognises_create_publication_bare() {
4380 let s = parse("CREATE PUBLICATION pub_a");
4381 let Statement::CreatePublication(p) = s else {
4382 panic!("expected CreatePublication, got {s:?}")
4383 };
4384 assert_eq!(p.name, "pub_a");
4385 assert_eq!(p.scope, PublicationScope::AllTables);
4386 }
4387
4388 #[test]
4389 fn parser_recognises_create_publication_for_all_tables() {
4390 let s = parse("CREATE PUBLICATION pub_a FOR ALL TABLES");
4391 let Statement::CreatePublication(p) = s else {
4392 panic!("expected CreatePublication, got {s:?}")
4393 };
4394 assert_eq!(p.name, "pub_a");
4395 assert_eq!(p.scope, PublicationScope::AllTables);
4396 }
4397
4398 #[test]
4399 fn parser_recognises_drop_publication() {
4400 let s = parse("DROP PUBLICATION pub_a");
4401 let Statement::DropPublication(name) = s else {
4402 panic!("expected DropPublication, got {s:?}")
4403 };
4404 assert_eq!(name, "pub_a");
4405 }
4406
4407 #[test]
4408 fn parser_recognises_for_table_list() {
4409 let s = parse("CREATE PUBLICATION pub_a FOR TABLE t1, t2, t3");
4410 let Statement::CreatePublication(p) = s else {
4411 panic!("expected CreatePublication, got {s:?}")
4412 };
4413 assert_eq!(p.name, "pub_a");
4414 let PublicationScope::ForTables(ts) = p.scope else {
4415 panic!("expected ForTables scope")
4416 };
4417 assert_eq!(ts, alloc::vec!["t1", "t2", "t3"]);
4418 }
4419
4420 #[test]
4421 fn parser_recognises_for_tables_plural() {
4422 let s = parse("CREATE PUBLICATION pub_a FOR TABLES t1, t2");
4424 let Statement::CreatePublication(p) = s else {
4425 panic!("expected CreatePublication, got {s:?}")
4426 };
4427 let PublicationScope::ForTables(ts) = p.scope else {
4428 panic!("expected ForTables")
4429 };
4430 assert_eq!(ts, alloc::vec!["t1", "t2"]);
4431 }
4432
4433 #[test]
4434 fn parser_recognises_for_all_tables_except_list() {
4435 let s = parse("CREATE PUBLICATION p FOR ALL TABLES EXCEPT t1, t2");
4436 let Statement::CreatePublication(p) = s else {
4437 panic!()
4438 };
4439 let PublicationScope::AllTablesExcept(ts) = p.scope else {
4440 panic!("expected AllTablesExcept")
4441 };
4442 assert_eq!(ts, alloc::vec!["t1", "t2"]);
4443 }
4444
4445 #[test]
4446 fn parser_rejects_for_table_with_empty_list() {
4447 let err = parse_statement("CREATE PUBLICATION p FOR TABLE")
4449 .expect_err("must error on empty list");
4450 assert!(!err.message.is_empty());
4453 }
4454
4455 #[test]
4456 fn parser_recognises_show_publications() {
4457 let s = parse("SHOW PUBLICATIONS");
4460 assert!(matches!(s, Statement::ShowPublications));
4461 }
4462
4463 #[test]
4466 fn parser_recognises_create_subscription_single_publication() {
4467 let s = parse("CREATE SUBSCRIPTION sub_a CONNECTION 'host=127.0.0.1 port=20002' PUBLICATION pub_a");
4468 let Statement::CreateSubscription(c) = s else {
4469 panic!("expected CreateSubscription, got {s:?}")
4470 };
4471 assert_eq!(c.name, "sub_a");
4472 assert_eq!(c.conn_str, "host=127.0.0.1 port=20002");
4473 assert_eq!(c.publications, alloc::vec!["pub_a"]);
4474 }
4475
4476 #[test]
4477 fn parser_recognises_create_subscription_multi_publication() {
4478 let s = parse(
4479 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=h' PUBLICATION p1, p2, p3",
4480 );
4481 let Statement::CreateSubscription(c) = s else {
4482 panic!()
4483 };
4484 assert_eq!(c.publications, alloc::vec!["p1", "p2", "p3"]);
4485 }
4486
4487 #[test]
4488 fn parser_rejects_create_subscription_missing_connection() {
4489 let err = parse_statement("CREATE SUBSCRIPTION s PUBLICATION p")
4490 .expect_err("must error on missing CONNECTION");
4491 assert!(err.message.contains("CONNECTION"), "got: {}", err.message);
4492 }
4493
4494 #[test]
4495 fn parser_rejects_create_subscription_missing_publication() {
4496 let err = parse_statement("CREATE SUBSCRIPTION s CONNECTION 'host=x'")
4497 .expect_err("must error on missing PUBLICATION");
4498 assert!(err.message.contains("PUBLICATION"), "got: {}", err.message);
4499 }
4500
4501 #[test]
4502 fn parser_recognises_drop_subscription() {
4503 let s = parse("DROP SUBSCRIPTION sub_a");
4504 let Statement::DropSubscription(name) = s else {
4505 panic!("expected DropSubscription, got {s:?}")
4506 };
4507 assert_eq!(name, "sub_a");
4508 }
4509
4510 #[test]
4511 fn parser_recognises_show_subscriptions() {
4512 let s = parse("SHOW SUBSCRIPTIONS");
4513 assert!(matches!(s, Statement::ShowSubscriptions));
4514 }
4515
4516 #[test]
4517 fn parser_recognises_wait_for_wal_position_no_timeout() {
4518 let s = parse("WAIT FOR WAL POSITION 12345");
4519 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
4520 panic!("expected WaitForWalPosition, got {s:?}")
4521 };
4522 assert_eq!(pos, 12345);
4523 assert!(timeout_ms.is_none());
4524 }
4525
4526 #[test]
4527 fn parser_recognises_wait_for_wal_position_with_timeout() {
4528 let s = parse("WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000");
4529 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
4530 panic!()
4531 };
4532 assert_eq!(pos, 67890);
4533 assert_eq!(timeout_ms, Some(5000));
4534 }
4535
4536 #[test]
4537 fn parser_rejects_wait_with_negative_position() {
4538 let err = parse_statement("WAIT FOR WAL POSITION -1").unwrap_err();
4544 assert!(!err.message.is_empty());
4545 }
4546
4547 #[test]
4548 fn parser_recognises_bare_analyze() {
4549 let s = parse("ANALYZE");
4550 assert!(matches!(s, Statement::Analyze(None)));
4551 }
4552
4553 #[test]
4554 fn parser_recognises_analyze_with_table() {
4555 let s = parse("ANALYZE users");
4556 let Statement::Analyze(Some(name)) = s else {
4557 panic!("expected Analyze, got {s:?}")
4558 };
4559 assert_eq!(name, "users");
4560 }
4561
4562 #[test]
4563 fn parser_recognises_analyze_with_quoted_table() {
4564 let s = parse("ANALYZE \"Mixed Case\"");
4565 let Statement::Analyze(Some(name)) = s else {
4566 panic!()
4567 };
4568 assert_eq!(name, "Mixed Case");
4569 }
4570
4571 #[test]
4572 fn parser_rejects_analyze_with_garbage_token() {
4573 let err = parse_statement("ANALYZE 42").expect_err("must error");
4574 assert!(!err.message.is_empty());
4575 }
4576
4577 #[test]
4578 fn analyze_display_roundtrips() {
4579 for sql in ["ANALYZE", "ANALYZE users"] {
4580 let s = parse(sql);
4581 let printed = s.to_string();
4582 let again = parse_statement(&printed)
4583 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
4584 assert_eq!(s, again);
4585 }
4586 }
4587
4588 #[test]
4589 fn wait_for_display_roundtrips() {
4590 for sql in [
4591 "WAIT FOR WAL POSITION 12345",
4592 "WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000",
4593 ] {
4594 let s = parse(sql);
4595 let printed = s.to_string();
4596 let again = parse_statement(&printed)
4597 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
4598 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
4599 }
4600 }
4601
4602 #[test]
4603 fn subscription_ddl_display_roundtrips() {
4604 for sql in [
4605 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=h port=20002' PUBLICATION pub_a",
4606 "CREATE SUBSCRIPTION sub_b CONNECTION 'host=h' PUBLICATION p1, p2",
4607 "DROP SUBSCRIPTION sub_a",
4608 "SHOW SUBSCRIPTIONS",
4609 ] {
4610 let s = parse(sql);
4611 let printed = s.to_string();
4612 let again = parse_statement(&printed)
4613 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
4614 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
4615 }
4616 }
4617
4618 #[test]
4619 fn parser_drop_dispatches_user_vs_publication() {
4620 let s = parse("DROP USER 'alice'");
4623 let Statement::DropUser(name) = s else {
4624 panic!("expected DropUser, got {s:?}")
4625 };
4626 assert_eq!(name, "alice");
4627 let s = parse("DROP PUBLICATION p1");
4629 assert!(matches!(s, Statement::DropPublication(_)));
4630 }
4631
4632 #[test]
4633 fn publication_ddl_display_roundtrips() {
4634 for sql in [
4637 "CREATE PUBLICATION pub_a",
4638 "CREATE PUBLICATION pub_a FOR ALL TABLES",
4639 "CREATE PUBLICATION pub_a FOR TABLE t1, t2",
4640 "CREATE PUBLICATION pub_a FOR ALL TABLES EXCEPT t1",
4641 "DROP PUBLICATION pub_a",
4642 "SHOW PUBLICATIONS",
4643 ] {
4644 let s = parse(sql);
4645 let printed = s.to_string();
4646 let again = parse_statement(&printed)
4647 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
4648 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
4649 }
4650 }
4651}