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(CreateSubscriptionStatement {
617 name,
618 conn_str,
619 publications,
620 }))
621 }
622
623 fn parse_wait_after_keyword(&mut self) -> Result<Statement, ParseError> {
628 if !matches!(self.peek(), Token::For) {
632 return Err(self.err(format!("expected FOR after WAIT, got {:?}", self.peek())));
633 }
634 self.advance();
635 self.expect_keyword_ident("wal")?;
636 self.expect_keyword_ident("position")?;
637 let pos = self.expect_u64_literal()?;
638 let timeout_ms = if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with"))
639 {
640 self.advance();
641 self.expect_keyword_ident("timeout")?;
642 Some(self.expect_u64_literal()?)
643 } else {
644 None
645 };
646 Ok(Statement::WaitForWalPosition { pos, timeout_ms })
647 }
648
649 fn expect_u64_literal(&mut self) -> Result<u64, ParseError> {
653 match self.advance() {
654 Token::Integer(n) if n >= 0 => Ok(n as u64),
655 Token::Integer(n) => Err(ParseError {
656 message: format!("expected non-negative integer, got {n}"),
657 token_pos: self.pos.saturating_sub(1),
658 }),
659 other => Err(ParseError {
660 message: format!("expected integer literal, got {other:?}"),
661 token_pos: self.pos.saturating_sub(1),
662 }),
663 }
664 }
665
666 fn parse_create_user_after_keyword(&mut self) -> Result<Statement, ParseError> {
670 let name = self.expect_ident_or_string()?;
671 self.expect_keyword_ident("with")?;
672 self.expect_keyword_ident("password")?;
673 let password = self.expect_string_literal()?;
674 let role = if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
675 && s.eq_ignore_ascii_case("role")
676 {
677 self.advance();
678 self.expect_string_literal()?
679 } else {
680 "readonly".to_string()
681 };
682 Ok(Statement::CreateUser(crate::ast::CreateUserStatement {
683 name,
684 password,
685 role,
686 }))
687 }
688
689 fn parse_update_after_keyword(&mut self) -> Result<Statement, ParseError> {
692 let table = self.expect_ident_like()?;
693 self.expect_keyword_ident("set")?;
694 let mut assignments = Vec::new();
695 loop {
696 let col = self.expect_ident_like()?;
697 if !matches!(self.peek(), Token::Eq) {
698 return Err(self.err(format!(
699 "expected `=` after column name in UPDATE SET, got {:?}",
700 self.peek()
701 )));
702 }
703 self.advance();
704 let value = self.parse_expr(0)?;
705 assignments.push((col, value));
706 if matches!(self.peek(), Token::Comma) {
707 self.advance();
708 continue;
709 }
710 break;
711 }
712 let where_ = if matches!(self.peek(), Token::Where) {
713 self.advance();
714 Some(self.parse_expr(0)?)
715 } else {
716 None
717 };
718 let returning = self.parse_optional_returning()?;
719 Ok(Statement::Update(crate::ast::UpdateStatement {
720 table,
721 assignments,
722 where_,
723 returning,
724 }))
725 }
726
727 fn parse_delete_after_keyword(&mut self) -> Result<Statement, ParseError> {
730 if !matches!(self.peek(), Token::From) {
731 return Err(self.err(format!("expected FROM after DELETE, got {:?}", self.peek())));
732 }
733 self.advance();
734 let table = self.expect_ident_like()?;
735 let where_ = if matches!(self.peek(), Token::Where) {
736 self.advance();
737 Some(self.parse_expr(0)?)
738 } else {
739 None
740 };
741 let returning = self.parse_optional_returning()?;
742 Ok(Statement::Delete(crate::ast::DeleteStatement {
743 table,
744 where_,
745 returning,
746 }))
747 }
748
749 fn parse_optional_returning(
754 &mut self,
755 ) -> Result<Option<Vec<crate::ast::SelectItem>>, ParseError> {
756 let is_returning_kw = matches!(
757 self.peek(),
758 Token::Ident(s) if s.eq_ignore_ascii_case("returning")
759 );
760 if !is_returning_kw {
761 return Ok(None);
762 }
763 self.advance();
764 let mut items = Vec::new();
765 loop {
766 items.push(self.parse_select_item()?);
767 if matches!(self.peek(), Token::Comma) {
768 self.advance();
769 continue;
770 }
771 break;
772 }
773 Ok(Some(items))
774 }
775
776 fn parse_alter_after_keyword(&mut self) -> Result<Statement, ParseError> {
784 match self.advance() {
786 Token::Index => {}
787 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("index") => {}
788 Token::Table => return self.parse_alter_table_after_keyword(),
790 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("table") => {
791 return self.parse_alter_table_after_keyword();
792 }
793 other => {
794 return Err(self.err(format!(
795 "expected INDEX or TABLE after ALTER, got {other:?}"
796 )));
797 }
798 }
799 let name = self.expect_ident_like()?;
800 self.expect_keyword_ident("rebuild")?;
802 let encoding = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
804 self.advance();
805 if !matches!(self.peek(), Token::LParen) {
806 return Err(self.err(format!(
807 "expected '(' after WITH in ALTER INDEX REBUILD, got {:?}",
808 self.peek()
809 )));
810 }
811 self.advance();
812 self.expect_keyword_ident("encoding")?;
813 if !matches!(self.peek(), Token::Eq) {
814 return Err(self.err(format!(
815 "expected '=' after encoding in ALTER INDEX REBUILD, got {:?}",
816 self.peek()
817 )));
818 }
819 self.advance();
820 let enc_ident = match self.advance() {
821 Token::Ident(s) | Token::QuotedIdent(s) => s,
822 other => {
823 return Err(self.err(format!("expected encoding name after =, got {other:?}")));
824 }
825 };
826 let enc = match enc_ident.to_ascii_lowercase().as_str() {
827 "f32" => VecEncoding::F32,
828 "sq8" => VecEncoding::Sq8,
829 "half" => VecEncoding::F16,
830 other => {
831 return Err(self.err(format!(
832 "unknown vector encoding {other:?} in ALTER INDEX REBUILD; supported: F32, SQ8, HALF"
833 )));
834 }
835 };
836 if !matches!(self.peek(), Token::RParen) {
837 return Err(self.err(format!(
838 "expected ')' after encoding value, got {:?}",
839 self.peek()
840 )));
841 }
842 self.advance();
843 Some(enc)
844 } else {
845 None
846 };
847 Ok(Statement::AlterIndex(crate::ast::AlterIndexStatement {
848 name,
849 target: crate::ast::AlterIndexTarget::Rebuild { encoding },
850 }))
851 }
852
853 fn parse_alter_table_after_keyword(&mut self) -> Result<Statement, ParseError> {
857 let table_name = self.expect_ident_like()?;
858 match self.peek() {
862 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
863 self.advance();
864 let setting = self.expect_ident_like()?;
865 if !setting.eq_ignore_ascii_case("hot_tier_bytes") {
866 return Err(self.err(alloc::format!(
867 "ALTER TABLE SET: unknown setting {setting:?}; supported: hot_tier_bytes"
868 )));
869 }
870 if !matches!(self.peek(), Token::Eq) {
871 return Err(self.err(alloc::format!(
872 "expected '=' after hot_tier_bytes, got {:?}",
873 self.peek()
874 )));
875 }
876 self.advance();
877 let n = self.expect_u64_literal()?;
878 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
879 name: table_name,
880 target: crate::ast::AlterTableTarget::SetHotTierBytes(n),
881 }))
882 }
883 Token::Ident(s) if s.eq_ignore_ascii_case("add") => {
884 self.advance();
885 let fk = self.parse_table_level_fk()?;
888 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
889 name: table_name,
890 target: crate::ast::AlterTableTarget::AddForeignKey(fk),
891 }))
892 }
893 Token::Drop => {
894 self.advance();
895 match self.advance() {
896 Token::Ident(s) if s.eq_ignore_ascii_case("constraint") => {}
897 other => {
898 return Err(self.err(alloc::format!(
899 "expected CONSTRAINT after DROP in ALTER TABLE, got {other:?}"
900 )));
901 }
902 }
903 let cname = self.expect_ident_like()?;
904 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
905 name: table_name,
906 target: crate::ast::AlterTableTarget::DropForeignKey(cname),
907 }))
908 }
909 other => Err(self.err(alloc::format!(
910 "expected SET / ADD / DROP in ALTER TABLE, got {other:?}"
911 ))),
912 }
913 }
914
915 fn expect_keyword_ident(&mut self, kw: &str) -> Result<(), ParseError> {
917 match self.advance() {
918 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case(kw) => Ok(()),
919 other => Err(ParseError {
920 message: format!("expected {kw:?}, got {other:?}"),
921 token_pos: self.pos.saturating_sub(1),
922 }),
923 }
924 }
925
926 fn expect_ident_or_string(&mut self) -> Result<String, ParseError> {
930 match self.advance() {
931 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => Ok(s),
932 other => Err(ParseError {
933 message: format!("expected identifier or string, got {other:?}"),
934 token_pos: self.pos.saturating_sub(1),
935 }),
936 }
937 }
938
939 fn expect_string_literal(&mut self) -> Result<String, ParseError> {
940 match self.advance() {
941 Token::String(s) => Ok(s),
942 other => Err(ParseError {
943 message: format!("expected quoted string, got {other:?}"),
944 token_pos: self.pos.saturating_sub(1),
945 }),
946 }
947 }
948
949 fn parse_select_stmt(&mut self) -> Result<Statement, ParseError> {
950 let mut head = self.parse_bare_select()?;
955 while matches!(self.peek(), Token::Union) {
956 self.advance();
957 let kind = if matches!(self.peek(), Token::All) {
958 self.advance();
959 UnionKind::All
960 } else {
961 UnionKind::Distinct
962 };
963 let peer = self.parse_bare_select()?;
964 head.unions.push((kind, peer));
965 }
966 head.order_by = if matches!(self.peek(), Token::Order) {
967 self.advance();
968 if !matches!(self.peek(), Token::By) {
969 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
970 }
971 self.advance();
972 let mut keys = Vec::new();
975 loop {
976 let expr = self.parse_expr(0)?;
977 let desc = if matches!(self.peek(), Token::Desc) {
978 self.advance();
979 true
980 } else if matches!(self.peek(), Token::Asc) {
981 self.advance();
982 false
983 } else {
984 false
985 };
986 keys.push(OrderBy { expr, desc });
987 if matches!(self.peek(), Token::Comma) {
988 self.advance();
989 } else {
990 break;
991 }
992 }
993 keys
994 } else {
995 Vec::new()
996 };
997 head.limit = if matches!(self.peek(), Token::Limit) {
998 self.advance();
999 Some(self.parse_limit_expr("LIMIT")?)
1000 } else {
1001 None
1002 };
1003 head.offset = if matches!(self.peek(), Token::Offset) {
1004 self.advance();
1005 Some(self.parse_limit_expr("OFFSET")?)
1006 } else {
1007 None
1008 };
1009 Ok(Statement::Select(head))
1010 }
1011
1012 fn parse_limit_expr(&mut self, label: &str) -> Result<crate::ast::LimitExpr, ParseError> {
1017 match self.advance() {
1018 Token::Integer(n) if n >= 0 => u32::try_from(n)
1019 .map(crate::ast::LimitExpr::Literal)
1020 .map_err(|_| ParseError {
1021 message: alloc::format!("{label} value too large: {n}"),
1022 token_pos: self.pos.saturating_sub(1),
1023 }),
1024 Token::Placeholder(n) => Ok(crate::ast::LimitExpr::Placeholder(n)),
1025 other => Err(ParseError {
1026 message: alloc::format!(
1027 "expected non-negative integer or $N placeholder after {label}, got {other:?}"
1028 ),
1029 token_pos: self.pos.saturating_sub(1),
1030 }),
1031 }
1032 }
1033
1034 fn parse_bare_select(&mut self) -> Result<SelectStatement, ParseError> {
1039 if !matches!(self.peek(), Token::Select) {
1040 return Err(self.err(format!(
1041 "expected SELECT to start a query block, got {:?}",
1042 self.peek()
1043 )));
1044 }
1045 self.advance();
1046 let distinct = if matches!(self.peek(), Token::Distinct) {
1047 self.advance();
1048 true
1049 } else {
1050 false
1051 };
1052 let items = self.parse_select_list()?;
1053 let from = if matches!(self.peek(), Token::From) {
1054 self.advance();
1055 Some(self.parse_from_clause()?)
1056 } else {
1057 None
1058 };
1059 let where_ = if matches!(self.peek(), Token::Where) {
1060 self.advance();
1061 Some(self.parse_expr(0)?)
1062 } else {
1063 None
1064 };
1065 let mut group_by_all = false;
1066 let group_by = if matches!(self.peek(), Token::Group) {
1067 self.advance();
1068 if !matches!(self.peek(), Token::By) {
1069 return Err(self.err(format!("expected BY after GROUP, got {:?}", self.peek())));
1070 }
1071 self.advance();
1072 if matches!(self.peek(), Token::All) {
1075 self.advance();
1076 group_by_all = true;
1077 None
1078 } else {
1079 let mut groups = Vec::new();
1080 loop {
1081 groups.push(self.parse_expr(0)?);
1082 if matches!(self.peek(), Token::Comma) {
1083 self.advance();
1084 } else {
1085 break;
1086 }
1087 }
1088 Some(groups)
1089 }
1090 } else {
1091 None
1092 };
1093 let having = if matches!(self.peek(), Token::Having) {
1094 self.advance();
1095 Some(self.parse_expr(0)?)
1096 } else {
1097 None
1098 };
1099 Ok(SelectStatement {
1100 ctes: Vec::new(),
1101 distinct,
1102 items,
1103 from,
1104 where_,
1105 group_by,
1106 group_by_all,
1107 having,
1108 unions: Vec::new(),
1109 order_by: Vec::new(),
1110 limit: None,
1111 offset: None,
1112 })
1113 }
1114
1115 fn parse_create_table_stmt_after_create(&mut self) -> Result<Statement, ParseError> {
1116 debug_assert!(matches!(self.peek(), Token::Table));
1118 self.advance();
1119 let if_not_exists = self.consume_if_not_exists();
1120 let name = self.expect_ident_like()?;
1121 if !matches!(self.peek(), Token::LParen) {
1122 return Err(self.err(format!(
1123 "expected '(' after table name, got {:?}",
1124 self.peek()
1125 )));
1126 }
1127 self.advance();
1128 let mut columns = Vec::new();
1129 let mut foreign_keys: Vec<ForeignKeyConstraint> = Vec::new();
1130 let mut table_constraints: Vec<crate::ast::TableConstraint> = Vec::new();
1131 loop {
1132 if self.peek_table_level_pk_start() {
1138 table_constraints.push(self.parse_table_level_primary_key()?);
1139 } else if self.peek_table_level_unique_start() {
1140 table_constraints.push(self.parse_table_level_unique()?);
1141 } else if self.peek_constraint_or_fk_start() {
1142 foreign_keys.push(self.parse_table_level_fk()?);
1143 } else {
1144 let (col, col_level_fk) = self.parse_column_def_with_fk()?;
1145 columns.push(col);
1146 if let Some(fk) = col_level_fk {
1147 foreign_keys.push(fk);
1148 }
1149 }
1150 match self.peek() {
1151 Token::Comma => {
1152 self.advance();
1153 }
1154 Token::RParen => {
1155 self.advance();
1156 break;
1157 }
1158 other => {
1159 return Err(
1160 self.err(format!("expected ',' or ')' in column list, got {other:?}"))
1161 );
1162 }
1163 }
1164 }
1165 if columns.is_empty() {
1166 return Err(self.err("CREATE TABLE requires at least one column".into()));
1167 }
1168 Ok(Statement::CreateTable(CreateTableStatement {
1169 name,
1170 columns,
1171 if_not_exists,
1172 foreign_keys,
1173 table_constraints,
1174 }))
1175 }
1176
1177 fn peek_table_level_pk_start(&self) -> bool {
1182 let cur = self.peek();
1183 let nxt = self.tokens.get(self.pos + 1);
1184 let nxt2 = self.tokens.get(self.pos + 2);
1185 let is_primary = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("primary"));
1186 let is_key = matches!(nxt, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("key"));
1187 let is_lparen = matches!(nxt2, Some(Token::LParen));
1188 is_primary && is_key && is_lparen
1189 }
1190
1191 fn peek_table_level_unique_start(&self) -> bool {
1193 let cur = self.peek();
1194 let nxt = self.tokens.get(self.pos + 1);
1195 let is_unique = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("unique"));
1196 let is_lparen = matches!(nxt, Some(Token::LParen));
1197 is_unique && is_lparen
1198 }
1199
1200 fn parse_table_level_primary_key(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
1201 self.advance(); self.advance(); let columns = self.parse_paren_ident_list("PRIMARY KEY")?;
1204 Ok(crate::ast::TableConstraint::PrimaryKey {
1205 name: None,
1206 columns,
1207 })
1208 }
1209
1210 fn parse_table_level_unique(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
1211 self.advance(); let columns = self.parse_paren_ident_list("UNIQUE")?;
1213 Ok(crate::ast::TableConstraint::Unique {
1214 name: None,
1215 columns,
1216 })
1217 }
1218
1219 fn parse_paren_ident_list(&mut self, ctx: &str) -> Result<Vec<String>, ParseError> {
1220 if !matches!(self.peek(), Token::LParen) {
1221 return Err(self.err(alloc::format!(
1222 "expected '(' after {ctx}, got {:?}",
1223 self.peek()
1224 )));
1225 }
1226 self.advance();
1227 let mut out = Vec::new();
1228 loop {
1229 out.push(self.expect_ident_like()?);
1230 match self.peek() {
1231 Token::Comma => {
1232 self.advance();
1233 }
1234 Token::RParen => {
1235 self.advance();
1236 break;
1237 }
1238 other => {
1239 return Err(self.err(alloc::format!(
1240 "expected ',' or ')' in {ctx} list, got {other:?}"
1241 )));
1242 }
1243 }
1244 }
1245 if out.is_empty() {
1246 return Err(self.err(alloc::format!("{ctx} requires at least one column")));
1247 }
1248 Ok(out)
1249 }
1250
1251 fn peek_constraint_or_fk_start(&self) -> bool {
1256 let is_constraint_kw = matches!(
1257 self.peek(),
1258 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
1259 );
1260 let is_foreign_kw = matches!(
1261 self.peek(),
1262 Token::Ident(s) if s.eq_ignore_ascii_case("foreign")
1263 );
1264 is_constraint_kw || is_foreign_kw
1265 }
1266
1267 fn parse_table_level_fk(&mut self) -> Result<ForeignKeyConstraint, ParseError> {
1271 let mut name: Option<String> = None;
1272 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
1273 self.advance();
1274 name = Some(self.expect_ident_like()?);
1275 }
1276 match self.advance() {
1278 Token::Ident(s) if s.eq_ignore_ascii_case("foreign") => {}
1279 other => return Err(self.err(format!("expected FOREIGN, got {other:?}"))),
1280 }
1281 match self.advance() {
1283 Token::Ident(s) if s.eq_ignore_ascii_case("key") => {}
1284 other => return Err(self.err(format!("expected KEY after FOREIGN, got {other:?}"))),
1285 }
1286 if !matches!(self.peek(), Token::LParen) {
1288 return Err(self.err(format!(
1289 "expected '(' after FOREIGN KEY, got {:?}",
1290 self.peek()
1291 )));
1292 }
1293 self.advance();
1294 let mut columns = Vec::new();
1295 loop {
1296 columns.push(self.expect_ident_like()?);
1297 match self.peek() {
1298 Token::Comma => {
1299 self.advance();
1300 }
1301 Token::RParen => {
1302 self.advance();
1303 break;
1304 }
1305 other => {
1306 return Err(self.err(format!(
1307 "expected ',' or ')' in FK column list, got {other:?}"
1308 )));
1309 }
1310 }
1311 }
1312 if columns.is_empty() {
1313 return Err(self.err("FOREIGN KEY requires at least one column".into()));
1314 }
1315 let (parent_table, parent_columns, on_delete, on_update) =
1316 self.parse_references_tail(columns.len())?;
1317 Ok(ForeignKeyConstraint {
1318 name,
1319 columns,
1320 parent_table,
1321 parent_columns,
1322 on_delete,
1323 on_update,
1324 })
1325 }
1326
1327 fn parse_references_tail(
1332 &mut self,
1333 expected_arity: usize,
1334 ) -> Result<(String, Vec<String>, FkAction, FkAction), ParseError> {
1335 match self.advance() {
1336 Token::Ident(s) if s.eq_ignore_ascii_case("references") => {}
1337 other => return Err(self.err(format!("expected REFERENCES, got {other:?}"))),
1338 }
1339 let parent_table = self.expect_ident_like()?;
1340 let mut parent_columns: Vec<String> = Vec::new();
1341 if matches!(self.peek(), Token::LParen) {
1342 self.advance();
1343 loop {
1344 parent_columns.push(self.expect_ident_like()?);
1345 match self.peek() {
1346 Token::Comma => {
1347 self.advance();
1348 }
1349 Token::RParen => {
1350 self.advance();
1351 break;
1352 }
1353 other => {
1354 return Err(self.err(format!(
1355 "expected ',' or ')' in REFERENCES column list, got {other:?}"
1356 )));
1357 }
1358 }
1359 }
1360 }
1361 if !parent_columns.is_empty() && parent_columns.len() != expected_arity {
1362 return Err(self.err(format!(
1363 "FK arity mismatch: {} local column(s) vs {} parent column(s)",
1364 expected_arity,
1365 parent_columns.len()
1366 )));
1367 }
1368 loop {
1374 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("deferrable")) {
1375 return Err(self.err(
1376 "DEFERRABLE constraints are not supported (SPG is single-writer; \
1377 constraints are always evaluated immediately at commit)"
1378 .into(),
1379 ));
1380 }
1381 if matches!(self.peek(), Token::Not) {
1382 let look = self.tokens.get(self.pos + 1);
1383 if matches!(look, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("deferrable")) {
1384 self.advance();
1387 self.advance();
1388 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("initially"))
1390 {
1391 self.advance();
1392 match self.advance() {
1393 Token::Ident(s) if s.eq_ignore_ascii_case("immediate") => {}
1394 other => {
1395 return Err(self.err(format!(
1396 "expected IMMEDIATE after INITIALLY for NOT DEFERRABLE, \
1397 got {other:?}"
1398 )));
1399 }
1400 }
1401 }
1402 continue;
1403 }
1404 break;
1405 }
1406 break;
1407 }
1408 let mut on_delete = FkAction::Restrict;
1411 let mut on_update = FkAction::Restrict;
1412 let mut seen_on_delete = false;
1413 let mut seen_on_update = false;
1414 loop {
1415 if !matches!(self.peek(), Token::On) {
1416 break;
1417 }
1418 self.advance();
1419 let which = self.advance();
1420 let action = self.parse_fk_action()?;
1421 match which {
1422 Token::Ident(ref s) if s.eq_ignore_ascii_case("delete") => {
1423 if seen_on_delete {
1424 return Err(self.err("ON DELETE specified twice".into()));
1425 }
1426 seen_on_delete = true;
1427 on_delete = action;
1428 }
1429 Token::Ident(ref s) if s.eq_ignore_ascii_case("update") => {
1430 if seen_on_update {
1431 return Err(self.err("ON UPDATE specified twice".into()));
1432 }
1433 seen_on_update = true;
1434 on_update = action;
1435 }
1436 other => {
1437 return Err(
1438 self.err(format!("expected DELETE or UPDATE after ON, got {other:?}"))
1439 );
1440 }
1441 }
1442 }
1443 Ok((parent_table, parent_columns, on_delete, on_update))
1444 }
1445
1446 fn parse_fk_action(&mut self) -> Result<FkAction, ParseError> {
1449 match self.advance() {
1450 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => Ok(FkAction::Cascade),
1451 Token::Ident(s) if s.eq_ignore_ascii_case("restrict") => Ok(FkAction::Restrict),
1452 Token::Ident(s) if s.eq_ignore_ascii_case("set") => match self.advance() {
1453 Token::Null => Ok(FkAction::SetNull),
1454 Token::Default => Ok(FkAction::SetDefault),
1455 other => Err(self.err(format!(
1456 "expected NULL or DEFAULT after SET in FK action, got {other:?}"
1457 ))),
1458 },
1459 Token::Ident(s) if s.eq_ignore_ascii_case("no") => match self.advance() {
1460 Token::Ident(s) if s.eq_ignore_ascii_case("action") => Ok(FkAction::NoAction),
1461 other => Err(self.err(format!(
1462 "expected ACTION after NO in FK action, got {other:?}"
1463 ))),
1464 },
1465 other => Err(self.err(format!(
1466 "expected CASCADE | RESTRICT | SET NULL | SET DEFAULT | NO ACTION, got {other:?}"
1467 ))),
1468 }
1469 }
1470
1471 fn consume_if_not_exists(&mut self) -> bool {
1474 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
1478 if !looks_like_if {
1479 return false;
1480 }
1481 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Not)) {
1484 return false;
1485 }
1486 if !matches!(
1487 self.tokens.get(self.pos + 2),
1488 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
1489 ) {
1490 return false;
1491 }
1492 self.advance(); self.advance(); self.advance(); true
1496 }
1497
1498 fn consume_optional_index_column_qualifiers(&mut self) {
1504 loop {
1505 match self.peek() {
1506 Token::Asc | Token::Desc => {
1507 self.advance();
1508 }
1509 Token::Ident(s) if s.eq_ignore_ascii_case("nulls") => {
1510 let look = self.tokens.get(self.pos + 1);
1511 if matches!(
1512 look,
1513 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("first")
1514 || k.eq_ignore_ascii_case("last")
1515 ) {
1516 self.advance();
1517 self.advance();
1518 } else {
1519 break;
1520 }
1521 }
1522 _ => break,
1523 }
1524 }
1525 }
1526
1527 fn parse_create_index_stmt_after_create(
1528 &mut self,
1529 is_unique: bool,
1530 ) -> Result<Statement, ParseError> {
1531 debug_assert!(matches!(self.peek(), Token::Index));
1533 self.advance();
1534 let if_not_exists = self.consume_if_not_exists();
1535 let name = self.expect_ident_like()?;
1536 if !matches!(self.peek(), Token::On) {
1537 return Err(self.err(format!(
1538 "expected ON after CREATE INDEX <name>, got {:?}",
1539 self.peek()
1540 )));
1541 }
1542 self.advance();
1543 let table = self.expect_ident_like()?;
1544 let method = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
1549 self.advance();
1550 let m = self.expect_ident_like()?;
1551 match m.to_ascii_lowercase().as_str() {
1552 "hnsw" => IndexMethod::Hnsw,
1553 "btree" => IndexMethod::BTree,
1554 "brin" => IndexMethod::Brin,
1555 "gin" | "gist" | "spgist" | "hash" => IndexMethod::BTree,
1564 other => {
1565 return Err(self.err(alloc::format!(
1566 "unknown index method {other:?}; supported: hnsw, btree, brin (gin/gist/spgist/hash accepted as BTree fallback)"
1567 )));
1568 }
1569 }
1570 } else {
1571 IndexMethod::BTree
1572 };
1573 if !matches!(self.peek(), Token::LParen) {
1574 return Err(self.err(format!(
1575 "expected '(' before indexed column, got {:?}",
1576 self.peek()
1577 )));
1578 }
1579 self.advance();
1580 let (column, expression): (String, Option<Expr>) = match self.peek().clone() {
1589 Token::Ident(s) | Token::QuotedIdent(s)
1596 if matches!(
1597 self.tokens.get(self.pos + 1),
1598 Some(Token::RParen | Token::Comma)
1599 ) =>
1600 {
1601 self.advance();
1602 (s, None)
1603 }
1604 Token::Ident(s) | Token::QuotedIdent(s)
1612 if matches!(
1613 self.tokens.get(self.pos + 1),
1614 Some(Token::Ident(op) | Token::QuotedIdent(op))
1615 if is_vector_opclass_name(op)
1616 ) =>
1617 {
1618 self.advance(); self.advance(); (s, None)
1621 }
1622 Token::Ident(_) | Token::QuotedIdent(_) => {
1623 let key_expr = self.parse_expr(0)?;
1624 let primary = extract_first_column(&key_expr).ok_or_else(|| {
1625 self.err("expression index key must reference at least one column".into())
1626 })?;
1627 (primary, Some(key_expr))
1628 }
1629 other => {
1630 return Err(self.err(format!(
1631 "expected column ident or expression, got {other:?}"
1632 )));
1633 }
1634 };
1635 let mut extra_columns: Vec<String> = Vec::new();
1644 self.consume_optional_index_column_qualifiers();
1646 while matches!(self.peek(), Token::Comma) {
1647 self.advance();
1648 let extra = self.expect_ident_like()?;
1649 self.consume_optional_index_column_qualifiers();
1650 extra_columns.push(extra);
1651 }
1652 if !matches!(self.peek(), Token::RParen) {
1653 return Err(self.err(format!(
1654 "expected ')' after indexed column / expression, got {:?}",
1655 self.peek()
1656 )));
1657 }
1658 self.advance();
1659 let included_columns = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("include"))
1663 {
1664 self.advance();
1665 if !matches!(self.peek(), Token::LParen) {
1666 return Err(self.err(format!("expected '(' after INCLUDE, got {:?}", self.peek())));
1667 }
1668 self.advance();
1669 let mut cols = Vec::new();
1670 loop {
1671 cols.push(self.expect_ident_like()?);
1672 match self.peek() {
1673 Token::Comma => {
1674 self.advance();
1675 }
1676 Token::RParen => {
1677 self.advance();
1678 break;
1679 }
1680 other => {
1681 return Err(self.err(format!(
1682 "expected ',' or ')' in INCLUDE list, got {other:?}"
1683 )));
1684 }
1685 }
1686 }
1687 cols
1688 } else {
1689 Vec::new()
1690 };
1691 let partial_predicate = if matches!(self.peek(), Token::Where) {
1693 self.advance();
1694 Some(self.parse_expr(0)?)
1695 } else {
1696 None
1697 };
1698 if is_unique && !matches!(method, IndexMethod::BTree) {
1703 return Err(self.err(alloc::format!(
1704 "UNIQUE is only supported on BTree indexes, got USING {:?}",
1705 method
1706 )));
1707 }
1708 Ok(Statement::CreateIndex(CreateIndexStatement {
1709 name,
1710 table,
1711 column,
1712 method,
1713 if_not_exists,
1714 included_columns,
1715 partial_predicate,
1716 extra_columns: extra_columns.clone(),
1717 expression,
1718 is_unique,
1719 }))
1720 }
1721
1722 fn parse_column_def_with_fk(
1727 &mut self,
1728 ) -> Result<(ColumnDef, Option<ForeignKeyConstraint>), ParseError> {
1729 let col = self.parse_column_def()?;
1730 let inline_references = matches!(
1732 self.peek(),
1733 Token::Ident(s) if s.eq_ignore_ascii_case("references")
1734 );
1735 if !inline_references {
1736 return Ok((col, None));
1737 }
1738 let (parent_table, parent_columns, on_delete, on_update) = self.parse_references_tail(1)?;
1739 let fk = ForeignKeyConstraint {
1740 name: None,
1741 columns: vec![col.name.clone()],
1742 parent_table,
1743 parent_columns,
1744 on_delete,
1745 on_update,
1746 };
1747 Ok((col, Some(fk)))
1748 }
1749
1750 fn parse_column_def(&mut self) -> Result<ColumnDef, ParseError> {
1751 let name = self.expect_ident_like()?;
1752 let ty_ident = match self.advance() {
1755 Token::Ident(s) => s,
1756 other => {
1757 return Err(ParseError {
1758 message: format!("expected column type, got {other:?}"),
1759 token_pos: self.pos.saturating_sub(1),
1760 });
1761 }
1762 };
1763 let mut implied_auto_increment = false;
1770 let mut implied_not_null = false;
1771 let mut ty = match ty_ident.as_str() {
1772 "smallserial" | "serial2" => {
1774 implied_auto_increment = true;
1775 implied_not_null = true;
1776 ColumnTypeName::SmallInt
1777 }
1778 "serial" | "serial4" => {
1779 implied_auto_increment = true;
1780 implied_not_null = true;
1781 ColumnTypeName::Int
1782 }
1783 "bigserial" | "serial8" => {
1784 implied_auto_increment = true;
1785 implied_not_null = true;
1786 ColumnTypeName::BigInt
1787 }
1788 "smallint" | "tinyint" => ColumnTypeName::SmallInt,
1794 "int" | "integer" | "mediumint" => ColumnTypeName::Int,
1796 "bigint" => ColumnTypeName::BigInt,
1797 "float" | "double" | "real" => ColumnTypeName::Float,
1799 "text" => ColumnTypeName::Text,
1800 "bool" | "boolean" => ColumnTypeName::Bool,
1801 "varchar" => ColumnTypeName::Varchar(self.parse_paren_size("VARCHAR")?),
1802 "char" => ColumnTypeName::Char(self.parse_paren_size("CHAR")?),
1803 "vector" => {
1804 let dim = self.parse_paren_size("VECTOR")?;
1805 let encoding = self.parse_optional_vector_encoding()?;
1806 ColumnTypeName::Vector { dim, encoding }
1807 }
1808 "numeric" => {
1809 let (precision, scale) = self.parse_optional_numeric_params()?;
1810 ColumnTypeName::Numeric(precision, scale)
1811 }
1812 "date" => ColumnTypeName::Date,
1813 "timestamp" | "datetime" => ColumnTypeName::Timestamp,
1816 "timestamptz" => ColumnTypeName::Timestamptz,
1820 "json" => ColumnTypeName::Json,
1825 "jsonb" => ColumnTypeName::Jsonb,
1826 "bytea" | "bytes" => ColumnTypeName::Bytes,
1832 other => {
1833 return Err(ParseError {
1834 message: format!("unsupported column type {other:?}"),
1835 token_pos: self.pos.saturating_sub(1),
1836 });
1837 }
1838 };
1839 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unsigned")) {
1844 self.advance();
1845 }
1846 if matches!(self.peek(), Token::LBracket) {
1851 self.advance();
1852 if !matches!(self.peek(), Token::RBracket) {
1853 return Err(self.err(alloc::format!(
1854 "TEXT[] takes no dimension; got {:?}",
1855 self.peek()
1856 )));
1857 }
1858 self.advance();
1859 ty = match ty {
1863 ColumnTypeName::Text => ColumnTypeName::TextArray,
1864 ColumnTypeName::Int => ColumnTypeName::IntArray,
1865 ColumnTypeName::BigInt => ColumnTypeName::BigIntArray,
1866 other => {
1867 return Err(self.err(alloc::format!(
1868 "v7.11 supports TEXT[] / INT[] / BIGINT[] only; got {other:?}[]"
1869 )));
1870 }
1871 };
1872 }
1873 let mut default: Option<Expr> = None;
1877 let mut nullable = !implied_not_null;
1878 let mut nullability_seen = implied_not_null;
1879 let mut auto_increment = implied_auto_increment;
1880 let mut is_primary_key = false;
1881 loop {
1882 if matches!(self.peek(), Token::Default) {
1883 if default.is_some() {
1884 return Err(self.err("DEFAULT specified twice".into()));
1885 }
1886 self.advance();
1887 default = Some(self.parse_expr(0)?);
1888 continue;
1889 }
1890 if matches!(self.peek(), Token::Not) {
1891 if nullability_seen {
1892 return Err(self.err("NOT NULL specified twice".into()));
1893 }
1894 self.advance();
1895 if !matches!(self.peek(), Token::Null) {
1896 return Err(self.err(format!(
1897 "expected NULL after NOT in column def, got {:?}",
1898 self.peek()
1899 )));
1900 }
1901 self.advance();
1902 nullable = false;
1903 nullability_seen = true;
1904 continue;
1905 }
1906 if let Token::Ident(s) = self.peek()
1909 && (s.eq_ignore_ascii_case("auto_increment")
1910 || s.eq_ignore_ascii_case("autoincrement"))
1911 {
1912 if auto_increment {
1913 return Err(self.err("AUTO_INCREMENT specified twice".into()));
1914 }
1915 self.advance();
1916 auto_increment = true;
1917 continue;
1918 }
1919 if let Token::Ident(s) = self.peek()
1924 && s.eq_ignore_ascii_case("primary")
1925 {
1926 if is_primary_key {
1927 return Err(self.err("PRIMARY KEY specified twice".into()));
1928 }
1929 let next = self.tokens.get(self.pos + 1);
1931 let next_is_key = matches!(
1932 next,
1933 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("key")
1934 );
1935 if !next_is_key {
1936 return Err(self.err(format!(
1937 "expected KEY after PRIMARY in column def, got {:?}",
1938 next
1939 )));
1940 }
1941 self.advance(); self.advance(); is_primary_key = true;
1944 if nullability_seen && nullable {
1945 return Err(self.err(
1946 "column declared NULL but inline PRIMARY KEY implies NOT NULL".into(),
1947 ));
1948 }
1949 nullable = false;
1950 nullability_seen = true;
1951 continue;
1952 }
1953 break;
1954 }
1955 Ok(ColumnDef {
1956 name,
1957 ty,
1958 nullable,
1959 default,
1960 auto_increment,
1961 is_primary_key,
1962 })
1963 }
1964
1965 fn parse_optional_numeric_params(&mut self) -> Result<(u8, u8), ParseError> {
1969 if !matches!(self.peek(), Token::LParen) {
1970 return Ok((0, 0));
1974 }
1975 self.advance();
1976 let precision = match self.advance() {
1977 Token::Integer(n) if (1..=38).contains(&n) => u8::try_from(n).expect("range-checked"),
1978 other => {
1979 return Err(ParseError {
1980 message: format!(
1981 "NUMERIC precision must be an integer in 1..=38, got {other:?}"
1982 ),
1983 token_pos: self.pos.saturating_sub(1),
1984 });
1985 }
1986 };
1987 let scale = if matches!(self.peek(), Token::Comma) {
1988 self.advance();
1989 match self.advance() {
1990 Token::Integer(n) if (0..=i64::from(precision)).contains(&n) => {
1991 u8::try_from(n).expect("range-checked")
1992 }
1993 other => {
1994 return Err(ParseError {
1995 message: format!(
1996 "NUMERIC scale must be a non-negative integer ≤ precision, got {other:?}"
1997 ),
1998 token_pos: self.pos.saturating_sub(1),
1999 });
2000 }
2001 }
2002 } else {
2003 0
2004 };
2005 if !matches!(self.peek(), Token::RParen) {
2006 return Err(self.err(format!(
2007 "expected ')' to close NUMERIC params, got {:?}",
2008 self.peek()
2009 )));
2010 }
2011 self.advance();
2012 Ok((precision, scale))
2013 }
2014
2015 fn parse_optional_vector_encoding(&mut self) -> Result<VecEncoding, ParseError> {
2023 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
2024 return Ok(VecEncoding::F32);
2025 }
2026 self.advance();
2027 let enc_ident = match self.advance() {
2028 Token::Ident(s) => s,
2029 other => {
2030 return Err(self.err(format!(
2031 "expected vector encoding after USING, got {other:?}"
2032 )));
2033 }
2034 };
2035 match enc_ident.to_ascii_lowercase().as_str() {
2036 "sq8" => Ok(VecEncoding::Sq8),
2037 "half" => Ok(VecEncoding::F16),
2040 other => Err(self.err(format!(
2041 "unknown vector encoding {other:?}; supported: SQ8, HALF"
2042 ))),
2043 }
2044 }
2045
2046 fn parse_paren_size(&mut self, label: &str) -> Result<u32, ParseError> {
2047 if !matches!(self.peek(), Token::LParen) {
2048 return Err(self.err(format!("{label} type requires (N), got {:?}", self.peek())));
2049 }
2050 self.advance();
2051 let n = match self.advance() {
2052 Token::Integer(n) if n > 0 => u32::try_from(n).map_err(|_| ParseError {
2053 message: format!("{label} size too large: {n}"),
2054 token_pos: self.pos.saturating_sub(1),
2055 })?,
2056 other => {
2057 return Err(ParseError {
2058 message: format!("expected positive integer {label} size, got {other:?}"),
2059 token_pos: self.pos.saturating_sub(1),
2060 });
2061 }
2062 };
2063 if !matches!(self.peek(), Token::RParen) {
2064 return Err(self.err(format!(
2065 "expected ')' after {label} size, got {:?}",
2066 self.peek()
2067 )));
2068 }
2069 self.advance();
2070 Ok(n)
2071 }
2072
2073 fn parse_insert_stmt(&mut self) -> Result<Statement, ParseError> {
2074 debug_assert!(matches!(self.peek(), Token::Insert));
2075 self.advance();
2076 if !matches!(self.peek(), Token::Into) {
2077 return Err(self.err(format!("expected INTO after INSERT, got {:?}", self.peek())));
2078 }
2079 self.advance();
2080 let table = self.expect_ident_like()?;
2081 let columns = if matches!(self.peek(), Token::LParen) {
2083 self.advance();
2084 let mut names = Vec::new();
2085 loop {
2086 names.push(self.expect_ident_like()?);
2087 match self.peek() {
2088 Token::Comma => {
2089 self.advance();
2090 }
2091 Token::RParen => {
2092 self.advance();
2093 break;
2094 }
2095 other => {
2096 return Err(self.err(format!(
2097 "expected ',' or ')' in INSERT column list, got {other:?}"
2098 )));
2099 }
2100 }
2101 }
2102 Some(names)
2103 } else {
2104 None
2105 };
2106 if !matches!(self.peek(), Token::Values) {
2107 return Err(self.err(format!(
2108 "expected VALUES after table name, got {:?}",
2109 self.peek()
2110 )));
2111 }
2112 self.advance();
2113 if !matches!(self.peek(), Token::LParen) {
2114 return Err(self.err(format!("expected '(' after VALUES, got {:?}", self.peek())));
2115 }
2116 let mut rows = Vec::new();
2117 loop {
2118 if !matches!(self.peek(), Token::LParen) {
2120 return Err(self.err(format!(
2121 "expected '(' for next VALUES tuple, got {:?}",
2122 self.peek()
2123 )));
2124 }
2125 self.advance();
2126 let mut tuple = Vec::new();
2127 loop {
2128 tuple.push(self.parse_expr(0)?);
2129 match self.peek() {
2130 Token::Comma => {
2131 self.advance();
2132 }
2133 Token::RParen => {
2134 self.advance();
2135 break;
2136 }
2137 other => {
2138 return Err(self.err(format!(
2139 "expected ',' or ')' in VALUES tuple, got {other:?}"
2140 )));
2141 }
2142 }
2143 }
2144 if tuple.is_empty() {
2145 return Err(self.err("INSERT VALUES tuple requires at least one value".into()));
2146 }
2147 rows.push(tuple);
2148 if matches!(self.peek(), Token::Comma) {
2150 self.advance();
2151 } else {
2152 break;
2153 }
2154 }
2155 let on_conflict = self.parse_optional_on_conflict()?;
2156 let returning = self.parse_optional_returning()?;
2157 Ok(Statement::Insert(InsertStatement {
2158 table,
2159 columns,
2160 rows,
2161 on_conflict,
2162 returning,
2163 }))
2164 }
2165
2166 fn parse_optional_on_conflict(
2171 &mut self,
2172 ) -> Result<Option<crate::ast::OnConflictClause>, ParseError> {
2173 if !matches!(self.peek(), Token::On) {
2174 return Ok(None);
2175 }
2176 let next_is_conflict = matches!(
2179 self.tokens.get(self.pos + 1),
2180 Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("conflict")
2181 );
2182 if !next_is_conflict {
2183 return Ok(None);
2184 }
2185 self.advance(); self.advance(); let mut target_columns: Vec<String> = Vec::new();
2189 if matches!(self.peek(), Token::LParen) {
2190 self.advance();
2191 loop {
2192 target_columns.push(self.expect_ident_like()?);
2193 match self.peek() {
2194 Token::Comma => {
2195 self.advance();
2196 }
2197 Token::RParen => {
2198 self.advance();
2199 break;
2200 }
2201 other => {
2202 return Err(self.err(alloc::format!(
2203 "expected ',' or ')' in ON CONFLICT target list, got {other:?}"
2204 )));
2205 }
2206 }
2207 }
2208 }
2209 match self.advance() {
2211 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {}
2212 other => {
2213 return Err(self.err(alloc::format!(
2214 "expected DO after ON CONFLICT [(…)], got {other:?}"
2215 )));
2216 }
2217 }
2218 let action = match self.advance() {
2220 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing") => {
2221 crate::ast::OnConflictAction::Nothing
2222 }
2223 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
2224 self.parse_on_conflict_update_action()?
2225 }
2226 other => {
2227 return Err(self.err(alloc::format!(
2228 "expected NOTHING or UPDATE after ON CONFLICT DO, got {other:?}"
2229 )));
2230 }
2231 };
2232 Ok(Some(crate::ast::OnConflictClause {
2233 target_columns,
2234 action,
2235 }))
2236 }
2237
2238 fn parse_on_conflict_update_action(
2242 &mut self,
2243 ) -> Result<crate::ast::OnConflictAction, ParseError> {
2244 match self.advance() {
2246 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {}
2247 other => {
2248 return Err(self.err(alloc::format!(
2249 "expected SET after ON CONFLICT DO UPDATE, got {other:?}"
2250 )));
2251 }
2252 }
2253 let mut assignments: Vec<(String, Expr)> = Vec::new();
2254 loop {
2255 let col = self.expect_ident_like()?;
2256 if !matches!(self.peek(), Token::Eq) {
2257 return Err(self.err(alloc::format!(
2258 "expected `=` after column in ON CONFLICT DO UPDATE SET, got {:?}",
2259 self.peek()
2260 )));
2261 }
2262 self.advance();
2263 let value = self.parse_expr(0)?;
2264 assignments.push((col, value));
2265 if matches!(self.peek(), Token::Comma) {
2266 self.advance();
2267 continue;
2268 }
2269 break;
2270 }
2271 let where_ = if matches!(self.peek(), Token::Where) {
2272 self.advance();
2273 Some(self.parse_expr(0)?)
2274 } else {
2275 None
2276 };
2277 Ok(crate::ast::OnConflictAction::Update {
2278 assignments,
2279 where_,
2280 })
2281 }
2282
2283 fn parse_select_list(&mut self) -> Result<Vec<SelectItem>, ParseError> {
2284 let mut items = Vec::new();
2285 loop {
2286 items.push(self.parse_select_item()?);
2287 if matches!(self.peek(), Token::Comma) {
2288 self.advance();
2289 } else {
2290 break;
2291 }
2292 }
2293 Ok(items)
2294 }
2295
2296 fn parse_select_item(&mut self) -> Result<SelectItem, ParseError> {
2297 if matches!(self.peek(), Token::Star) {
2298 self.advance();
2299 return Ok(SelectItem::Wildcard);
2300 }
2301 let expr = self.parse_expr(0)?;
2302 let alias = self.parse_optional_alias();
2303 Ok(SelectItem::Expr { expr, alias })
2304 }
2305
2306 fn parse_table_ref(&mut self) -> Result<TableRef, ParseError> {
2307 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unnest"))
2311 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
2312 {
2313 self.advance(); self.advance(); let expr = self.parse_expr(0)?;
2316 if !matches!(self.peek(), Token::RParen) {
2317 return Err(self.err(alloc::format!(
2318 "expected ')' after unnest() argument, got {:?}",
2319 self.peek()
2320 )));
2321 }
2322 self.advance();
2323 let alias_ident = self.parse_optional_alias();
2324 let name = alias_ident.clone().unwrap_or_else(|| "unnest".to_string());
2325 return Ok(TableRef {
2326 name,
2327 alias: alias_ident,
2328 as_of_segment: None,
2329 unnest_expr: Some(Box::new(expr)),
2330 });
2331 }
2332 let name = self.expect_ident_like()?;
2333 let as_of_segment = if matches!(self.peek(), Token::As)
2339 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("of"))
2340 {
2341 self.advance(); self.advance(); let kw = match self.peek().clone() {
2344 Token::Ident(s) | Token::QuotedIdent(s) => s,
2345 other => {
2346 return Err(self.err(format!("expected SEGMENT after AS OF, got {other:?}")));
2347 }
2348 };
2349 if !kw.eq_ignore_ascii_case("segment") {
2350 return Err(self.err(format!(
2351 "expected SEGMENT after AS OF, got {kw:?}; v6.10.2 supports SEGMENT only"
2352 )));
2353 }
2354 self.advance();
2355 let id = match self.advance() {
2358 Token::String(s) => s
2359 .parse::<u32>()
2360 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
2361 Token::Integer(n) => u32::try_from(n)
2362 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
2363 other => {
2364 return Err(self.err(format!(
2365 "expected segment id literal after AS OF SEGMENT, got {other:?}"
2366 )));
2367 }
2368 };
2369 Some(id)
2370 } else {
2371 None
2372 };
2373 let alias = self.parse_optional_alias();
2374 Ok(TableRef {
2375 name,
2376 alias,
2377 as_of_segment,
2378 unnest_expr: None,
2379 })
2380 }
2381
2382 fn parse_from_clause(&mut self) -> Result<FromClause, ParseError> {
2387 let primary = self.parse_table_ref()?;
2388 let mut joins = Vec::new();
2389 loop {
2390 if matches!(self.peek(), Token::Comma) {
2392 self.advance();
2393 let table = self.parse_table_ref()?;
2394 joins.push(FromJoin {
2395 kind: JoinKind::Cross,
2396 table,
2397 on: None,
2398 });
2399 continue;
2400 }
2401 let kind =
2404 match self.peek() {
2405 Token::Inner => {
2406 self.advance();
2407 if !matches!(self.peek(), Token::Join) {
2408 return Err(self
2409 .err(format!("expected JOIN after INNER, got {:?}", self.peek())));
2410 }
2411 self.advance();
2412 JoinKind::Inner
2413 }
2414 Token::Left => {
2415 self.advance();
2416 if matches!(self.peek(), Token::Outer) {
2417 self.advance();
2418 }
2419 if !matches!(self.peek(), Token::Join) {
2420 return Err(self.err(format!(
2421 "expected JOIN after LEFT [OUTER], got {:?}",
2422 self.peek()
2423 )));
2424 }
2425 self.advance();
2426 JoinKind::Left
2427 }
2428 Token::Cross => {
2429 self.advance();
2430 if !matches!(self.peek(), Token::Join) {
2431 return Err(self
2432 .err(format!("expected JOIN after CROSS, got {:?}", self.peek())));
2433 }
2434 self.advance();
2435 JoinKind::Cross
2436 }
2437 Token::Join => {
2438 self.advance();
2439 JoinKind::Inner
2440 }
2441 _ => break,
2442 };
2443 let table = self.parse_table_ref()?;
2444 let on = if matches!(self.peek(), Token::On) {
2445 self.advance();
2446 Some(self.parse_expr(0)?)
2447 } else if kind == JoinKind::Cross {
2448 None
2449 } else {
2450 return Err(self.err(format!(
2451 "expected ON after {:?} JOIN, got {:?}",
2452 kind,
2453 self.peek()
2454 )));
2455 };
2456 joins.push(FromJoin { kind, table, on });
2457 }
2458 Ok(FromClause { primary, joins })
2459 }
2460
2461 fn parse_optional_alias(&mut self) -> Option<String> {
2466 if matches!(self.peek(), Token::As) {
2467 self.advance();
2468 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
2473 return self.expect_ident_like().ok();
2474 }
2475 return None;
2476 }
2477 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
2478 return self.expect_ident_like().ok();
2479 }
2480 None
2481 }
2482
2483 fn parse_expr(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
2485 let mut lhs = self.parse_unary()?;
2486 while let Some((op, prec)) = binop_from(self.peek()) {
2487 if prec < min_prec {
2488 break;
2489 }
2490 self.advance();
2491 let any_kind = match self.peek() {
2496 Token::All if matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) => {
2497 Some(false)
2498 }
2499 Token::Ident(s) | Token::QuotedIdent(s)
2500 if (s.eq_ignore_ascii_case("any") || s.eq_ignore_ascii_case("all"))
2501 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) =>
2502 {
2503 Some(s.eq_ignore_ascii_case("any"))
2504 }
2505 _ => None,
2506 };
2507 if let Some(is_any) = any_kind {
2508 self.advance(); self.advance(); let arr = self.parse_expr(0)?;
2511 if !matches!(self.peek(), Token::RParen) {
2512 return Err(self.err(alloc::format!(
2513 "expected ')' after ANY/ALL argument, got {:?}",
2514 self.peek()
2515 )));
2516 }
2517 self.advance();
2518 lhs = Expr::AnyAll {
2519 expr: Box::new(lhs),
2520 op,
2521 array: Box::new(arr),
2522 is_any,
2523 };
2524 continue;
2525 }
2526 let rhs = self.parse_expr(prec + 1)?;
2527 lhs = Expr::Binary {
2528 lhs: Box::new(lhs),
2529 op,
2530 rhs: Box::new(rhs),
2531 };
2532 }
2533 Ok(lhs)
2534 }
2535
2536 fn parse_unary(&mut self) -> Result<Expr, ParseError> {
2537 match self.peek() {
2538 Token::Not => {
2539 self.advance();
2540 let e = self.parse_expr(3)?;
2543 Ok(Expr::Unary {
2544 op: UnOp::Not,
2545 expr: Box::new(e),
2546 })
2547 }
2548 Token::Minus => {
2549 self.advance();
2550 let e = self.parse_expr(8)?;
2553 Ok(Expr::Unary {
2554 op: UnOp::Neg,
2555 expr: Box::new(e),
2556 })
2557 }
2558 _ => self.parse_atom(),
2559 }
2560 }
2561
2562 fn parse_atom(&mut self) -> Result<Expr, ParseError> {
2563 let tok_pos = self.pos;
2564 match self.advance() {
2565 Token::Integer(n) => Ok(Expr::Literal(Literal::Integer(n))),
2566 Token::Float(x) => Ok(Expr::Literal(Literal::Float(x))),
2567 Token::String(s) => Ok(Expr::Literal(Literal::String(s))),
2568 Token::True => Ok(Expr::Literal(Literal::Bool(true))),
2569 Token::False => Ok(Expr::Literal(Literal::Bool(false))),
2570 Token::Null => Ok(Expr::Literal(Literal::Null)),
2571 Token::Placeholder(n) => Ok(Expr::Placeholder(n)),
2575 Token::LParen => {
2576 if matches!(self.peek(), Token::Select) {
2580 let inner = self.parse_select_stmt()?;
2581 match self.advance() {
2582 Token::RParen => {
2583 let Statement::Select(s) = inner else {
2584 unreachable!("parse_select_stmt returns Select")
2585 };
2586 Ok(Expr::ScalarSubquery(Box::new(s)))
2587 }
2588 other => Err(ParseError {
2589 message: format!("expected ')' after scalar subquery, got {other:?}"),
2590 token_pos: self.pos.saturating_sub(1),
2591 }),
2592 }
2593 } else {
2594 let e = self.parse_expr(0)?;
2595 match self.advance() {
2596 Token::RParen => Ok(e),
2597 other => Err(ParseError {
2598 message: format!("expected ')', got {other:?}"),
2599 token_pos: self.pos.saturating_sub(1),
2600 }),
2601 }
2602 }
2603 }
2604 Token::LBracket => self.parse_vector_literal_body(),
2605 Token::Extract => self.parse_extract_atom(),
2606 Token::Interval => self.parse_interval_atom(),
2607 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists") => {
2612 self.parse_exists_atom(false)
2613 }
2614 Token::Ident(s) | Token::QuotedIdent(s)
2618 if s.eq_ignore_ascii_case("array") && matches!(self.peek(), Token::LBracket) =>
2619 {
2620 self.advance(); let mut items: Vec<Expr> = Vec::new();
2622 if !matches!(self.peek(), Token::RBracket) {
2623 loop {
2624 items.push(self.parse_expr(0)?);
2625 match self.peek() {
2626 Token::Comma => {
2627 self.advance();
2628 }
2629 Token::RBracket => break,
2630 other => {
2631 return Err(self.err(alloc::format!(
2632 "expected ',' or ']' in ARRAY literal, got {other:?}"
2633 )));
2634 }
2635 }
2636 }
2637 }
2638 self.advance(); Ok(Expr::Array(items))
2640 }
2641 Token::Ident(s) | Token::QuotedIdent(s) => self.finish_ident_atom(s),
2642 other => Err(ParseError {
2643 message: format!("unexpected token {other:?} in expression"),
2644 token_pos: tok_pos,
2645 }),
2646 }
2647 .and_then(|atom| self.finish_postfix_casts(atom))
2649 }
2650
2651 fn finish_postfix_casts(&mut self, mut expr: Expr) -> Result<Expr, ParseError> {
2654 loop {
2655 if matches!(self.peek(), Token::DoubleColon) {
2656 self.advance();
2657 let target = match self.advance() {
2662 Token::Ident(s) => match s.to_ascii_lowercase().as_str() {
2663 "int" | "integer" | "int4" => {
2664 if matches!(self.peek(), Token::LBracket)
2665 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
2666 {
2667 self.advance();
2668 self.advance();
2669 CastTarget::IntArray
2670 } else {
2671 CastTarget::Int
2672 }
2673 }
2674 "bigint" | "int8" => {
2675 if matches!(self.peek(), Token::LBracket)
2676 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
2677 {
2678 self.advance();
2679 self.advance();
2680 CastTarget::BigIntArray
2681 } else {
2682 CastTarget::BigInt
2683 }
2684 }
2685 "float" | "double" | "real" => CastTarget::Float,
2686 "text" => {
2687 if matches!(self.peek(), Token::LBracket)
2689 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
2690 {
2691 self.advance();
2692 self.advance();
2693 CastTarget::TextArray
2694 } else {
2695 CastTarget::Text
2696 }
2697 }
2698 "bool" | "boolean" => CastTarget::Bool,
2699 "vector" => CastTarget::Vector,
2700 "date" => CastTarget::Date,
2701 "timestamp" | "datetime" => CastTarget::Timestamp,
2702 "timestamptz" => CastTarget::Timestamptz,
2703 "interval" => CastTarget::Interval,
2704 "json" => CastTarget::Json,
2705 "jsonb" => CastTarget::Jsonb,
2706 "regtype" => CastTarget::RegType,
2707 "regclass" => CastTarget::RegClass,
2708 other => {
2709 return Err(ParseError {
2710 message: format!("unsupported cast target `::{other}`"),
2711 token_pos: self.pos.saturating_sub(1),
2712 });
2713 }
2714 },
2715 Token::Interval => CastTarget::Interval,
2716 other => {
2717 return Err(ParseError {
2718 message: format!("expected type ident after `::`, got {other:?}"),
2719 token_pos: self.pos.saturating_sub(1),
2720 });
2721 }
2722 };
2723 expr = Expr::Cast {
2724 expr: Box::new(expr),
2725 target,
2726 };
2727 continue;
2728 }
2729 if matches!(self.peek(), Token::Is) {
2730 self.advance();
2731 let negated = if matches!(self.peek(), Token::Not) {
2732 self.advance();
2733 true
2734 } else {
2735 false
2736 };
2737 if matches!(self.peek(), Token::Distinct) {
2740 self.advance();
2741 if !matches!(self.peek(), Token::From) {
2742 return Err(self.err(format!(
2743 "expected FROM after IS{} DISTINCT, got {:?}",
2744 if negated { " NOT" } else { "" },
2745 self.peek()
2746 )));
2747 }
2748 self.advance();
2749 let rhs = self.parse_expr(20)?;
2753 let op = if negated {
2754 BinOp::IsNotDistinctFrom
2755 } else {
2756 BinOp::IsDistinctFrom
2757 };
2758 expr = Expr::Binary {
2759 op,
2760 lhs: Box::new(expr),
2761 rhs: Box::new(rhs),
2762 };
2763 continue;
2764 }
2765 if !matches!(self.peek(), Token::Null) {
2766 return Err(self.err(format!(
2767 "expected NULL or DISTINCT after IS{}, got {:?}",
2768 if negated { " NOT" } else { "" },
2769 self.peek()
2770 )));
2771 }
2772 self.advance();
2773 expr = Expr::IsNull {
2774 expr: Box::new(expr),
2775 negated,
2776 };
2777 continue;
2778 }
2779 let negated = if matches!(self.peek(), Token::Not) {
2783 let next = self.tokens.get(self.pos + 1);
2784 matches!(next, Some(Token::Between | Token::In | Token::Like))
2785 } else {
2786 false
2787 };
2788 if negated {
2789 self.advance();
2790 }
2791 if matches!(self.peek(), Token::Between) {
2792 expr = self.parse_between_tail(expr, negated)?;
2793 continue;
2794 }
2795 if matches!(self.peek(), Token::In) {
2796 expr = self.parse_in_tail(expr, negated)?;
2797 continue;
2798 }
2799 if matches!(self.peek(), Token::Like) {
2800 self.advance();
2801 let pattern = self.parse_expr(5)?;
2804 expr = Expr::Like {
2805 expr: Box::new(expr),
2806 pattern: Box::new(pattern),
2807 negated,
2808 };
2809 continue;
2810 }
2811 if matches!(self.peek(), Token::LBracket) {
2815 self.advance();
2816 let index = self.parse_expr(0)?;
2817 if !matches!(self.peek(), Token::RBracket) {
2818 return Err(self.err(alloc::format!(
2819 "expected ']' after array index, got {:?}",
2820 self.peek()
2821 )));
2822 }
2823 self.advance();
2824 expr = Expr::ArraySubscript {
2825 target: Box::new(expr),
2826 index: Box::new(index),
2827 };
2828 continue;
2829 }
2830 return Ok(expr);
2831 }
2832 }
2833
2834 fn parse_between_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
2838 self.advance(); let low = self.parse_expr(5)?;
2840 if !matches!(self.peek(), Token::And) {
2841 return Err(self.err(format!(
2842 "expected AND after BETWEEN low bound, got {:?}",
2843 self.peek()
2844 )));
2845 }
2846 self.advance();
2847 let high = self.parse_expr(5)?;
2848 let target = Box::new(expr);
2849 let combined = Expr::Binary {
2850 lhs: Box::new(Expr::Binary {
2851 lhs: target.clone(),
2852 op: BinOp::GtEq,
2853 rhs: Box::new(low),
2854 }),
2855 op: BinOp::And,
2856 rhs: Box::new(Expr::Binary {
2857 lhs: target,
2858 op: BinOp::LtEq,
2859 rhs: Box::new(high),
2860 }),
2861 };
2862 Ok(maybe_not(combined, negated))
2863 }
2864
2865 fn parse_with_cte_then_select(&mut self) -> Result<Statement, ParseError> {
2870 let mut recursive = false;
2875 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
2876 && s.eq_ignore_ascii_case("recursive")
2877 {
2878 self.advance();
2879 recursive = true;
2880 }
2881 let mut ctes = Vec::new();
2882 loop {
2883 let name = self.expect_ident_like()?;
2884 let column_overrides: Vec<String> = if matches!(self.peek(), Token::LParen) {
2888 self.advance();
2889 let mut names = Vec::new();
2890 loop {
2891 names.push(self.expect_ident_like()?);
2892 if matches!(self.peek(), Token::Comma) {
2893 self.advance();
2894 continue;
2895 }
2896 break;
2897 }
2898 if !matches!(self.peek(), Token::RParen) {
2899 return Err(self.err(format!(
2900 "expected ')' to close CTE column list, got {:?}",
2901 self.peek()
2902 )));
2903 }
2904 self.advance();
2905 names
2906 } else {
2907 Vec::new()
2908 };
2909 if !matches!(self.peek(), Token::As) {
2913 return Err(self.err(format!(
2914 "expected AS after CTE name {name:?}, got {:?}",
2915 self.peek()
2916 )));
2917 }
2918 self.advance();
2919 if !matches!(self.peek(), Token::LParen) {
2920 return Err(self.err(format!(
2921 "expected '(' after AS in WITH clause, got {:?}",
2922 self.peek()
2923 )));
2924 }
2925 self.advance();
2926 if !matches!(self.peek(), Token::Select) {
2927 return Err(self.err(format!("WITH body must be a SELECT, got {:?}", self.peek())));
2928 }
2929 let inner = self.parse_select_stmt()?;
2930 if !matches!(self.peek(), Token::RParen) {
2931 return Err(self.err(format!(
2932 "expected ')' after CTE body, got {:?}",
2933 self.peek()
2934 )));
2935 }
2936 self.advance();
2937 let Statement::Select(body) = inner else {
2938 unreachable!("parse_select_stmt returns Select")
2939 };
2940 ctes.push(crate::ast::Cte {
2941 name,
2942 body,
2943 recursive,
2944 column_overrides,
2945 });
2946 if matches!(self.peek(), Token::Comma) {
2947 self.advance();
2948 continue;
2949 }
2950 break;
2951 }
2952 if !matches!(self.peek(), Token::Select) {
2954 return Err(self.err(format!(
2955 "expected SELECT after WITH clause, got {:?}",
2956 self.peek()
2957 )));
2958 }
2959 let body_stmt = self.parse_select_stmt()?;
2960 let Statement::Select(mut body) = body_stmt else {
2961 unreachable!()
2962 };
2963 body.ctes = ctes;
2964 Ok(Statement::Select(body))
2965 }
2966
2967 fn parse_exists_atom(&mut self, negated: bool) -> Result<Expr, ParseError> {
2971 if !matches!(self.peek(), Token::LParen) {
2972 return Err(self.err(format!("expected '(' after EXISTS, got {:?}", self.peek())));
2973 }
2974 self.advance();
2975 let inner = self.parse_select_stmt()?;
2976 if !matches!(self.peek(), Token::RParen) {
2977 return Err(self.err(format!(
2978 "expected ')' after EXISTS-subquery, got {:?}",
2979 self.peek()
2980 )));
2981 }
2982 self.advance();
2983 let Statement::Select(s) = inner else {
2984 unreachable!("parse_select_stmt returns Select")
2985 };
2986 Ok(Expr::Exists {
2987 subquery: Box::new(s),
2988 negated,
2989 })
2990 }
2991
2992 fn parse_in_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
2993 self.advance(); if !matches!(self.peek(), Token::LParen) {
2995 return Err(self.err(format!("expected '(' after IN, got {:?}", self.peek())));
2996 }
2997 self.advance();
2998 if matches!(self.peek(), Token::Select) {
3000 let inner = self.parse_select_stmt()?;
3001 if !matches!(self.peek(), Token::RParen) {
3002 return Err(self.err(format!(
3003 "expected ')' after IN-subquery, got {:?}",
3004 self.peek()
3005 )));
3006 }
3007 self.advance();
3008 let Statement::Select(s) = inner else {
3009 unreachable!("parse_select_stmt always returns Statement::Select")
3010 };
3011 return Ok(Expr::InSubquery {
3012 expr: Box::new(expr),
3013 subquery: Box::new(s),
3014 negated,
3015 });
3016 }
3017 let mut elements = Vec::new();
3018 if !matches!(self.peek(), Token::RParen) {
3019 loop {
3020 elements.push(self.parse_expr(0)?);
3021 match self.peek() {
3022 Token::Comma => {
3023 self.advance();
3024 }
3025 Token::RParen => break,
3026 other => {
3027 return Err(
3028 self.err(format!("expected ',' or ')' in IN list, got {other:?}"))
3029 );
3030 }
3031 }
3032 }
3033 }
3034 self.advance(); let target = Box::new(expr);
3036 let combined = if elements.is_empty() {
3037 Expr::Literal(Literal::Bool(false))
3038 } else {
3039 let mut iter = elements.into_iter();
3040 let first = iter.next().unwrap();
3041 let mut acc = Expr::Binary {
3042 lhs: target.clone(),
3043 op: BinOp::Eq,
3044 rhs: Box::new(first),
3045 };
3046 for elt in iter {
3047 acc = Expr::Binary {
3048 lhs: Box::new(acc),
3049 op: BinOp::Or,
3050 rhs: Box::new(Expr::Binary {
3051 lhs: target.clone(),
3052 op: BinOp::Eq,
3053 rhs: Box::new(elt),
3054 }),
3055 };
3056 }
3057 acc
3058 };
3059 Ok(maybe_not(combined, negated))
3060 }
3061
3062 fn parse_extract_atom(&mut self) -> Result<Expr, ParseError> {
3070 if !matches!(self.peek(), Token::LParen) {
3071 return Err(self.err(format!("expected '(' after EXTRACT, got {:?}", self.peek())));
3072 }
3073 self.advance();
3074 let field_name = self.expect_ident_like()?;
3075 let field = match field_name.to_ascii_lowercase().as_str() {
3076 "year" => ExtractField::Year,
3077 "month" => ExtractField::Month,
3078 "day" => ExtractField::Day,
3079 "hour" => ExtractField::Hour,
3080 "minute" => ExtractField::Minute,
3081 "second" => ExtractField::Second,
3082 "microsecond" | "microseconds" => ExtractField::Microsecond,
3083 other => {
3084 return Err(self.err(format!(
3085 "unknown EXTRACT field {other:?}; \
3086 supported: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MICROSECOND"
3087 )));
3088 }
3089 };
3090 if !matches!(self.peek(), Token::From) {
3091 return Err(self.err(format!(
3092 "expected FROM after EXTRACT field, got {:?}",
3093 self.peek()
3094 )));
3095 }
3096 self.advance();
3097 let source = self.parse_expr(0)?;
3098 if !matches!(self.peek(), Token::RParen) {
3099 return Err(self.err(format!(
3100 "expected ')' to close EXTRACT, got {:?}",
3101 self.peek()
3102 )));
3103 }
3104 self.advance();
3105 Ok(Expr::Extract {
3106 field,
3107 source: Box::new(source),
3108 })
3109 }
3110
3111 fn parse_interval_atom(&mut self) -> Result<Expr, ParseError> {
3116 let tok = self.advance();
3117 let Token::String(text) = tok else {
3118 return Err(self.err(format!(
3119 "expected string literal after INTERVAL, got {tok:?}"
3120 )));
3121 };
3122 let (months, micros) = parse_interval_text(&text).ok_or_else(|| ParseError {
3123 message: format!(
3124 "cannot parse INTERVAL {text:?}; \
3125 expected `<n> <unit> [<n> <unit> ...]` with units \
3126 microsecond[s], millisecond[s], second[s], minute[s], \
3127 hour[s], day[s], week[s], month[s], year[s]"
3128 ),
3129 token_pos: self.pos.saturating_sub(1),
3130 })?;
3131 Ok(Expr::Literal(Literal::Interval {
3132 months,
3133 micros,
3134 text,
3135 }))
3136 }
3137
3138 fn parse_vector_literal_body(&mut self) -> Result<Expr, ParseError> {
3139 let mut elems = Vec::new();
3140 if matches!(self.peek(), Token::RBracket) {
3141 self.advance();
3142 return Ok(Expr::Literal(Literal::Vector(elems)));
3143 }
3144 loop {
3145 let e = self.parse_expr(0)?;
3146 let x = extract_numeric_literal(&e).ok_or_else(|| ParseError {
3147 message: format!("vector element must be a numeric literal, got {e:?}"),
3148 token_pos: self.pos,
3149 })?;
3150 elems.push(x);
3151 match self.peek() {
3152 Token::Comma => {
3153 self.advance();
3154 }
3155 Token::RBracket => {
3156 self.advance();
3157 break;
3158 }
3159 other => {
3160 return Err(self.err(format!("expected ',' or ']' in vector, got {other:?}")));
3161 }
3162 }
3163 }
3164 Ok(Expr::Literal(Literal::Vector(elems)))
3165 }
3166
3167 fn parse_null_treatment_modifier(&mut self) -> NullTreatment {
3176 let Token::Ident(s) = self.peek().clone() else {
3177 return NullTreatment::Respect;
3178 };
3179 let is_ignore = s.eq_ignore_ascii_case("ignore");
3180 let is_respect = s.eq_ignore_ascii_case("respect");
3181 if !is_ignore && !is_respect {
3182 return NullTreatment::Respect;
3183 }
3184 if self.pos + 1 < self.tokens.len()
3187 && let Token::Ident(s2) = &self.tokens[self.pos + 1]
3188 && s2.eq_ignore_ascii_case("nulls")
3189 {
3190 self.advance();
3191 self.advance();
3192 return if is_ignore {
3193 NullTreatment::Ignore
3194 } else {
3195 NullTreatment::Respect
3196 };
3197 }
3198 NullTreatment::Respect
3199 }
3200
3201 #[allow(clippy::type_complexity)] fn parse_over_clause(
3204 &mut self,
3205 ) -> Result<(Vec<Expr>, Vec<(Expr, bool)>, Option<WindowFrame>), ParseError> {
3206 if !matches!(self.peek(), Token::LParen) {
3207 return Err(self.err(format!("expected '(' after OVER, got {:?}", self.peek())));
3208 }
3209 self.advance();
3210 let mut partition_by = Vec::new();
3211 let mut order_by = Vec::new();
3212 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
3214 && s.eq_ignore_ascii_case("partition")
3215 {
3216 self.advance();
3217 if !matches!(self.peek(), Token::By) {
3218 return Err(self.err(format!(
3219 "expected BY after PARTITION, got {:?}",
3220 self.peek()
3221 )));
3222 }
3223 self.advance();
3224 loop {
3225 partition_by.push(self.parse_expr(0)?);
3226 if matches!(self.peek(), Token::Comma) {
3227 self.advance();
3228 continue;
3229 }
3230 break;
3231 }
3232 }
3233 if matches!(self.peek(), Token::Order) {
3235 self.advance();
3236 if !matches!(self.peek(), Token::By) {
3237 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
3238 }
3239 self.advance();
3240 loop {
3241 let e = self.parse_expr(0)?;
3242 let desc = if matches!(self.peek(), Token::Desc) {
3243 self.advance();
3244 true
3245 } else if matches!(self.peek(), Token::Asc) {
3246 self.advance();
3247 false
3248 } else {
3249 false
3250 };
3251 order_by.push((e, desc));
3252 if matches!(self.peek(), Token::Comma) {
3253 self.advance();
3254 continue;
3255 }
3256 break;
3257 }
3258 }
3259 let mut frame: Option<WindowFrame> = None;
3263 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
3264 let kind = if s.eq_ignore_ascii_case("rows") {
3265 Some(FrameKind::Rows)
3266 } else if s.eq_ignore_ascii_case("range") {
3267 Some(FrameKind::Range)
3268 } else {
3269 None
3270 };
3271 if let Some(kind) = kind {
3272 self.advance();
3273 frame = Some(self.parse_frame_tail(kind)?);
3274 }
3275 }
3276 if !matches!(self.peek(), Token::RParen) {
3277 return Err(self.err(format!(
3278 "expected ')' to close OVER clause, got {:?}",
3279 self.peek()
3280 )));
3281 }
3282 self.advance();
3283 Ok((partition_by, order_by, frame))
3284 }
3285
3286 fn parse_frame_tail(&mut self, kind: FrameKind) -> Result<WindowFrame, ParseError> {
3292 if matches!(self.peek(), Token::Between) {
3293 self.advance();
3294 let start = self.parse_frame_bound()?;
3295 if !matches!(self.peek(), Token::And) {
3296 return Err(self.err(format!("expected AND in frame spec, got {:?}", self.peek())));
3297 }
3298 self.advance();
3299 let end = self.parse_frame_bound()?;
3300 Ok(WindowFrame {
3301 kind,
3302 start,
3303 end: Some(end),
3304 })
3305 } else {
3306 let start = self.parse_frame_bound()?;
3307 Ok(WindowFrame {
3308 kind,
3309 start,
3310 end: None,
3311 })
3312 }
3313 }
3314
3315 fn parse_frame_bound(&mut self) -> Result<FrameBound, ParseError> {
3318 if let Token::Integer(n) = *self.peek() {
3320 self.advance();
3321 let n: u64 = u64::try_from(n).map_err(|_| {
3322 self.err(format!(
3323 "invalid frame offset {n} — expected non-negative integer"
3324 ))
3325 })?;
3326 let dir = self.expect_ident_like()?;
3327 return if dir.eq_ignore_ascii_case("preceding") {
3328 Ok(FrameBound::OffsetPreceding(n))
3329 } else if dir.eq_ignore_ascii_case("following") {
3330 Ok(FrameBound::OffsetFollowing(n))
3331 } else {
3332 Err(self.err(format!(
3333 "expected PRECEDING or FOLLOWING after offset, got {dir:?}"
3334 )))
3335 };
3336 }
3337 let first = self.expect_ident_like()?;
3338 if first.eq_ignore_ascii_case("unbounded") {
3339 let dir = self.expect_ident_like()?;
3340 return if dir.eq_ignore_ascii_case("preceding") {
3341 Ok(FrameBound::UnboundedPreceding)
3342 } else if dir.eq_ignore_ascii_case("following") {
3343 Ok(FrameBound::UnboundedFollowing)
3344 } else {
3345 Err(self.err(format!(
3346 "expected PRECEDING or FOLLOWING after UNBOUNDED, got {dir:?}"
3347 )))
3348 };
3349 }
3350 if first.eq_ignore_ascii_case("current") {
3351 let row = self.expect_ident_like()?;
3352 if !row.eq_ignore_ascii_case("row") {
3353 return Err(self.err(format!("expected ROW after CURRENT, got {row:?}")));
3354 }
3355 return Ok(FrameBound::CurrentRow);
3356 }
3357 Err(self.err(format!(
3358 "expected frame bound (UNBOUNDED/CURRENT/<n>), got {first:?}"
3359 )))
3360 }
3361
3362 fn finish_ident_atom(&mut self, first: String) -> Result<Expr, ParseError> {
3363 if matches!(self.peek(), Token::Dot) {
3364 self.advance();
3365 let name = self.expect_ident_like()?;
3366 return Ok(Expr::Column(ColumnName {
3367 qualifier: Some(first),
3368 name,
3369 }));
3370 }
3371 if matches!(self.peek(), Token::LParen) {
3372 self.advance();
3373 if first.eq_ignore_ascii_case("count") && matches!(self.peek(), Token::Star) {
3377 self.advance();
3378 if !matches!(self.peek(), Token::RParen) {
3379 return Err(self.err(format!(
3380 "expected ')' after COUNT(*), got {:?}",
3381 self.peek()
3382 )));
3383 }
3384 self.advance();
3385 let null_treatment = self.parse_null_treatment_modifier();
3387 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
3388 && s.eq_ignore_ascii_case("over")
3389 {
3390 self.advance();
3391 let (partition_by, order_by, frame) = self.parse_over_clause()?;
3392 return Ok(Expr::WindowFunction {
3393 name: "count_star".into(),
3394 args: Vec::new(),
3395 partition_by,
3396 order_by,
3397 frame,
3398 null_treatment,
3399 });
3400 }
3401 return Ok(Expr::FunctionCall {
3402 name: "count_star".into(),
3403 args: Vec::new(),
3404 });
3405 }
3406 let mut args = Vec::new();
3408 if !matches!(self.peek(), Token::RParen) {
3409 loop {
3410 args.push(self.parse_expr(0)?);
3411 match self.peek() {
3412 Token::Comma => {
3413 self.advance();
3414 }
3415 Token::RParen => break,
3416 other => {
3417 return Err(self.err(format!(
3418 "expected ',' or ')' in function args, got {other:?}"
3419 )));
3420 }
3421 }
3422 }
3423 }
3424 self.advance(); let null_treatment = self.parse_null_treatment_modifier();
3432 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
3433 && s.eq_ignore_ascii_case("over")
3434 {
3435 self.advance();
3436 let (partition_by, order_by, frame) = self.parse_over_clause()?;
3437 return Ok(Expr::WindowFunction {
3438 name: first,
3439 args,
3440 partition_by,
3441 order_by,
3442 frame,
3443 null_treatment,
3444 });
3445 }
3446 return Ok(Expr::FunctionCall { name: first, args });
3447 }
3448 let lc = first.to_ascii_lowercase();
3454 if matches!(
3455 lc.as_str(),
3456 "current_date" | "current_time" | "current_timestamp" | "localtimestamp" | "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!(
4203 c.extra_columns,
4204 vec!["uid".to_string(), "recurrence_id".to_string()]
4205 );
4206 assert!(c.partial_predicate.is_some());
4207 }
4208
4209 #[test]
4210 fn create_unique_index_using_btree_ok() {
4211 let s = parse("CREATE UNIQUE INDEX uq_x ON t USING btree (a)");
4212 assert!(matches!(s, Statement::CreateIndex(ref c) if c.is_unique));
4213 }
4214
4215 #[test]
4216 fn create_unique_index_using_hnsw_rejected() {
4217 let err =
4218 parse_statement("CREATE UNIQUE INDEX uq_v ON t USING hnsw (embedding)").unwrap_err();
4219 assert!(err.message.contains("UNIQUE"), "{}", err.message);
4220 }
4221
4222 #[test]
4223 fn create_unique_index_round_trip() {
4224 let original = parse(
4225 "CREATE UNIQUE INDEX uq_calendar_events_master \
4226 ON calendar_events (calendar_id, uid) WHERE recurrence_id IS NULL",
4227 );
4228 let again = parse_statement(&original.to_string()).unwrap();
4229 assert_eq!(original, again);
4230 }
4231
4232 #[test]
4233 fn create_unique_without_index_errors() {
4234 let err = parse_statement("CREATE UNIQUE TABLE t (a INT)").unwrap_err();
4235 assert!(err.message.contains("INDEX"), "{}", err.message);
4236 }
4237
4238 #[test]
4241 fn create_table_bytea_column() {
4242 let s = parse("CREATE TABLE t (id INT NOT NULL, payload BYTEA NOT NULL)");
4243 let Statement::CreateTable(c) = s else {
4244 panic!("expected CreateTable");
4245 };
4246 assert_eq!(c.columns.len(), 2);
4247 assert_eq!(c.columns[1].ty, ColumnTypeName::Bytes);
4248 assert!(!c.columns[1].nullable);
4249 }
4250
4251 #[test]
4252 fn create_table_bytes_alias_column() {
4253 let s = parse("CREATE TABLE t (blob BYTES)");
4254 let Statement::CreateTable(c) = s else {
4255 panic!("expected CreateTable");
4256 };
4257 assert_eq!(c.columns[0].ty, ColumnTypeName::Bytes);
4258 }
4259
4260 #[test]
4261 fn bytea_round_trip_display() {
4262 let original = parse("CREATE TABLE t (a BYTEA NOT NULL)");
4263 let again = parse_statement(&original.to_string()).unwrap();
4264 assert_eq!(original, again);
4265 }
4266
4267 #[test]
4270 fn begin_commit_rollback_parse_as_unit_variants() {
4271 assert_eq!(parse("BEGIN"), Statement::Begin);
4272 assert_eq!(parse("COMMIT"), Statement::Commit);
4273 assert_eq!(parse("ROLLBACK"), Statement::Rollback);
4274 assert_eq!(parse("BEGIN;"), Statement::Begin);
4276 }
4277
4278 #[test]
4281 fn inner_product_binop_parses() {
4282 let s = parse("SELECT v <#> [1.0, 2.0] FROM t");
4283 let Statement::Select(s) = s else { panic!() };
4284 let SelectItem::Expr { expr, .. } = &s.items[0] else {
4285 panic!()
4286 };
4287 assert!(matches!(
4288 expr,
4289 Expr::Binary {
4290 op: BinOp::InnerProduct,
4291 ..
4292 }
4293 ));
4294 }
4295
4296 #[test]
4297 fn cosine_distance_binop_parses() {
4298 let s = parse("SELECT v <=> [1.0, 2.0] FROM t");
4299 let Statement::Select(s) = s else { panic!() };
4300 let SelectItem::Expr { expr, .. } = &s.items[0] else {
4301 panic!()
4302 };
4303 assert!(matches!(
4304 expr,
4305 Expr::Binary {
4306 op: BinOp::CosineDistance,
4307 ..
4308 }
4309 ));
4310 }
4311
4312 #[test]
4313 fn vector_cast_postfix_wraps_string_literal() {
4314 let s = parse("SELECT '[1,2,3]'::vector FROM t");
4315 let Statement::Select(s) = s else { panic!() };
4316 let SelectItem::Expr { expr, .. } = &s.items[0] else {
4317 panic!()
4318 };
4319 assert!(matches!(
4320 expr,
4321 Expr::Cast {
4322 target: CastTarget::Vector,
4323 ..
4324 }
4325 ));
4326 }
4327
4328 #[test]
4329 fn unsupported_cast_target_errors() {
4330 let err = parse_statement("SELECT 1::numeric FROM t").unwrap_err();
4332 assert!(err.message.contains("unsupported cast target"));
4333 }
4334
4335 #[test]
4336 fn tx_statements_round_trip() {
4337 for q in ["BEGIN", "COMMIT", "ROLLBACK"] {
4338 let original = parse(q);
4339 let again = parse_statement(&original.to_string()).unwrap();
4340 assert_eq!(original, again);
4341 }
4342 }
4343
4344 #[test]
4345 fn interval_text_parsing_units() {
4346 assert_eq!(parse_interval_text("1 day"), Some((0, 86_400_000_000)));
4348 assert_eq!(parse_interval_text("1 second"), Some((0, 1_000_000)));
4349 assert_eq!(parse_interval_text("1 month"), Some((1, 0)));
4350 assert_eq!(parse_interval_text("2 years"), Some((24, 0)));
4351 assert_eq!(parse_interval_text("1 year 6 months"), Some((18, 0)));
4353 assert_eq!(
4354 parse_interval_text("1 day 2 hours"),
4355 Some((0, 86_400_000_000 + 7_200_000_000))
4356 );
4357 assert_eq!(parse_interval_text("-1 day"), Some((0, -86_400_000_000)));
4359 assert_eq!(parse_interval_text(""), None);
4361 assert_eq!(parse_interval_text("garbage"), None);
4362 assert_eq!(parse_interval_text("1 fortnight"), None);
4363 assert_eq!(parse_interval_text("1"), None);
4364 }
4365
4366 #[test]
4367 fn interval_literal_roundtrips_via_display() {
4368 let parsed = parse("SELECT INTERVAL '1 day 2 hours'");
4369 let s = parsed.to_string();
4370 assert!(s.contains("INTERVAL '1 day 2 hours'"), "got: {s}");
4372 let again = parse_statement(&s).unwrap();
4374 assert_eq!(parsed, again);
4375 }
4376
4377 #[test]
4380 fn parser_recognises_create_publication_bare() {
4381 let s = parse("CREATE PUBLICATION pub_a");
4382 let Statement::CreatePublication(p) = s else {
4383 panic!("expected CreatePublication, got {s:?}")
4384 };
4385 assert_eq!(p.name, "pub_a");
4386 assert_eq!(p.scope, PublicationScope::AllTables);
4387 }
4388
4389 #[test]
4390 fn parser_recognises_create_publication_for_all_tables() {
4391 let s = parse("CREATE PUBLICATION pub_a FOR ALL TABLES");
4392 let Statement::CreatePublication(p) = s else {
4393 panic!("expected CreatePublication, got {s:?}")
4394 };
4395 assert_eq!(p.name, "pub_a");
4396 assert_eq!(p.scope, PublicationScope::AllTables);
4397 }
4398
4399 #[test]
4400 fn parser_recognises_drop_publication() {
4401 let s = parse("DROP PUBLICATION pub_a");
4402 let Statement::DropPublication(name) = s else {
4403 panic!("expected DropPublication, got {s:?}")
4404 };
4405 assert_eq!(name, "pub_a");
4406 }
4407
4408 #[test]
4409 fn parser_recognises_for_table_list() {
4410 let s = parse("CREATE PUBLICATION pub_a FOR TABLE t1, t2, t3");
4411 let Statement::CreatePublication(p) = s else {
4412 panic!("expected CreatePublication, got {s:?}")
4413 };
4414 assert_eq!(p.name, "pub_a");
4415 let PublicationScope::ForTables(ts) = p.scope else {
4416 panic!("expected ForTables scope")
4417 };
4418 assert_eq!(ts, alloc::vec!["t1", "t2", "t3"]);
4419 }
4420
4421 #[test]
4422 fn parser_recognises_for_tables_plural() {
4423 let s = parse("CREATE PUBLICATION pub_a FOR TABLES t1, t2");
4425 let Statement::CreatePublication(p) = s else {
4426 panic!("expected CreatePublication, got {s:?}")
4427 };
4428 let PublicationScope::ForTables(ts) = p.scope else {
4429 panic!("expected ForTables")
4430 };
4431 assert_eq!(ts, alloc::vec!["t1", "t2"]);
4432 }
4433
4434 #[test]
4435 fn parser_recognises_for_all_tables_except_list() {
4436 let s = parse("CREATE PUBLICATION p FOR ALL TABLES EXCEPT t1, t2");
4437 let Statement::CreatePublication(p) = s else {
4438 panic!()
4439 };
4440 let PublicationScope::AllTablesExcept(ts) = p.scope else {
4441 panic!("expected AllTablesExcept")
4442 };
4443 assert_eq!(ts, alloc::vec!["t1", "t2"]);
4444 }
4445
4446 #[test]
4447 fn parser_rejects_for_table_with_empty_list() {
4448 let err = parse_statement("CREATE PUBLICATION p FOR TABLE")
4450 .expect_err("must error on empty list");
4451 assert!(!err.message.is_empty());
4454 }
4455
4456 #[test]
4457 fn parser_recognises_show_publications() {
4458 let s = parse("SHOW PUBLICATIONS");
4461 assert!(matches!(s, Statement::ShowPublications));
4462 }
4463
4464 #[test]
4467 fn parser_recognises_create_subscription_single_publication() {
4468 let s = parse(
4469 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=127.0.0.1 port=20002' PUBLICATION pub_a",
4470 );
4471 let Statement::CreateSubscription(c) = s else {
4472 panic!("expected CreateSubscription, got {s:?}")
4473 };
4474 assert_eq!(c.name, "sub_a");
4475 assert_eq!(c.conn_str, "host=127.0.0.1 port=20002");
4476 assert_eq!(c.publications, alloc::vec!["pub_a"]);
4477 }
4478
4479 #[test]
4480 fn parser_recognises_create_subscription_multi_publication() {
4481 let s = parse("CREATE SUBSCRIPTION sub_a CONNECTION 'host=h' PUBLICATION p1, p2, p3");
4482 let Statement::CreateSubscription(c) = s else {
4483 panic!()
4484 };
4485 assert_eq!(c.publications, alloc::vec!["p1", "p2", "p3"]);
4486 }
4487
4488 #[test]
4489 fn parser_rejects_create_subscription_missing_connection() {
4490 let err = parse_statement("CREATE SUBSCRIPTION s PUBLICATION p")
4491 .expect_err("must error on missing CONNECTION");
4492 assert!(err.message.contains("CONNECTION"), "got: {}", err.message);
4493 }
4494
4495 #[test]
4496 fn parser_rejects_create_subscription_missing_publication() {
4497 let err = parse_statement("CREATE SUBSCRIPTION s CONNECTION 'host=x'")
4498 .expect_err("must error on missing PUBLICATION");
4499 assert!(err.message.contains("PUBLICATION"), "got: {}", err.message);
4500 }
4501
4502 #[test]
4503 fn parser_recognises_drop_subscription() {
4504 let s = parse("DROP SUBSCRIPTION sub_a");
4505 let Statement::DropSubscription(name) = s else {
4506 panic!("expected DropSubscription, got {s:?}")
4507 };
4508 assert_eq!(name, "sub_a");
4509 }
4510
4511 #[test]
4512 fn parser_recognises_show_subscriptions() {
4513 let s = parse("SHOW SUBSCRIPTIONS");
4514 assert!(matches!(s, Statement::ShowSubscriptions));
4515 }
4516
4517 #[test]
4518 fn parser_recognises_wait_for_wal_position_no_timeout() {
4519 let s = parse("WAIT FOR WAL POSITION 12345");
4520 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
4521 panic!("expected WaitForWalPosition, got {s:?}")
4522 };
4523 assert_eq!(pos, 12345);
4524 assert!(timeout_ms.is_none());
4525 }
4526
4527 #[test]
4528 fn parser_recognises_wait_for_wal_position_with_timeout() {
4529 let s = parse("WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000");
4530 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
4531 panic!()
4532 };
4533 assert_eq!(pos, 67890);
4534 assert_eq!(timeout_ms, Some(5000));
4535 }
4536
4537 #[test]
4538 fn parser_rejects_wait_with_negative_position() {
4539 let err = parse_statement("WAIT FOR WAL POSITION -1").unwrap_err();
4545 assert!(!err.message.is_empty());
4546 }
4547
4548 #[test]
4549 fn parser_recognises_bare_analyze() {
4550 let s = parse("ANALYZE");
4551 assert!(matches!(s, Statement::Analyze(None)));
4552 }
4553
4554 #[test]
4555 fn parser_recognises_analyze_with_table() {
4556 let s = parse("ANALYZE users");
4557 let Statement::Analyze(Some(name)) = s else {
4558 panic!("expected Analyze, got {s:?}")
4559 };
4560 assert_eq!(name, "users");
4561 }
4562
4563 #[test]
4564 fn parser_recognises_analyze_with_quoted_table() {
4565 let s = parse("ANALYZE \"Mixed Case\"");
4566 let Statement::Analyze(Some(name)) = s else {
4567 panic!()
4568 };
4569 assert_eq!(name, "Mixed Case");
4570 }
4571
4572 #[test]
4573 fn parser_rejects_analyze_with_garbage_token() {
4574 let err = parse_statement("ANALYZE 42").expect_err("must error");
4575 assert!(!err.message.is_empty());
4576 }
4577
4578 #[test]
4579 fn analyze_display_roundtrips() {
4580 for sql in ["ANALYZE", "ANALYZE users"] {
4581 let s = parse(sql);
4582 let printed = s.to_string();
4583 let again = parse_statement(&printed)
4584 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
4585 assert_eq!(s, again);
4586 }
4587 }
4588
4589 #[test]
4590 fn wait_for_display_roundtrips() {
4591 for sql in [
4592 "WAIT FOR WAL POSITION 12345",
4593 "WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000",
4594 ] {
4595 let s = parse(sql);
4596 let printed = s.to_string();
4597 let again = parse_statement(&printed)
4598 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
4599 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
4600 }
4601 }
4602
4603 #[test]
4604 fn subscription_ddl_display_roundtrips() {
4605 for sql in [
4606 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=h port=20002' PUBLICATION pub_a",
4607 "CREATE SUBSCRIPTION sub_b CONNECTION 'host=h' PUBLICATION p1, p2",
4608 "DROP SUBSCRIPTION sub_a",
4609 "SHOW SUBSCRIPTIONS",
4610 ] {
4611 let s = parse(sql);
4612 let printed = s.to_string();
4613 let again = parse_statement(&printed)
4614 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
4615 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
4616 }
4617 }
4618
4619 #[test]
4620 fn parser_drop_dispatches_user_vs_publication() {
4621 let s = parse("DROP USER 'alice'");
4624 let Statement::DropUser(name) = s else {
4625 panic!("expected DropUser, got {s:?}")
4626 };
4627 assert_eq!(name, "alice");
4628 let s = parse("DROP PUBLICATION p1");
4630 assert!(matches!(s, Statement::DropPublication(_)));
4631 }
4632
4633 #[test]
4634 fn publication_ddl_display_roundtrips() {
4635 for sql in [
4638 "CREATE PUBLICATION pub_a",
4639 "CREATE PUBLICATION pub_a FOR ALL TABLES",
4640 "CREATE PUBLICATION pub_a FOR TABLE t1, t2",
4641 "CREATE PUBLICATION pub_a FOR ALL TABLES EXCEPT t1",
4642 "DROP PUBLICATION pub_a",
4643 "SHOW PUBLICATIONS",
4644 ] {
4645 let s = parse(sql);
4646 let printed = s.to_string();
4647 let again = parse_statement(&printed)
4648 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
4649 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
4650 }
4651 }
4652}