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
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct ParseError {
31 pub message: String,
32 pub token_pos: usize,
34}
35
36impl fmt::Display for ParseError {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 write!(
39 f,
40 "parse error at token #{}: {}",
41 self.token_pos, self.message
42 )
43 }
44}
45
46impl From<LexError> for ParseError {
47 fn from(e: LexError) -> Self {
48 Self {
49 message: format!("lex: {e}"),
50 token_pos: 0,
51 }
52 }
53}
54
55pub fn parse_statement(input: &str) -> Result<Statement, ParseError> {
58 let tokens = lexer::tokenize(input)?;
59 let mut p = Parser::new(tokens);
60 let stmt = p.parse_one_statement()?;
61 if matches!(p.peek(), Token::Semicolon) {
62 p.advance();
63 }
64 p.expect_eof()?;
65 Ok(stmt)
66}
67
68struct Parser {
69 tokens: Vec<Token>,
70 pos: usize,
71}
72
73impl Parser {
74 fn new(tokens: Vec<Token>) -> Self {
75 Self { tokens, pos: 0 }
76 }
77
78 fn peek(&self) -> &Token {
79 &self.tokens[self.pos]
81 }
82
83 fn advance(&mut self) -> Token {
84 let t = mem::replace(&mut self.tokens[self.pos], Token::Eof);
85 if self.pos + 1 < self.tokens.len() {
86 self.pos += 1;
87 }
88 t
89 }
90
91 fn err(&self, message: String) -> ParseError {
92 ParseError {
93 message,
94 token_pos: self.pos,
95 }
96 }
97
98 fn expect_eof(&self) -> Result<(), ParseError> {
99 if matches!(self.peek(), Token::Eof) {
100 Ok(())
101 } else {
102 Err(self.err(format!("expected end of input, got {:?}", self.peek())))
103 }
104 }
105
106 fn expect_ident_like(&mut self) -> Result<String, ParseError> {
107 match self.advance() {
108 Token::Ident(s) | Token::QuotedIdent(s) => Ok(s),
109 other => Err(ParseError {
110 message: format!("expected identifier, got {other:?}"),
111 token_pos: self.pos.saturating_sub(1),
112 }),
113 }
114 }
115
116 #[allow(clippy::too_many_lines)]
117 fn parse_one_statement(&mut self) -> Result<Statement, ParseError> {
118 match self.peek() {
119 Token::Select => self.parse_select_stmt(),
120 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with") => {
124 self.advance();
125 self.parse_with_cte_then_select()
126 }
127 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("explain") => {
130 self.advance();
131 let mut analyze = false;
132 let mut suggest = false;
133 if matches!(self.peek(), Token::LParen) {
135 self.advance();
136 let opt = match self.peek().clone() {
137 Token::Ident(s) | Token::QuotedIdent(s) => s,
138 other => {
139 return Err(self.err(format!(
140 "expected option keyword inside EXPLAIN (…), got {other:?}"
141 )));
142 }
143 };
144 if !opt.eq_ignore_ascii_case("suggest") {
145 return Err(self.err(format!(
146 "unknown EXPLAIN option {opt:?}; v6.8.3 supports SUGGEST"
147 )));
148 }
149 self.advance();
150 if !matches!(self.peek(), Token::RParen) {
151 return Err(self.err(format!(
152 "expected ')' after EXPLAIN option, got {:?}",
153 self.peek()
154 )));
155 }
156 self.advance();
157 suggest = true;
158 } else if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
159 && (s.eq_ignore_ascii_case("analyze") || s.eq_ignore_ascii_case("analyse"))
160 {
161 self.advance();
162 analyze = true;
163 }
164 let inner = self.parse_select_stmt()?;
165 let Statement::Select(s) = inner else {
166 return Err(self.err(format!("EXPLAIN body must be a SELECT, got {inner:?}")));
167 };
168 Ok(Statement::Explain(crate::ast::ExplainStatement {
169 analyze,
170 inner: Box::new(s),
171 suggest,
172 }))
173 }
174 Token::Create => self.parse_create_stmt(),
175 Token::Insert => self.parse_insert_stmt(),
176 Token::Begin => {
177 self.advance();
178 Ok(Statement::Begin)
179 }
180 Token::Commit => {
181 self.advance();
182 Ok(Statement::Commit)
183 }
184 Token::Rollback => {
185 self.advance();
186 if matches!(self.peek(), Token::To) {
190 self.advance();
191 if matches!(self.peek(), Token::Savepoint) {
192 self.advance();
193 }
194 let name = self.expect_ident_like()?;
195 Ok(Statement::RollbackToSavepoint(name))
196 } else {
197 Ok(Statement::Rollback)
198 }
199 }
200 Token::Savepoint => {
201 self.advance();
202 let name = self.expect_ident_like()?;
203 Ok(Statement::Savepoint(name))
204 }
205 Token::Release => {
206 self.advance();
207 if matches!(self.peek(), Token::Savepoint) {
210 self.advance();
211 }
212 let name = self.expect_ident_like()?;
213 Ok(Statement::ReleaseSavepoint(name))
214 }
215 Token::Show => {
216 self.advance();
217 let target = match self.advance() {
223 Token::Tables => "tables".to_string(),
224 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
225 other => {
226 return Err(self.err(format!(
227 "expected SHOW target, got {other:?}"
228 )));
229 }
230 };
231 match target.as_str() {
232 "tables" => Ok(Statement::ShowTables),
233 "users" => Ok(Statement::ShowUsers),
234 "publications" => Ok(Statement::ShowPublications),
239 "subscriptions" => Ok(Statement::ShowSubscriptions),
241 "columns" => {
242 if !matches!(self.peek(), Token::From) {
243 return Err(self.err(format!(
244 "expected FROM after SHOW COLUMNS, got {:?}",
245 self.peek()
246 )));
247 }
248 self.advance();
249 let table = self.expect_ident_like()?;
250 Ok(Statement::ShowColumns(table))
251 }
252 other => Err(self.err(format!(
253 "unknown SHOW target {other:?}; supported: TABLES, COLUMNS, USERS, PUBLICATIONS"
254 ))),
255 }
256 }
257 Token::Drop => {
263 self.advance();
264 match self.peek() {
265 Token::Publication => {
266 self.advance();
267 let name = self.expect_ident_or_string()?;
268 Ok(Statement::DropPublication(name))
269 }
270 Token::Subscription => {
271 self.advance();
272 let name = self.expect_ident_or_string()?;
273 Ok(Statement::DropSubscription(name))
274 }
275 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("user") => {
276 self.advance();
277 let name = self.expect_ident_or_string()?;
278 Ok(Statement::DropUser(name))
279 }
280 other => Err(self.err(format!(
281 "expected USER / PUBLICATION / SUBSCRIPTION after DROP, got {other:?}"
282 ))),
283 }
284 }
285 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
286 self.advance();
287 self.parse_update_after_keyword()
288 }
289 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
290 self.advance();
291 self.parse_delete_after_keyword()
292 }
293 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("alter") => {
297 self.advance();
298 self.parse_alter_after_keyword()
299 }
300 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("wait") => {
304 self.advance();
305 self.parse_wait_after_keyword()
306 }
307 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("compact") => {
318 self.advance();
319 let next = self.peek().clone();
320 let cold = match next {
321 Token::Ident(s) | Token::QuotedIdent(s) => s,
322 _ => {
323 return Err(
324 self.err(format!("expected COLD after COMPACT, got {:?}", self.peek()))
325 );
326 }
327 };
328 if !cold.eq_ignore_ascii_case("cold") {
329 return Err(self.err(format!("expected COLD after COMPACT, got {cold:?}")));
330 }
331 self.advance();
332 let next = self.peek().clone();
333 let segments = match next {
334 Token::Ident(s) | Token::QuotedIdent(s) => s,
335 _ => {
336 return Err(self.err(format!(
337 "expected SEGMENTS after COMPACT COLD, got {:?}",
338 self.peek()
339 )));
340 }
341 };
342 if !segments.eq_ignore_ascii_case("segments") {
343 return Err(self.err(format!(
344 "expected SEGMENTS after COMPACT COLD, got {segments:?}"
345 )));
346 }
347 self.advance();
348 Ok(Statement::CompactColdSegments)
349 }
350 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("analyze") => {
351 self.advance();
352 let target = match self.peek() {
353 Token::Eof | Token::Semicolon => None,
354 Token::Ident(_) | Token::QuotedIdent(_) => {
355 Some(self.expect_ident_like()?)
356 }
357 other => {
358 return Err(self.err(format!(
359 "expected table name or end of statement after ANALYZE, got {other:?}"
360 )));
361 }
362 };
363 Ok(Statement::Analyze(target))
364 }
365 other => Err(self.err(format!(
366 "expected SELECT / CREATE / DROP / INSERT / UPDATE / DELETE / ALTER / BEGIN / COMMIT / \
367 ROLLBACK / SAVEPOINT / RELEASE / SHOW at start of statement, got {other:?}"
368 ))),
369 }
370 }
371
372 fn parse_create_stmt(&mut self) -> Result<Statement, ParseError> {
373 debug_assert!(matches!(self.peek(), Token::Create));
374 self.advance();
375 match self.peek() {
376 Token::Table => self.parse_create_table_stmt_after_create(),
377 Token::Index => self.parse_create_index_stmt_after_create(),
378 Token::Publication => {
379 self.advance();
380 self.parse_create_publication_after_keyword()
381 }
382 Token::Subscription => {
383 self.advance();
384 self.parse_create_subscription_after_keyword()
385 }
386 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("user") => {
390 self.advance();
391 self.parse_create_user_after_keyword()
392 }
393 other => Err(self.err(format!(
394 "expected TABLE / INDEX / USER / PUBLICATION / SUBSCRIPTION after CREATE, got {other:?}"
395 ))),
396 }
397 }
398
399 fn parse_create_publication_after_keyword(&mut self) -> Result<Statement, ParseError> {
406 let name = self.expect_ident_or_string()?;
407 let scope = if matches!(self.peek(), Token::For) {
410 self.advance();
411 if matches!(self.peek(), Token::All) {
412 self.advance();
413 if !matches!(self.peek(), Token::Tables) {
414 return Err(self.err(format!(
415 "expected TABLES after FOR ALL, got {:?}",
416 self.peek()
417 )));
418 }
419 self.advance();
420 if matches!(self.peek(), Token::Except) {
421 self.advance();
422 let tables = self.parse_publication_table_list()?;
423 PublicationScope::AllTablesExcept(tables)
424 } else {
425 PublicationScope::AllTables
426 }
427 } else if matches!(self.peek(), Token::Table | Token::Tables) {
428 self.advance();
431 let tables = self.parse_publication_table_list()?;
432 PublicationScope::ForTables(tables)
433 } else {
434 return Err(self.err(format!(
435 "expected ALL TABLES or TABLE <list> after FOR, got {:?}",
436 self.peek()
437 )));
438 }
439 } else {
440 PublicationScope::AllTables
441 };
442 Ok(Statement::CreatePublication(CreatePublicationStatement {
443 name,
444 scope,
445 }))
446 }
447
448 fn parse_publication_table_list(&mut self) -> Result<Vec<String>, ParseError> {
453 let first = self.expect_ident_like()?;
454 let mut out = alloc::vec![first];
455 while matches!(self.peek(), Token::Comma) {
456 self.advance();
457 out.push(self.expect_ident_like()?);
458 }
459 Ok(out)
460 }
461
462 fn parse_create_subscription_after_keyword(&mut self) -> Result<Statement, ParseError> {
470 let name = self.expect_ident_or_string()?;
471 if !matches!(self.peek(), Token::Connection) {
472 return Err(self.err(format!(
473 "expected CONNECTION after CREATE SUBSCRIPTION <name>, got {:?}",
474 self.peek()
475 )));
476 }
477 self.advance();
478 let conn_str = self.expect_string_literal()?;
479 if !matches!(self.peek(), Token::Publication) {
480 return Err(self.err(format!(
481 "expected PUBLICATION after CONNECTION '<conn>', got {:?}",
482 self.peek()
483 )));
484 }
485 self.advance();
486 let first = self.expect_ident_like()?;
489 let mut publications = alloc::vec![first];
490 while matches!(self.peek(), Token::Comma) {
491 self.advance();
492 publications.push(self.expect_ident_like()?);
493 }
494 Ok(Statement::CreateSubscription(
495 CreateSubscriptionStatement {
496 name,
497 conn_str,
498 publications,
499 },
500 ))
501 }
502
503 fn parse_wait_after_keyword(&mut self) -> Result<Statement, ParseError> {
508 if !matches!(self.peek(), Token::For) {
512 return Err(self.err(format!(
513 "expected FOR after WAIT, got {:?}",
514 self.peek()
515 )));
516 }
517 self.advance();
518 self.expect_keyword_ident("wal")?;
519 self.expect_keyword_ident("position")?;
520 let pos = self.expect_u64_literal()?;
521 let timeout_ms = if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with"))
522 {
523 self.advance();
524 self.expect_keyword_ident("timeout")?;
525 Some(self.expect_u64_literal()?)
526 } else {
527 None
528 };
529 Ok(Statement::WaitForWalPosition { pos, timeout_ms })
530 }
531
532 fn expect_u64_literal(&mut self) -> Result<u64, ParseError> {
536 match self.advance() {
537 Token::Integer(n) if n >= 0 => Ok(n as u64),
538 Token::Integer(n) => Err(ParseError {
539 message: format!("expected non-negative integer, got {n}"),
540 token_pos: self.pos.saturating_sub(1),
541 }),
542 other => Err(ParseError {
543 message: format!("expected integer literal, got {other:?}"),
544 token_pos: self.pos.saturating_sub(1),
545 }),
546 }
547 }
548
549 fn parse_create_user_after_keyword(&mut self) -> Result<Statement, ParseError> {
553 let name = self.expect_ident_or_string()?;
554 self.expect_keyword_ident("with")?;
555 self.expect_keyword_ident("password")?;
556 let password = self.expect_string_literal()?;
557 let role = if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
558 && s.eq_ignore_ascii_case("role")
559 {
560 self.advance();
561 self.expect_string_literal()?
562 } else {
563 "readonly".to_string()
564 };
565 Ok(Statement::CreateUser(crate::ast::CreateUserStatement {
566 name,
567 password,
568 role,
569 }))
570 }
571
572 fn parse_update_after_keyword(&mut self) -> Result<Statement, ParseError> {
575 let table = self.expect_ident_like()?;
576 self.expect_keyword_ident("set")?;
577 let mut assignments = Vec::new();
578 loop {
579 let col = self.expect_ident_like()?;
580 if !matches!(self.peek(), Token::Eq) {
581 return Err(self.err(format!(
582 "expected `=` after column name in UPDATE SET, got {:?}",
583 self.peek()
584 )));
585 }
586 self.advance();
587 let value = self.parse_expr(0)?;
588 assignments.push((col, value));
589 if matches!(self.peek(), Token::Comma) {
590 self.advance();
591 continue;
592 }
593 break;
594 }
595 let where_ = if matches!(self.peek(), Token::Where) {
596 self.advance();
597 Some(self.parse_expr(0)?)
598 } else {
599 None
600 };
601 let returning = self.parse_optional_returning()?;
602 Ok(Statement::Update(crate::ast::UpdateStatement {
603 table,
604 assignments,
605 where_,
606 returning,
607 }))
608 }
609
610 fn parse_delete_after_keyword(&mut self) -> Result<Statement, ParseError> {
613 if !matches!(self.peek(), Token::From) {
614 return Err(self.err(format!("expected FROM after DELETE, got {:?}", self.peek())));
615 }
616 self.advance();
617 let table = self.expect_ident_like()?;
618 let where_ = if matches!(self.peek(), Token::Where) {
619 self.advance();
620 Some(self.parse_expr(0)?)
621 } else {
622 None
623 };
624 let returning = self.parse_optional_returning()?;
625 Ok(Statement::Delete(crate::ast::DeleteStatement {
626 table,
627 where_,
628 returning,
629 }))
630 }
631
632 fn parse_optional_returning(&mut self) -> Result<Option<Vec<crate::ast::SelectItem>>, ParseError> {
637 let is_returning_kw = matches!(
638 self.peek(),
639 Token::Ident(s) if s.eq_ignore_ascii_case("returning")
640 );
641 if !is_returning_kw {
642 return Ok(None);
643 }
644 self.advance();
645 let mut items = Vec::new();
646 loop {
647 items.push(self.parse_select_item()?);
648 if matches!(self.peek(), Token::Comma) {
649 self.advance();
650 continue;
651 }
652 break;
653 }
654 Ok(Some(items))
655 }
656
657 fn parse_alter_after_keyword(&mut self) -> Result<Statement, ParseError> {
665 match self.advance() {
667 Token::Index => {}
668 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("index") => {}
669 Token::Table => return self.parse_alter_table_after_keyword(),
671 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("table") => {
672 return self.parse_alter_table_after_keyword();
673 }
674 other => {
675 return Err(self.err(format!("expected INDEX or TABLE after ALTER, got {other:?}")));
676 }
677 }
678 let name = self.expect_ident_like()?;
679 self.expect_keyword_ident("rebuild")?;
681 let encoding = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
683 self.advance();
684 if !matches!(self.peek(), Token::LParen) {
685 return Err(self.err(format!(
686 "expected '(' after WITH in ALTER INDEX REBUILD, got {:?}",
687 self.peek()
688 )));
689 }
690 self.advance();
691 self.expect_keyword_ident("encoding")?;
692 if !matches!(self.peek(), Token::Eq) {
693 return Err(self.err(format!(
694 "expected '=' after encoding in ALTER INDEX REBUILD, got {:?}",
695 self.peek()
696 )));
697 }
698 self.advance();
699 let enc_ident = match self.advance() {
700 Token::Ident(s) | Token::QuotedIdent(s) => s,
701 other => {
702 return Err(self.err(format!("expected encoding name after =, got {other:?}")));
703 }
704 };
705 let enc = match enc_ident.to_ascii_lowercase().as_str() {
706 "f32" => VecEncoding::F32,
707 "sq8" => VecEncoding::Sq8,
708 "half" => VecEncoding::F16,
709 other => {
710 return Err(self.err(format!(
711 "unknown vector encoding {other:?} in ALTER INDEX REBUILD; supported: F32, SQ8, HALF"
712 )));
713 }
714 };
715 if !matches!(self.peek(), Token::RParen) {
716 return Err(self.err(format!(
717 "expected ')' after encoding value, got {:?}",
718 self.peek()
719 )));
720 }
721 self.advance();
722 Some(enc)
723 } else {
724 None
725 };
726 Ok(Statement::AlterIndex(crate::ast::AlterIndexStatement {
727 name,
728 target: crate::ast::AlterIndexTarget::Rebuild { encoding },
729 }))
730 }
731
732 fn parse_alter_table_after_keyword(&mut self) -> Result<Statement, ParseError> {
736 let table_name = self.expect_ident_like()?;
737 match self.peek() {
741 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
742 self.advance();
743 let setting = self.expect_ident_like()?;
744 if !setting.eq_ignore_ascii_case("hot_tier_bytes") {
745 return Err(self.err(alloc::format!(
746 "ALTER TABLE SET: unknown setting {setting:?}; supported: hot_tier_bytes"
747 )));
748 }
749 if !matches!(self.peek(), Token::Eq) {
750 return Err(self.err(alloc::format!(
751 "expected '=' after hot_tier_bytes, got {:?}",
752 self.peek()
753 )));
754 }
755 self.advance();
756 let n = self.expect_u64_literal()?;
757 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
758 name: table_name,
759 target: crate::ast::AlterTableTarget::SetHotTierBytes(n),
760 }))
761 }
762 Token::Ident(s) if s.eq_ignore_ascii_case("add") => {
763 self.advance();
764 let fk = self.parse_table_level_fk()?;
767 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
768 name: table_name,
769 target: crate::ast::AlterTableTarget::AddForeignKey(fk),
770 }))
771 }
772 Token::Drop => {
773 self.advance();
774 match self.advance() {
775 Token::Ident(s) if s.eq_ignore_ascii_case("constraint") => {}
776 other => {
777 return Err(self.err(alloc::format!(
778 "expected CONSTRAINT after DROP in ALTER TABLE, got {other:?}"
779 )));
780 }
781 }
782 let cname = self.expect_ident_like()?;
783 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
784 name: table_name,
785 target: crate::ast::AlterTableTarget::DropForeignKey(cname),
786 }))
787 }
788 other => Err(self.err(alloc::format!(
789 "expected SET / ADD / DROP in ALTER TABLE, got {other:?}"
790 ))),
791 }
792 }
793
794 fn expect_keyword_ident(&mut self, kw: &str) -> Result<(), ParseError> {
796 match self.advance() {
797 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case(kw) => Ok(()),
798 other => Err(ParseError {
799 message: format!("expected {kw:?}, got {other:?}"),
800 token_pos: self.pos.saturating_sub(1),
801 }),
802 }
803 }
804
805 fn expect_ident_or_string(&mut self) -> Result<String, ParseError> {
809 match self.advance() {
810 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => Ok(s),
811 other => Err(ParseError {
812 message: format!("expected identifier or string, got {other:?}"),
813 token_pos: self.pos.saturating_sub(1),
814 }),
815 }
816 }
817
818 fn expect_string_literal(&mut self) -> Result<String, ParseError> {
819 match self.advance() {
820 Token::String(s) => Ok(s),
821 other => Err(ParseError {
822 message: format!("expected quoted string, got {other:?}"),
823 token_pos: self.pos.saturating_sub(1),
824 }),
825 }
826 }
827
828 fn parse_select_stmt(&mut self) -> Result<Statement, ParseError> {
829 let mut head = self.parse_bare_select()?;
834 while matches!(self.peek(), Token::Union) {
835 self.advance();
836 let kind = if matches!(self.peek(), Token::All) {
837 self.advance();
838 UnionKind::All
839 } else {
840 UnionKind::Distinct
841 };
842 let peer = self.parse_bare_select()?;
843 head.unions.push((kind, peer));
844 }
845 head.order_by = if matches!(self.peek(), Token::Order) {
846 self.advance();
847 if !matches!(self.peek(), Token::By) {
848 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
849 }
850 self.advance();
851 let mut keys = Vec::new();
854 loop {
855 let expr = self.parse_expr(0)?;
856 let desc = if matches!(self.peek(), Token::Desc) {
857 self.advance();
858 true
859 } else if matches!(self.peek(), Token::Asc) {
860 self.advance();
861 false
862 } else {
863 false
864 };
865 keys.push(OrderBy { expr, desc });
866 if matches!(self.peek(), Token::Comma) {
867 self.advance();
868 } else {
869 break;
870 }
871 }
872 keys
873 } else {
874 Vec::new()
875 };
876 head.limit = if matches!(self.peek(), Token::Limit) {
877 self.advance();
878 let n = self.expect_u32_literal("LIMIT")?;
879 Some(n)
880 } else {
881 None
882 };
883 head.offset = if matches!(self.peek(), Token::Offset) {
884 self.advance();
885 let n = self.expect_u32_literal("OFFSET")?;
886 Some(n)
887 } else {
888 None
889 };
890 Ok(Statement::Select(head))
891 }
892
893 fn expect_u32_literal(&mut self, label: &str) -> Result<u32, ParseError> {
894 match self.advance() {
895 Token::Integer(n) if n >= 0 => u32::try_from(n).map_err(|_| ParseError {
896 message: format!("{label} value too large: {n}"),
897 token_pos: self.pos.saturating_sub(1),
898 }),
899 other => Err(ParseError {
900 message: format!("expected non-negative integer after {label}, got {other:?}"),
901 token_pos: self.pos.saturating_sub(1),
902 }),
903 }
904 }
905
906 fn parse_bare_select(&mut self) -> Result<SelectStatement, ParseError> {
911 if !matches!(self.peek(), Token::Select) {
912 return Err(self.err(format!(
913 "expected SELECT to start a query block, got {:?}",
914 self.peek()
915 )));
916 }
917 self.advance();
918 let distinct = if matches!(self.peek(), Token::Distinct) {
919 self.advance();
920 true
921 } else {
922 false
923 };
924 let items = self.parse_select_list()?;
925 let from = if matches!(self.peek(), Token::From) {
926 self.advance();
927 Some(self.parse_from_clause()?)
928 } else {
929 None
930 };
931 let where_ = if matches!(self.peek(), Token::Where) {
932 self.advance();
933 Some(self.parse_expr(0)?)
934 } else {
935 None
936 };
937 let mut group_by_all = false;
938 let group_by = if matches!(self.peek(), Token::Group) {
939 self.advance();
940 if !matches!(self.peek(), Token::By) {
941 return Err(self.err(format!("expected BY after GROUP, got {:?}", self.peek())));
942 }
943 self.advance();
944 if matches!(self.peek(), Token::All) {
947 self.advance();
948 group_by_all = true;
949 None
950 } else {
951 let mut groups = Vec::new();
952 loop {
953 groups.push(self.parse_expr(0)?);
954 if matches!(self.peek(), Token::Comma) {
955 self.advance();
956 } else {
957 break;
958 }
959 }
960 Some(groups)
961 }
962 } else {
963 None
964 };
965 let having = if matches!(self.peek(), Token::Having) {
966 self.advance();
967 Some(self.parse_expr(0)?)
968 } else {
969 None
970 };
971 Ok(SelectStatement {
972 ctes: Vec::new(),
973 distinct,
974 items,
975 from,
976 where_,
977 group_by,
978 group_by_all,
979 having,
980 unions: Vec::new(),
981 order_by: Vec::new(),
982 limit: None,
983 offset: None,
984 })
985 }
986
987 fn parse_create_table_stmt_after_create(&mut self) -> Result<Statement, ParseError> {
988 debug_assert!(matches!(self.peek(), Token::Table));
990 self.advance();
991 let if_not_exists = self.consume_if_not_exists();
992 let name = self.expect_ident_like()?;
993 if !matches!(self.peek(), Token::LParen) {
994 return Err(self.err(format!(
995 "expected '(' after table name, got {:?}",
996 self.peek()
997 )));
998 }
999 self.advance();
1000 let mut columns = Vec::new();
1001 let mut foreign_keys: Vec<ForeignKeyConstraint> = Vec::new();
1002 loop {
1003 if self.peek_constraint_or_fk_start() {
1008 foreign_keys.push(self.parse_table_level_fk()?);
1009 } else {
1010 let (col, col_level_fk) = self.parse_column_def_with_fk()?;
1011 columns.push(col);
1012 if let Some(fk) = col_level_fk {
1013 foreign_keys.push(fk);
1014 }
1015 }
1016 match self.peek() {
1017 Token::Comma => {
1018 self.advance();
1019 }
1020 Token::RParen => {
1021 self.advance();
1022 break;
1023 }
1024 other => {
1025 return Err(
1026 self.err(format!("expected ',' or ')' in column list, got {other:?}"))
1027 );
1028 }
1029 }
1030 }
1031 if columns.is_empty() {
1032 return Err(self.err("CREATE TABLE requires at least one column".into()));
1033 }
1034 Ok(Statement::CreateTable(CreateTableStatement {
1035 name,
1036 columns,
1037 if_not_exists,
1038 foreign_keys,
1039 }))
1040 }
1041
1042 fn peek_constraint_or_fk_start(&self) -> bool {
1047 let is_constraint_kw = matches!(
1048 self.peek(),
1049 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
1050 );
1051 let is_foreign_kw = matches!(
1052 self.peek(),
1053 Token::Ident(s) if s.eq_ignore_ascii_case("foreign")
1054 );
1055 is_constraint_kw || is_foreign_kw
1056 }
1057
1058 fn parse_table_level_fk(&mut self) -> Result<ForeignKeyConstraint, ParseError> {
1062 let mut name: Option<String> = None;
1063 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
1064 self.advance();
1065 name = Some(self.expect_ident_like()?);
1066 }
1067 match self.advance() {
1069 Token::Ident(s) if s.eq_ignore_ascii_case("foreign") => {}
1070 other => return Err(self.err(format!("expected FOREIGN, got {other:?}"))),
1071 }
1072 match self.advance() {
1074 Token::Ident(s) if s.eq_ignore_ascii_case("key") => {}
1075 other => return Err(self.err(format!("expected KEY after FOREIGN, got {other:?}"))),
1076 }
1077 if !matches!(self.peek(), Token::LParen) {
1079 return Err(self.err(format!("expected '(' after FOREIGN KEY, got {:?}", self.peek())));
1080 }
1081 self.advance();
1082 let mut columns = Vec::new();
1083 loop {
1084 columns.push(self.expect_ident_like()?);
1085 match self.peek() {
1086 Token::Comma => {
1087 self.advance();
1088 }
1089 Token::RParen => {
1090 self.advance();
1091 break;
1092 }
1093 other => return Err(self.err(format!("expected ',' or ')' in FK column list, got {other:?}"))),
1094 }
1095 }
1096 if columns.is_empty() {
1097 return Err(self.err("FOREIGN KEY requires at least one column".into()));
1098 }
1099 let (parent_table, parent_columns, on_delete, on_update) =
1100 self.parse_references_tail(columns.len())?;
1101 Ok(ForeignKeyConstraint {
1102 name,
1103 columns,
1104 parent_table,
1105 parent_columns,
1106 on_delete,
1107 on_update,
1108 })
1109 }
1110
1111 fn parse_references_tail(
1116 &mut self,
1117 expected_arity: usize,
1118 ) -> Result<(String, Vec<String>, FkAction, FkAction), ParseError> {
1119 match self.advance() {
1120 Token::Ident(s) if s.eq_ignore_ascii_case("references") => {}
1121 other => return Err(self.err(format!("expected REFERENCES, got {other:?}"))),
1122 }
1123 let parent_table = self.expect_ident_like()?;
1124 let mut parent_columns: Vec<String> = Vec::new();
1125 if matches!(self.peek(), Token::LParen) {
1126 self.advance();
1127 loop {
1128 parent_columns.push(self.expect_ident_like()?);
1129 match self.peek() {
1130 Token::Comma => {
1131 self.advance();
1132 }
1133 Token::RParen => {
1134 self.advance();
1135 break;
1136 }
1137 other => return Err(self.err(format!("expected ',' or ')' in REFERENCES column list, got {other:?}"))),
1138 }
1139 }
1140 }
1141 if !parent_columns.is_empty() && parent_columns.len() != expected_arity {
1142 return Err(self.err(format!(
1143 "FK arity mismatch: {} local column(s) vs {} parent column(s)",
1144 expected_arity,
1145 parent_columns.len()
1146 )));
1147 }
1148 loop {
1154 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("deferrable")) {
1155 return Err(self.err(
1156 "DEFERRABLE constraints are not supported (SPG is single-writer; \
1157 constraints are always evaluated immediately at commit)"
1158 .into(),
1159 ));
1160 }
1161 if matches!(self.peek(), Token::Not) {
1162 let look = self.tokens.get(self.pos + 1);
1163 if matches!(look, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("deferrable")) {
1164 self.advance();
1167 self.advance();
1168 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("initially"))
1170 {
1171 self.advance();
1172 match self.advance() {
1173 Token::Ident(s) if s.eq_ignore_ascii_case("immediate") => {}
1174 other => {
1175 return Err(self.err(format!(
1176 "expected IMMEDIATE after INITIALLY for NOT DEFERRABLE, \
1177 got {other:?}"
1178 )));
1179 }
1180 }
1181 }
1182 continue;
1183 }
1184 break;
1185 }
1186 break;
1187 }
1188 let mut on_delete = FkAction::Restrict;
1191 let mut on_update = FkAction::Restrict;
1192 let mut seen_on_delete = false;
1193 let mut seen_on_update = false;
1194 loop {
1195 if !matches!(self.peek(), Token::On) {
1196 break;
1197 }
1198 self.advance();
1199 let which = self.advance();
1200 let action = self.parse_fk_action()?;
1201 match which {
1202 Token::Ident(ref s) if s.eq_ignore_ascii_case("delete") => {
1203 if seen_on_delete {
1204 return Err(self.err("ON DELETE specified twice".into()));
1205 }
1206 seen_on_delete = true;
1207 on_delete = action;
1208 }
1209 Token::Ident(ref s) if s.eq_ignore_ascii_case("update") => {
1210 if seen_on_update {
1211 return Err(self.err("ON UPDATE specified twice".into()));
1212 }
1213 seen_on_update = true;
1214 on_update = action;
1215 }
1216 other => {
1217 return Err(self.err(format!(
1218 "expected DELETE or UPDATE after ON, got {other:?}"
1219 )));
1220 }
1221 }
1222 }
1223 Ok((parent_table, parent_columns, on_delete, on_update))
1224 }
1225
1226 fn parse_fk_action(&mut self) -> Result<FkAction, ParseError> {
1229 match self.advance() {
1230 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => Ok(FkAction::Cascade),
1231 Token::Ident(s) if s.eq_ignore_ascii_case("restrict") => Ok(FkAction::Restrict),
1232 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
1233 match self.advance() {
1234 Token::Null => Ok(FkAction::SetNull),
1235 Token::Default => Ok(FkAction::SetDefault),
1236 other => Err(self.err(format!(
1237 "expected NULL or DEFAULT after SET in FK action, got {other:?}"
1238 ))),
1239 }
1240 }
1241 Token::Ident(s) if s.eq_ignore_ascii_case("no") => {
1242 match self.advance() {
1243 Token::Ident(s) if s.eq_ignore_ascii_case("action") => Ok(FkAction::NoAction),
1244 other => Err(self.err(format!(
1245 "expected ACTION after NO in FK action, got {other:?}"
1246 ))),
1247 }
1248 }
1249 other => Err(self.err(format!(
1250 "expected CASCADE | RESTRICT | SET NULL | SET DEFAULT | NO ACTION, got {other:?}"
1251 ))),
1252 }
1253 }
1254
1255 fn consume_if_not_exists(&mut self) -> bool {
1258 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
1262 if !looks_like_if {
1263 return false;
1264 }
1265 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Not)) {
1268 return false;
1269 }
1270 if !matches!(
1271 self.tokens.get(self.pos + 2),
1272 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
1273 ) {
1274 return false;
1275 }
1276 self.advance(); self.advance(); self.advance(); true
1280 }
1281
1282 fn parse_create_index_stmt_after_create(&mut self) -> Result<Statement, ParseError> {
1283 debug_assert!(matches!(self.peek(), Token::Index));
1285 self.advance();
1286 let if_not_exists = self.consume_if_not_exists();
1287 let name = self.expect_ident_like()?;
1288 if !matches!(self.peek(), Token::On) {
1289 return Err(self.err(format!(
1290 "expected ON after CREATE INDEX <name>, got {:?}",
1291 self.peek()
1292 )));
1293 }
1294 self.advance();
1295 let table = self.expect_ident_like()?;
1296 let method = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
1301 self.advance();
1302 let m = self.expect_ident_like()?;
1303 match m.to_ascii_lowercase().as_str() {
1304 "hnsw" => IndexMethod::Hnsw,
1305 "btree" => IndexMethod::BTree,
1306 "brin" => IndexMethod::Brin,
1307 other => {
1308 return Err(self.err(alloc::format!(
1309 "unknown index method {other:?}; supported: hnsw, btree, brin"
1310 )));
1311 }
1312 }
1313 } else {
1314 IndexMethod::BTree
1315 };
1316 if !matches!(self.peek(), Token::LParen) {
1317 return Err(self.err(format!(
1318 "expected '(' before indexed column, got {:?}",
1319 self.peek()
1320 )));
1321 }
1322 self.advance();
1323 let (column, expression): (String, Option<Expr>) = match self.peek().clone() {
1332 Token::Ident(s) | Token::QuotedIdent(s)
1333 if matches!(self.tokens.get(self.pos + 1), Some(Token::RParen)) =>
1334 {
1335 self.advance();
1336 (s, None)
1337 }
1338 Token::Ident(_) | Token::QuotedIdent(_) => {
1339 let key_expr = self.parse_expr(0)?;
1340 let primary = extract_first_column(&key_expr).ok_or_else(|| {
1341 self.err(
1342 "expression index key must reference at least one column".into(),
1343 )
1344 })?;
1345 (primary, Some(key_expr))
1346 }
1347 other => {
1348 return Err(self.err(format!(
1349 "expected column ident or expression, got {other:?}"
1350 )));
1351 }
1352 };
1353 if !matches!(self.peek(), Token::RParen) {
1354 return Err(self.err(format!(
1355 "expected ')' after indexed column / expression, got {:?}",
1356 self.peek()
1357 )));
1358 }
1359 self.advance();
1360 let included_columns =
1364 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("include")) {
1365 self.advance();
1366 if !matches!(self.peek(), Token::LParen) {
1367 return Err(self.err(format!(
1368 "expected '(' after INCLUDE, got {:?}",
1369 self.peek()
1370 )));
1371 }
1372 self.advance();
1373 let mut cols = Vec::new();
1374 loop {
1375 cols.push(self.expect_ident_like()?);
1376 match self.peek() {
1377 Token::Comma => {
1378 self.advance();
1379 }
1380 Token::RParen => {
1381 self.advance();
1382 break;
1383 }
1384 other => {
1385 return Err(self.err(format!(
1386 "expected ',' or ')' in INCLUDE list, got {other:?}"
1387 )));
1388 }
1389 }
1390 }
1391 cols
1392 } else {
1393 Vec::new()
1394 };
1395 let partial_predicate = if matches!(self.peek(), Token::Where) {
1397 self.advance();
1398 Some(self.parse_expr(0)?)
1399 } else {
1400 None
1401 };
1402 Ok(Statement::CreateIndex(CreateIndexStatement {
1403 name,
1404 table,
1405 column,
1406 method,
1407 if_not_exists,
1408 included_columns,
1409 partial_predicate,
1410 expression,
1411 }))
1412 }
1413
1414 fn parse_column_def_with_fk(
1419 &mut self,
1420 ) -> Result<(ColumnDef, Option<ForeignKeyConstraint>), ParseError> {
1421 let col = self.parse_column_def()?;
1422 let inline_references = matches!(
1424 self.peek(),
1425 Token::Ident(s) if s.eq_ignore_ascii_case("references")
1426 );
1427 if !inline_references {
1428 return Ok((col, None));
1429 }
1430 let (parent_table, parent_columns, on_delete, on_update) =
1431 self.parse_references_tail(1)?;
1432 let fk = ForeignKeyConstraint {
1433 name: None,
1434 columns: vec![col.name.clone()],
1435 parent_table,
1436 parent_columns,
1437 on_delete,
1438 on_update,
1439 };
1440 Ok((col, Some(fk)))
1441 }
1442
1443 fn parse_column_def(&mut self) -> Result<ColumnDef, ParseError> {
1444 let name = self.expect_ident_like()?;
1445 let ty_ident = match self.advance() {
1448 Token::Ident(s) => s,
1449 other => {
1450 return Err(ParseError {
1451 message: format!("expected column type, got {other:?}"),
1452 token_pos: self.pos.saturating_sub(1),
1453 });
1454 }
1455 };
1456 let mut implied_auto_increment = false;
1463 let mut implied_not_null = false;
1464 let ty = match ty_ident.as_str() {
1465 "smallserial" | "serial2" => {
1467 implied_auto_increment = true;
1468 implied_not_null = true;
1469 ColumnTypeName::SmallInt
1470 }
1471 "serial" | "serial4" => {
1472 implied_auto_increment = true;
1473 implied_not_null = true;
1474 ColumnTypeName::Int
1475 }
1476 "bigserial" | "serial8" => {
1477 implied_auto_increment = true;
1478 implied_not_null = true;
1479 ColumnTypeName::BigInt
1480 }
1481 "smallint" | "tinyint" => ColumnTypeName::SmallInt,
1487 "int" | "integer" | "mediumint" => ColumnTypeName::Int,
1489 "bigint" => ColumnTypeName::BigInt,
1490 "float" | "double" | "real" => ColumnTypeName::Float,
1492 "text" => ColumnTypeName::Text,
1493 "bool" | "boolean" => ColumnTypeName::Bool,
1494 "varchar" => ColumnTypeName::Varchar(self.parse_paren_size("VARCHAR")?),
1495 "char" => ColumnTypeName::Char(self.parse_paren_size("CHAR")?),
1496 "vector" => {
1497 let dim = self.parse_paren_size("VECTOR")?;
1498 let encoding = self.parse_optional_vector_encoding()?;
1499 ColumnTypeName::Vector { dim, encoding }
1500 }
1501 "numeric" => {
1502 let (precision, scale) = self.parse_optional_numeric_params()?;
1503 ColumnTypeName::Numeric(precision, scale)
1504 }
1505 "date" => ColumnTypeName::Date,
1506 "timestamp" | "datetime" => ColumnTypeName::Timestamp,
1509 "timestamptz" => ColumnTypeName::Timestamptz,
1513 "json" => ColumnTypeName::Json,
1518 "jsonb" => ColumnTypeName::Jsonb,
1519 other => {
1520 return Err(ParseError {
1521 message: format!("unsupported column type {other:?}"),
1522 token_pos: self.pos.saturating_sub(1),
1523 });
1524 }
1525 };
1526 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unsigned")) {
1531 self.advance();
1532 }
1533 let mut default: Option<Expr> = None;
1537 let mut nullable = !implied_not_null;
1538 let mut nullability_seen = implied_not_null;
1539 let mut auto_increment = implied_auto_increment;
1540 loop {
1541 if matches!(self.peek(), Token::Default) {
1542 if default.is_some() {
1543 return Err(self.err("DEFAULT specified twice".into()));
1544 }
1545 self.advance();
1546 default = Some(self.parse_expr(0)?);
1547 continue;
1548 }
1549 if matches!(self.peek(), Token::Not) {
1550 if nullability_seen {
1551 return Err(self.err("NOT NULL specified twice".into()));
1552 }
1553 self.advance();
1554 if !matches!(self.peek(), Token::Null) {
1555 return Err(self.err(format!(
1556 "expected NULL after NOT in column def, got {:?}",
1557 self.peek()
1558 )));
1559 }
1560 self.advance();
1561 nullable = false;
1562 nullability_seen = true;
1563 continue;
1564 }
1565 if let Token::Ident(s) = self.peek()
1568 && (s.eq_ignore_ascii_case("auto_increment")
1569 || s.eq_ignore_ascii_case("autoincrement"))
1570 {
1571 if auto_increment {
1572 return Err(self.err("AUTO_INCREMENT specified twice".into()));
1573 }
1574 self.advance();
1575 auto_increment = true;
1576 continue;
1577 }
1578 break;
1579 }
1580 Ok(ColumnDef {
1581 name,
1582 ty,
1583 nullable,
1584 default,
1585 auto_increment,
1586 })
1587 }
1588
1589 fn parse_optional_numeric_params(&mut self) -> Result<(u8, u8), ParseError> {
1593 if !matches!(self.peek(), Token::LParen) {
1594 return Ok((0, 0));
1598 }
1599 self.advance();
1600 let precision = match self.advance() {
1601 Token::Integer(n) if (1..=38).contains(&n) => u8::try_from(n).expect("range-checked"),
1602 other => {
1603 return Err(ParseError {
1604 message: format!(
1605 "NUMERIC precision must be an integer in 1..=38, got {other:?}"
1606 ),
1607 token_pos: self.pos.saturating_sub(1),
1608 });
1609 }
1610 };
1611 let scale = if matches!(self.peek(), Token::Comma) {
1612 self.advance();
1613 match self.advance() {
1614 Token::Integer(n) if (0..=i64::from(precision)).contains(&n) => {
1615 u8::try_from(n).expect("range-checked")
1616 }
1617 other => {
1618 return Err(ParseError {
1619 message: format!(
1620 "NUMERIC scale must be a non-negative integer ≤ precision, got {other:?}"
1621 ),
1622 token_pos: self.pos.saturating_sub(1),
1623 });
1624 }
1625 }
1626 } else {
1627 0
1628 };
1629 if !matches!(self.peek(), Token::RParen) {
1630 return Err(self.err(format!(
1631 "expected ')' to close NUMERIC params, got {:?}",
1632 self.peek()
1633 )));
1634 }
1635 self.advance();
1636 Ok((precision, scale))
1637 }
1638
1639 fn parse_optional_vector_encoding(&mut self) -> Result<VecEncoding, ParseError> {
1647 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
1648 return Ok(VecEncoding::F32);
1649 }
1650 self.advance();
1651 let enc_ident = match self.advance() {
1652 Token::Ident(s) => s,
1653 other => {
1654 return Err(self.err(format!(
1655 "expected vector encoding after USING, got {other:?}"
1656 )));
1657 }
1658 };
1659 match enc_ident.to_ascii_lowercase().as_str() {
1660 "sq8" => Ok(VecEncoding::Sq8),
1661 "half" => Ok(VecEncoding::F16),
1664 other => Err(self.err(format!(
1665 "unknown vector encoding {other:?}; supported: SQ8, HALF"
1666 ))),
1667 }
1668 }
1669
1670 fn parse_paren_size(&mut self, label: &str) -> Result<u32, ParseError> {
1671 if !matches!(self.peek(), Token::LParen) {
1672 return Err(self.err(format!("{label} type requires (N), got {:?}", self.peek())));
1673 }
1674 self.advance();
1675 let n = match self.advance() {
1676 Token::Integer(n) if n > 0 => u32::try_from(n).map_err(|_| ParseError {
1677 message: format!("{label} size too large: {n}"),
1678 token_pos: self.pos.saturating_sub(1),
1679 })?,
1680 other => {
1681 return Err(ParseError {
1682 message: format!("expected positive integer {label} size, got {other:?}"),
1683 token_pos: self.pos.saturating_sub(1),
1684 });
1685 }
1686 };
1687 if !matches!(self.peek(), Token::RParen) {
1688 return Err(self.err(format!(
1689 "expected ')' after {label} size, got {:?}",
1690 self.peek()
1691 )));
1692 }
1693 self.advance();
1694 Ok(n)
1695 }
1696
1697 fn parse_insert_stmt(&mut self) -> Result<Statement, ParseError> {
1698 debug_assert!(matches!(self.peek(), Token::Insert));
1699 self.advance();
1700 if !matches!(self.peek(), Token::Into) {
1701 return Err(self.err(format!("expected INTO after INSERT, got {:?}", self.peek())));
1702 }
1703 self.advance();
1704 let table = self.expect_ident_like()?;
1705 let columns = if matches!(self.peek(), Token::LParen) {
1707 self.advance();
1708 let mut names = Vec::new();
1709 loop {
1710 names.push(self.expect_ident_like()?);
1711 match self.peek() {
1712 Token::Comma => {
1713 self.advance();
1714 }
1715 Token::RParen => {
1716 self.advance();
1717 break;
1718 }
1719 other => {
1720 return Err(self.err(format!(
1721 "expected ',' or ')' in INSERT column list, got {other:?}"
1722 )));
1723 }
1724 }
1725 }
1726 Some(names)
1727 } else {
1728 None
1729 };
1730 if !matches!(self.peek(), Token::Values) {
1731 return Err(self.err(format!(
1732 "expected VALUES after table name, got {:?}",
1733 self.peek()
1734 )));
1735 }
1736 self.advance();
1737 if !matches!(self.peek(), Token::LParen) {
1738 return Err(self.err(format!("expected '(' after VALUES, got {:?}", self.peek())));
1739 }
1740 let mut rows = Vec::new();
1741 loop {
1742 if !matches!(self.peek(), Token::LParen) {
1744 return Err(self.err(format!(
1745 "expected '(' for next VALUES tuple, got {:?}",
1746 self.peek()
1747 )));
1748 }
1749 self.advance();
1750 let mut tuple = Vec::new();
1751 loop {
1752 tuple.push(self.parse_expr(0)?);
1753 match self.peek() {
1754 Token::Comma => {
1755 self.advance();
1756 }
1757 Token::RParen => {
1758 self.advance();
1759 break;
1760 }
1761 other => {
1762 return Err(self.err(format!(
1763 "expected ',' or ')' in VALUES tuple, got {other:?}"
1764 )));
1765 }
1766 }
1767 }
1768 if tuple.is_empty() {
1769 return Err(self.err("INSERT VALUES tuple requires at least one value".into()));
1770 }
1771 rows.push(tuple);
1772 if matches!(self.peek(), Token::Comma) {
1774 self.advance();
1775 } else {
1776 break;
1777 }
1778 }
1779 let on_conflict = self.parse_optional_on_conflict()?;
1780 let returning = self.parse_optional_returning()?;
1781 Ok(Statement::Insert(InsertStatement {
1782 table,
1783 columns,
1784 rows,
1785 on_conflict,
1786 returning,
1787 }))
1788 }
1789
1790 fn parse_optional_on_conflict(
1795 &mut self,
1796 ) -> Result<Option<crate::ast::OnConflictClause>, ParseError> {
1797 if !matches!(self.peek(), Token::On) {
1798 return Ok(None);
1799 }
1800 let next_is_conflict = matches!(
1803 self.tokens.get(self.pos + 1),
1804 Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("conflict")
1805 );
1806 if !next_is_conflict {
1807 return Ok(None);
1808 }
1809 self.advance(); self.advance(); let mut target_columns: Vec<String> = Vec::new();
1813 if matches!(self.peek(), Token::LParen) {
1814 self.advance();
1815 loop {
1816 target_columns.push(self.expect_ident_like()?);
1817 match self.peek() {
1818 Token::Comma => {
1819 self.advance();
1820 }
1821 Token::RParen => {
1822 self.advance();
1823 break;
1824 }
1825 other => {
1826 return Err(self.err(alloc::format!(
1827 "expected ',' or ')' in ON CONFLICT target list, got {other:?}"
1828 )));
1829 }
1830 }
1831 }
1832 }
1833 match self.advance() {
1835 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {}
1836 other => {
1837 return Err(self.err(alloc::format!(
1838 "expected DO after ON CONFLICT [(…)], got {other:?}"
1839 )));
1840 }
1841 }
1842 let action = match self.advance() {
1844 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing") => {
1845 crate::ast::OnConflictAction::Nothing
1846 }
1847 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
1848 self.parse_on_conflict_update_action()?
1849 }
1850 other => {
1851 return Err(self.err(alloc::format!(
1852 "expected NOTHING or UPDATE after ON CONFLICT DO, got {other:?}"
1853 )));
1854 }
1855 };
1856 Ok(Some(crate::ast::OnConflictClause {
1857 target_columns,
1858 action,
1859 }))
1860 }
1861
1862 fn parse_on_conflict_update_action(
1866 &mut self,
1867 ) -> Result<crate::ast::OnConflictAction, ParseError> {
1868 match self.advance() {
1870 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {}
1871 other => {
1872 return Err(self.err(alloc::format!(
1873 "expected SET after ON CONFLICT DO UPDATE, got {other:?}"
1874 )));
1875 }
1876 }
1877 let mut assignments: Vec<(String, Expr)> = Vec::new();
1878 loop {
1879 let col = self.expect_ident_like()?;
1880 if !matches!(self.peek(), Token::Eq) {
1881 return Err(self.err(alloc::format!(
1882 "expected `=` after column in ON CONFLICT DO UPDATE SET, got {:?}",
1883 self.peek()
1884 )));
1885 }
1886 self.advance();
1887 let value = self.parse_expr(0)?;
1888 assignments.push((col, value));
1889 if matches!(self.peek(), Token::Comma) {
1890 self.advance();
1891 continue;
1892 }
1893 break;
1894 }
1895 let where_ = if matches!(self.peek(), Token::Where) {
1896 self.advance();
1897 Some(self.parse_expr(0)?)
1898 } else {
1899 None
1900 };
1901 Ok(crate::ast::OnConflictAction::Update {
1902 assignments,
1903 where_,
1904 })
1905 }
1906
1907 fn parse_select_list(&mut self) -> Result<Vec<SelectItem>, ParseError> {
1908 let mut items = Vec::new();
1909 loop {
1910 items.push(self.parse_select_item()?);
1911 if matches!(self.peek(), Token::Comma) {
1912 self.advance();
1913 } else {
1914 break;
1915 }
1916 }
1917 Ok(items)
1918 }
1919
1920 fn parse_select_item(&mut self) -> Result<SelectItem, ParseError> {
1921 if matches!(self.peek(), Token::Star) {
1922 self.advance();
1923 return Ok(SelectItem::Wildcard);
1924 }
1925 let expr = self.parse_expr(0)?;
1926 let alias = self.parse_optional_alias();
1927 Ok(SelectItem::Expr { expr, alias })
1928 }
1929
1930 fn parse_table_ref(&mut self) -> Result<TableRef, ParseError> {
1931 let name = self.expect_ident_like()?;
1932 let as_of_segment = if matches!(self.peek(), Token::As)
1938 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("of"))
1939 {
1940 self.advance(); self.advance(); let kw = match self.peek().clone() {
1943 Token::Ident(s) | Token::QuotedIdent(s) => s,
1944 other => {
1945 return Err(self.err(format!(
1946 "expected SEGMENT after AS OF, got {other:?}"
1947 )));
1948 }
1949 };
1950 if !kw.eq_ignore_ascii_case("segment") {
1951 return Err(self.err(format!(
1952 "expected SEGMENT after AS OF, got {kw:?}; v6.10.2 supports SEGMENT only"
1953 )));
1954 }
1955 self.advance();
1956 let id = match self.advance() {
1959 Token::String(s) => s
1960 .parse::<u32>()
1961 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
1962 Token::Integer(n) => u32::try_from(n).map_err(|e| {
1963 self.err(format!("AS OF SEGMENT id parse: {e}"))
1964 })?,
1965 other => {
1966 return Err(self.err(format!(
1967 "expected segment id literal after AS OF SEGMENT, got {other:?}"
1968 )));
1969 }
1970 };
1971 Some(id)
1972 } else {
1973 None
1974 };
1975 let alias = self.parse_optional_alias();
1976 Ok(TableRef {
1977 name,
1978 alias,
1979 as_of_segment,
1980 })
1981 }
1982
1983 fn parse_from_clause(&mut self) -> Result<FromClause, ParseError> {
1988 let primary = self.parse_table_ref()?;
1989 let mut joins = Vec::new();
1990 loop {
1991 if matches!(self.peek(), Token::Comma) {
1993 self.advance();
1994 let table = self.parse_table_ref()?;
1995 joins.push(FromJoin {
1996 kind: JoinKind::Cross,
1997 table,
1998 on: None,
1999 });
2000 continue;
2001 }
2002 let kind =
2005 match self.peek() {
2006 Token::Inner => {
2007 self.advance();
2008 if !matches!(self.peek(), Token::Join) {
2009 return Err(self
2010 .err(format!("expected JOIN after INNER, got {:?}", self.peek())));
2011 }
2012 self.advance();
2013 JoinKind::Inner
2014 }
2015 Token::Left => {
2016 self.advance();
2017 if matches!(self.peek(), Token::Outer) {
2018 self.advance();
2019 }
2020 if !matches!(self.peek(), Token::Join) {
2021 return Err(self.err(format!(
2022 "expected JOIN after LEFT [OUTER], got {:?}",
2023 self.peek()
2024 )));
2025 }
2026 self.advance();
2027 JoinKind::Left
2028 }
2029 Token::Cross => {
2030 self.advance();
2031 if !matches!(self.peek(), Token::Join) {
2032 return Err(self
2033 .err(format!("expected JOIN after CROSS, got {:?}", self.peek())));
2034 }
2035 self.advance();
2036 JoinKind::Cross
2037 }
2038 Token::Join => {
2039 self.advance();
2040 JoinKind::Inner
2041 }
2042 _ => break,
2043 };
2044 let table = self.parse_table_ref()?;
2045 let on = if matches!(self.peek(), Token::On) {
2046 self.advance();
2047 Some(self.parse_expr(0)?)
2048 } else if kind == JoinKind::Cross {
2049 None
2050 } else {
2051 return Err(self.err(format!(
2052 "expected ON after {:?} JOIN, got {:?}",
2053 kind,
2054 self.peek()
2055 )));
2056 };
2057 joins.push(FromJoin { kind, table, on });
2058 }
2059 Ok(FromClause { primary, joins })
2060 }
2061
2062 fn parse_optional_alias(&mut self) -> Option<String> {
2067 if matches!(self.peek(), Token::As) {
2068 self.advance();
2069 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
2074 return self.expect_ident_like().ok();
2075 }
2076 return None;
2077 }
2078 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
2079 return self.expect_ident_like().ok();
2080 }
2081 None
2082 }
2083
2084 fn parse_expr(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
2086 let mut lhs = self.parse_unary()?;
2087 while let Some((op, prec)) = binop_from(self.peek()) {
2088 if prec < min_prec {
2089 break;
2090 }
2091 self.advance();
2092 let rhs = self.parse_expr(prec + 1)?;
2093 lhs = Expr::Binary {
2094 lhs: Box::new(lhs),
2095 op,
2096 rhs: Box::new(rhs),
2097 };
2098 }
2099 Ok(lhs)
2100 }
2101
2102 fn parse_unary(&mut self) -> Result<Expr, ParseError> {
2103 match self.peek() {
2104 Token::Not => {
2105 self.advance();
2106 let e = self.parse_expr(3)?;
2109 Ok(Expr::Unary {
2110 op: UnOp::Not,
2111 expr: Box::new(e),
2112 })
2113 }
2114 Token::Minus => {
2115 self.advance();
2116 let e = self.parse_expr(8)?;
2119 Ok(Expr::Unary {
2120 op: UnOp::Neg,
2121 expr: Box::new(e),
2122 })
2123 }
2124 _ => self.parse_atom(),
2125 }
2126 }
2127
2128 fn parse_atom(&mut self) -> Result<Expr, ParseError> {
2129 let tok_pos = self.pos;
2130 match self.advance() {
2131 Token::Integer(n) => Ok(Expr::Literal(Literal::Integer(n))),
2132 Token::Float(x) => Ok(Expr::Literal(Literal::Float(x))),
2133 Token::String(s) => Ok(Expr::Literal(Literal::String(s))),
2134 Token::True => Ok(Expr::Literal(Literal::Bool(true))),
2135 Token::False => Ok(Expr::Literal(Literal::Bool(false))),
2136 Token::Null => Ok(Expr::Literal(Literal::Null)),
2137 Token::Placeholder(n) => Ok(Expr::Placeholder(n)),
2141 Token::LParen => {
2142 if matches!(self.peek(), Token::Select) {
2146 let inner = self.parse_select_stmt()?;
2147 match self.advance() {
2148 Token::RParen => {
2149 let Statement::Select(s) = inner else {
2150 unreachable!("parse_select_stmt returns Select")
2151 };
2152 Ok(Expr::ScalarSubquery(Box::new(s)))
2153 }
2154 other => Err(ParseError {
2155 message: format!("expected ')' after scalar subquery, got {other:?}"),
2156 token_pos: self.pos.saturating_sub(1),
2157 }),
2158 }
2159 } else {
2160 let e = self.parse_expr(0)?;
2161 match self.advance() {
2162 Token::RParen => Ok(e),
2163 other => Err(ParseError {
2164 message: format!("expected ')', got {other:?}"),
2165 token_pos: self.pos.saturating_sub(1),
2166 }),
2167 }
2168 }
2169 }
2170 Token::LBracket => self.parse_vector_literal_body(),
2171 Token::Extract => self.parse_extract_atom(),
2172 Token::Interval => self.parse_interval_atom(),
2173 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists") => {
2178 self.parse_exists_atom(false)
2179 }
2180 Token::Ident(s) | Token::QuotedIdent(s) => self.finish_ident_atom(s),
2181 other => Err(ParseError {
2182 message: format!("unexpected token {other:?} in expression"),
2183 token_pos: tok_pos,
2184 }),
2185 }
2186 .and_then(|atom| self.finish_postfix_casts(atom))
2188 }
2189
2190 fn finish_postfix_casts(&mut self, mut expr: Expr) -> Result<Expr, ParseError> {
2193 loop {
2194 if matches!(self.peek(), Token::DoubleColon) {
2195 self.advance();
2196 let target = match self.advance() {
2197 Token::Ident(s) => match s.as_str() {
2198 "int" => CastTarget::Int,
2199 "bigint" => CastTarget::BigInt,
2200 "float" => CastTarget::Float,
2201 "text" => CastTarget::Text,
2202 "bool" => CastTarget::Bool,
2203 "vector" => CastTarget::Vector,
2204 "date" => CastTarget::Date,
2205 "timestamp" | "datetime" => CastTarget::Timestamp,
2206 other => {
2207 return Err(ParseError {
2208 message: format!("unsupported cast target `::{other}`"),
2209 token_pos: self.pos.saturating_sub(1),
2210 });
2211 }
2212 },
2213 other => {
2214 return Err(ParseError {
2215 message: format!("expected type ident after `::`, got {other:?}"),
2216 token_pos: self.pos.saturating_sub(1),
2217 });
2218 }
2219 };
2220 expr = Expr::Cast {
2221 expr: Box::new(expr),
2222 target,
2223 };
2224 continue;
2225 }
2226 if matches!(self.peek(), Token::Is) {
2227 self.advance();
2228 let negated = if matches!(self.peek(), Token::Not) {
2229 self.advance();
2230 true
2231 } else {
2232 false
2233 };
2234 if !matches!(self.peek(), Token::Null) {
2235 return Err(self.err(format!(
2236 "expected NULL after IS{}, got {:?}",
2237 if negated { " NOT" } else { "" },
2238 self.peek()
2239 )));
2240 }
2241 self.advance();
2242 expr = Expr::IsNull {
2243 expr: Box::new(expr),
2244 negated,
2245 };
2246 continue;
2247 }
2248 let negated = if matches!(self.peek(), Token::Not) {
2252 let next = self.tokens.get(self.pos + 1);
2253 matches!(next, Some(Token::Between | Token::In | Token::Like))
2254 } else {
2255 false
2256 };
2257 if negated {
2258 self.advance();
2259 }
2260 if matches!(self.peek(), Token::Between) {
2261 expr = self.parse_between_tail(expr, negated)?;
2262 continue;
2263 }
2264 if matches!(self.peek(), Token::In) {
2265 expr = self.parse_in_tail(expr, negated)?;
2266 continue;
2267 }
2268 if matches!(self.peek(), Token::Like) {
2269 self.advance();
2270 let pattern = self.parse_expr(5)?;
2273 expr = Expr::Like {
2274 expr: Box::new(expr),
2275 pattern: Box::new(pattern),
2276 negated,
2277 };
2278 continue;
2279 }
2280 return Ok(expr);
2281 }
2282 }
2283
2284 fn parse_between_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
2288 self.advance(); let low = self.parse_expr(5)?;
2290 if !matches!(self.peek(), Token::And) {
2291 return Err(self.err(format!(
2292 "expected AND after BETWEEN low bound, got {:?}",
2293 self.peek()
2294 )));
2295 }
2296 self.advance();
2297 let high = self.parse_expr(5)?;
2298 let target = Box::new(expr);
2299 let combined = Expr::Binary {
2300 lhs: Box::new(Expr::Binary {
2301 lhs: target.clone(),
2302 op: BinOp::GtEq,
2303 rhs: Box::new(low),
2304 }),
2305 op: BinOp::And,
2306 rhs: Box::new(Expr::Binary {
2307 lhs: target,
2308 op: BinOp::LtEq,
2309 rhs: Box::new(high),
2310 }),
2311 };
2312 Ok(maybe_not(combined, negated))
2313 }
2314
2315 fn parse_with_cte_then_select(&mut self) -> Result<Statement, ParseError> {
2320 let mut recursive = false;
2325 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
2326 && s.eq_ignore_ascii_case("recursive")
2327 {
2328 self.advance();
2329 recursive = true;
2330 }
2331 let mut ctes = Vec::new();
2332 loop {
2333 let name = self.expect_ident_like()?;
2334 let column_overrides: Vec<String> = if matches!(self.peek(), Token::LParen) {
2338 self.advance();
2339 let mut names = Vec::new();
2340 loop {
2341 names.push(self.expect_ident_like()?);
2342 if matches!(self.peek(), Token::Comma) {
2343 self.advance();
2344 continue;
2345 }
2346 break;
2347 }
2348 if !matches!(self.peek(), Token::RParen) {
2349 return Err(self.err(format!(
2350 "expected ')' to close CTE column list, got {:?}",
2351 self.peek()
2352 )));
2353 }
2354 self.advance();
2355 names
2356 } else {
2357 Vec::new()
2358 };
2359 if !matches!(self.peek(), Token::As) {
2363 return Err(self.err(format!(
2364 "expected AS after CTE name {name:?}, got {:?}",
2365 self.peek()
2366 )));
2367 }
2368 self.advance();
2369 if !matches!(self.peek(), Token::LParen) {
2370 return Err(self.err(format!(
2371 "expected '(' after AS in WITH clause, got {:?}",
2372 self.peek()
2373 )));
2374 }
2375 self.advance();
2376 if !matches!(self.peek(), Token::Select) {
2377 return Err(self.err(format!("WITH body must be a SELECT, got {:?}", self.peek())));
2378 }
2379 let inner = self.parse_select_stmt()?;
2380 if !matches!(self.peek(), Token::RParen) {
2381 return Err(self.err(format!(
2382 "expected ')' after CTE body, got {:?}",
2383 self.peek()
2384 )));
2385 }
2386 self.advance();
2387 let Statement::Select(body) = inner else {
2388 unreachable!("parse_select_stmt returns Select")
2389 };
2390 ctes.push(crate::ast::Cte {
2391 name,
2392 body,
2393 recursive,
2394 column_overrides,
2395 });
2396 if matches!(self.peek(), Token::Comma) {
2397 self.advance();
2398 continue;
2399 }
2400 break;
2401 }
2402 if !matches!(self.peek(), Token::Select) {
2404 return Err(self.err(format!(
2405 "expected SELECT after WITH clause, got {:?}",
2406 self.peek()
2407 )));
2408 }
2409 let body_stmt = self.parse_select_stmt()?;
2410 let Statement::Select(mut body) = body_stmt else {
2411 unreachable!()
2412 };
2413 body.ctes = ctes;
2414 Ok(Statement::Select(body))
2415 }
2416
2417 fn parse_exists_atom(&mut self, negated: bool) -> Result<Expr, ParseError> {
2421 if !matches!(self.peek(), Token::LParen) {
2422 return Err(self.err(format!("expected '(' after EXISTS, got {:?}", self.peek())));
2423 }
2424 self.advance();
2425 let inner = self.parse_select_stmt()?;
2426 if !matches!(self.peek(), Token::RParen) {
2427 return Err(self.err(format!(
2428 "expected ')' after EXISTS-subquery, got {:?}",
2429 self.peek()
2430 )));
2431 }
2432 self.advance();
2433 let Statement::Select(s) = inner else {
2434 unreachable!("parse_select_stmt returns Select")
2435 };
2436 Ok(Expr::Exists {
2437 subquery: Box::new(s),
2438 negated,
2439 })
2440 }
2441
2442 fn parse_in_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
2443 self.advance(); if !matches!(self.peek(), Token::LParen) {
2445 return Err(self.err(format!("expected '(' after IN, got {:?}", self.peek())));
2446 }
2447 self.advance();
2448 if matches!(self.peek(), Token::Select) {
2450 let inner = self.parse_select_stmt()?;
2451 if !matches!(self.peek(), Token::RParen) {
2452 return Err(self.err(format!(
2453 "expected ')' after IN-subquery, got {:?}",
2454 self.peek()
2455 )));
2456 }
2457 self.advance();
2458 let Statement::Select(s) = inner else {
2459 unreachable!("parse_select_stmt always returns Statement::Select")
2460 };
2461 return Ok(Expr::InSubquery {
2462 expr: Box::new(expr),
2463 subquery: Box::new(s),
2464 negated,
2465 });
2466 }
2467 let mut elements = Vec::new();
2468 if !matches!(self.peek(), Token::RParen) {
2469 loop {
2470 elements.push(self.parse_expr(0)?);
2471 match self.peek() {
2472 Token::Comma => {
2473 self.advance();
2474 }
2475 Token::RParen => break,
2476 other => {
2477 return Err(
2478 self.err(format!("expected ',' or ')' in IN list, got {other:?}"))
2479 );
2480 }
2481 }
2482 }
2483 }
2484 self.advance(); let target = Box::new(expr);
2486 let combined = if elements.is_empty() {
2487 Expr::Literal(Literal::Bool(false))
2488 } else {
2489 let mut iter = elements.into_iter();
2490 let first = iter.next().unwrap();
2491 let mut acc = Expr::Binary {
2492 lhs: target.clone(),
2493 op: BinOp::Eq,
2494 rhs: Box::new(first),
2495 };
2496 for elt in iter {
2497 acc = Expr::Binary {
2498 lhs: Box::new(acc),
2499 op: BinOp::Or,
2500 rhs: Box::new(Expr::Binary {
2501 lhs: target.clone(),
2502 op: BinOp::Eq,
2503 rhs: Box::new(elt),
2504 }),
2505 };
2506 }
2507 acc
2508 };
2509 Ok(maybe_not(combined, negated))
2510 }
2511
2512 fn parse_extract_atom(&mut self) -> Result<Expr, ParseError> {
2520 if !matches!(self.peek(), Token::LParen) {
2521 return Err(self.err(format!("expected '(' after EXTRACT, got {:?}", self.peek())));
2522 }
2523 self.advance();
2524 let field_name = self.expect_ident_like()?;
2525 let field = match field_name.to_ascii_lowercase().as_str() {
2526 "year" => ExtractField::Year,
2527 "month" => ExtractField::Month,
2528 "day" => ExtractField::Day,
2529 "hour" => ExtractField::Hour,
2530 "minute" => ExtractField::Minute,
2531 "second" => ExtractField::Second,
2532 "microsecond" | "microseconds" => ExtractField::Microsecond,
2533 other => {
2534 return Err(self.err(format!(
2535 "unknown EXTRACT field {other:?}; \
2536 supported: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MICROSECOND"
2537 )));
2538 }
2539 };
2540 if !matches!(self.peek(), Token::From) {
2541 return Err(self.err(format!(
2542 "expected FROM after EXTRACT field, got {:?}",
2543 self.peek()
2544 )));
2545 }
2546 self.advance();
2547 let source = self.parse_expr(0)?;
2548 if !matches!(self.peek(), Token::RParen) {
2549 return Err(self.err(format!(
2550 "expected ')' to close EXTRACT, got {:?}",
2551 self.peek()
2552 )));
2553 }
2554 self.advance();
2555 Ok(Expr::Extract {
2556 field,
2557 source: Box::new(source),
2558 })
2559 }
2560
2561 fn parse_interval_atom(&mut self) -> Result<Expr, ParseError> {
2566 let tok = self.advance();
2567 let Token::String(text) = tok else {
2568 return Err(self.err(format!(
2569 "expected string literal after INTERVAL, got {tok:?}"
2570 )));
2571 };
2572 let (months, micros) = parse_interval_text(&text).ok_or_else(|| ParseError {
2573 message: format!(
2574 "cannot parse INTERVAL {text:?}; \
2575 expected `<n> <unit> [<n> <unit> ...]` with units \
2576 microsecond[s], millisecond[s], second[s], minute[s], \
2577 hour[s], day[s], week[s], month[s], year[s]"
2578 ),
2579 token_pos: self.pos.saturating_sub(1),
2580 })?;
2581 Ok(Expr::Literal(Literal::Interval {
2582 months,
2583 micros,
2584 text,
2585 }))
2586 }
2587
2588 fn parse_vector_literal_body(&mut self) -> Result<Expr, ParseError> {
2589 let mut elems = Vec::new();
2590 if matches!(self.peek(), Token::RBracket) {
2591 self.advance();
2592 return Ok(Expr::Literal(Literal::Vector(elems)));
2593 }
2594 loop {
2595 let e = self.parse_expr(0)?;
2596 let x = extract_numeric_literal(&e).ok_or_else(|| ParseError {
2597 message: format!("vector element must be a numeric literal, got {e:?}"),
2598 token_pos: self.pos,
2599 })?;
2600 elems.push(x);
2601 match self.peek() {
2602 Token::Comma => {
2603 self.advance();
2604 }
2605 Token::RBracket => {
2606 self.advance();
2607 break;
2608 }
2609 other => {
2610 return Err(self.err(format!("expected ',' or ']' in vector, got {other:?}")));
2611 }
2612 }
2613 }
2614 Ok(Expr::Literal(Literal::Vector(elems)))
2615 }
2616
2617 fn parse_null_treatment_modifier(&mut self) -> NullTreatment {
2626 let Token::Ident(s) = self.peek().clone() else {
2627 return NullTreatment::Respect;
2628 };
2629 let is_ignore = s.eq_ignore_ascii_case("ignore");
2630 let is_respect = s.eq_ignore_ascii_case("respect");
2631 if !is_ignore && !is_respect {
2632 return NullTreatment::Respect;
2633 }
2634 if self.pos + 1 < self.tokens.len()
2637 && let Token::Ident(s2) = &self.tokens[self.pos + 1]
2638 && s2.eq_ignore_ascii_case("nulls")
2639 {
2640 self.advance();
2641 self.advance();
2642 return if is_ignore {
2643 NullTreatment::Ignore
2644 } else {
2645 NullTreatment::Respect
2646 };
2647 }
2648 NullTreatment::Respect
2649 }
2650
2651 #[allow(clippy::type_complexity)] fn parse_over_clause(
2654 &mut self,
2655 ) -> Result<(Vec<Expr>, Vec<(Expr, bool)>, Option<WindowFrame>), ParseError> {
2656 if !matches!(self.peek(), Token::LParen) {
2657 return Err(self.err(format!("expected '(' after OVER, got {:?}", self.peek())));
2658 }
2659 self.advance();
2660 let mut partition_by = Vec::new();
2661 let mut order_by = Vec::new();
2662 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
2664 && s.eq_ignore_ascii_case("partition")
2665 {
2666 self.advance();
2667 if !matches!(self.peek(), Token::By) {
2668 return Err(self.err(format!(
2669 "expected BY after PARTITION, got {:?}",
2670 self.peek()
2671 )));
2672 }
2673 self.advance();
2674 loop {
2675 partition_by.push(self.parse_expr(0)?);
2676 if matches!(self.peek(), Token::Comma) {
2677 self.advance();
2678 continue;
2679 }
2680 break;
2681 }
2682 }
2683 if matches!(self.peek(), Token::Order) {
2685 self.advance();
2686 if !matches!(self.peek(), Token::By) {
2687 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
2688 }
2689 self.advance();
2690 loop {
2691 let e = self.parse_expr(0)?;
2692 let desc = if matches!(self.peek(), Token::Desc) {
2693 self.advance();
2694 true
2695 } else if matches!(self.peek(), Token::Asc) {
2696 self.advance();
2697 false
2698 } else {
2699 false
2700 };
2701 order_by.push((e, desc));
2702 if matches!(self.peek(), Token::Comma) {
2703 self.advance();
2704 continue;
2705 }
2706 break;
2707 }
2708 }
2709 let mut frame: Option<WindowFrame> = None;
2713 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
2714 let kind = if s.eq_ignore_ascii_case("rows") {
2715 Some(FrameKind::Rows)
2716 } else if s.eq_ignore_ascii_case("range") {
2717 Some(FrameKind::Range)
2718 } else {
2719 None
2720 };
2721 if let Some(kind) = kind {
2722 self.advance();
2723 frame = Some(self.parse_frame_tail(kind)?);
2724 }
2725 }
2726 if !matches!(self.peek(), Token::RParen) {
2727 return Err(self.err(format!(
2728 "expected ')' to close OVER clause, got {:?}",
2729 self.peek()
2730 )));
2731 }
2732 self.advance();
2733 Ok((partition_by, order_by, frame))
2734 }
2735
2736 fn parse_frame_tail(&mut self, kind: FrameKind) -> Result<WindowFrame, ParseError> {
2742 if matches!(self.peek(), Token::Between) {
2743 self.advance();
2744 let start = self.parse_frame_bound()?;
2745 if !matches!(self.peek(), Token::And) {
2746 return Err(self.err(format!("expected AND in frame spec, got {:?}", self.peek())));
2747 }
2748 self.advance();
2749 let end = self.parse_frame_bound()?;
2750 Ok(WindowFrame {
2751 kind,
2752 start,
2753 end: Some(end),
2754 })
2755 } else {
2756 let start = self.parse_frame_bound()?;
2757 Ok(WindowFrame {
2758 kind,
2759 start,
2760 end: None,
2761 })
2762 }
2763 }
2764
2765 fn parse_frame_bound(&mut self) -> Result<FrameBound, ParseError> {
2768 if let Token::Integer(n) = *self.peek() {
2770 self.advance();
2771 let n: u64 = u64::try_from(n).map_err(|_| {
2772 self.err(format!(
2773 "invalid frame offset {n} — expected non-negative integer"
2774 ))
2775 })?;
2776 let dir = self.expect_ident_like()?;
2777 return if dir.eq_ignore_ascii_case("preceding") {
2778 Ok(FrameBound::OffsetPreceding(n))
2779 } else if dir.eq_ignore_ascii_case("following") {
2780 Ok(FrameBound::OffsetFollowing(n))
2781 } else {
2782 Err(self.err(format!(
2783 "expected PRECEDING or FOLLOWING after offset, got {dir:?}"
2784 )))
2785 };
2786 }
2787 let first = self.expect_ident_like()?;
2788 if first.eq_ignore_ascii_case("unbounded") {
2789 let dir = self.expect_ident_like()?;
2790 return if dir.eq_ignore_ascii_case("preceding") {
2791 Ok(FrameBound::UnboundedPreceding)
2792 } else if dir.eq_ignore_ascii_case("following") {
2793 Ok(FrameBound::UnboundedFollowing)
2794 } else {
2795 Err(self.err(format!(
2796 "expected PRECEDING or FOLLOWING after UNBOUNDED, got {dir:?}"
2797 )))
2798 };
2799 }
2800 if first.eq_ignore_ascii_case("current") {
2801 let row = self.expect_ident_like()?;
2802 if !row.eq_ignore_ascii_case("row") {
2803 return Err(self.err(format!("expected ROW after CURRENT, got {row:?}")));
2804 }
2805 return Ok(FrameBound::CurrentRow);
2806 }
2807 Err(self.err(format!(
2808 "expected frame bound (UNBOUNDED/CURRENT/<n>), got {first:?}"
2809 )))
2810 }
2811
2812 fn finish_ident_atom(&mut self, first: String) -> Result<Expr, ParseError> {
2813 if matches!(self.peek(), Token::Dot) {
2814 self.advance();
2815 let name = self.expect_ident_like()?;
2816 return Ok(Expr::Column(ColumnName {
2817 qualifier: Some(first),
2818 name,
2819 }));
2820 }
2821 if matches!(self.peek(), Token::LParen) {
2822 self.advance();
2823 if first.eq_ignore_ascii_case("count") && matches!(self.peek(), Token::Star) {
2827 self.advance();
2828 if !matches!(self.peek(), Token::RParen) {
2829 return Err(self.err(format!(
2830 "expected ')' after COUNT(*), got {:?}",
2831 self.peek()
2832 )));
2833 }
2834 self.advance();
2835 let null_treatment = self.parse_null_treatment_modifier();
2837 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
2838 && s.eq_ignore_ascii_case("over")
2839 {
2840 self.advance();
2841 let (partition_by, order_by, frame) = self.parse_over_clause()?;
2842 return Ok(Expr::WindowFunction {
2843 name: "count_star".into(),
2844 args: Vec::new(),
2845 partition_by,
2846 order_by,
2847 frame,
2848 null_treatment,
2849 });
2850 }
2851 return Ok(Expr::FunctionCall {
2852 name: "count_star".into(),
2853 args: Vec::new(),
2854 });
2855 }
2856 let mut args = Vec::new();
2858 if !matches!(self.peek(), Token::RParen) {
2859 loop {
2860 args.push(self.parse_expr(0)?);
2861 match self.peek() {
2862 Token::Comma => {
2863 self.advance();
2864 }
2865 Token::RParen => break,
2866 other => {
2867 return Err(self.err(format!(
2868 "expected ',' or ')' in function args, got {other:?}"
2869 )));
2870 }
2871 }
2872 }
2873 }
2874 self.advance(); let null_treatment = self.parse_null_treatment_modifier();
2882 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
2883 && s.eq_ignore_ascii_case("over")
2884 {
2885 self.advance();
2886 let (partition_by, order_by, frame) = self.parse_over_clause()?;
2887 return Ok(Expr::WindowFunction {
2888 name: first,
2889 args,
2890 partition_by,
2891 order_by,
2892 frame,
2893 null_treatment,
2894 });
2895 }
2896 return Ok(Expr::FunctionCall { name: first, args });
2897 }
2898 Ok(Expr::Column(ColumnName {
2899 qualifier: None,
2900 name: first,
2901 }))
2902 }
2903}
2904
2905fn extract_first_column(expr: &Expr) -> Option<String> {
2913 match expr {
2914 Expr::Column(cn) => Some(cn.name.clone()),
2915 Expr::FunctionCall { args, .. } => args.iter().find_map(extract_first_column),
2916 Expr::Binary { lhs, rhs, .. } => {
2917 extract_first_column(lhs).or_else(|| extract_first_column(rhs))
2918 }
2919 Expr::Unary { expr: e, .. } => extract_first_column(e),
2920 _ => None,
2921 }
2922}
2923
2924fn maybe_not(expr: Expr, negated: bool) -> Expr {
2925 if negated {
2926 Expr::Unary {
2927 op: UnOp::Not,
2928 expr: Box::new(expr),
2929 }
2930 } else {
2931 expr
2932 }
2933}
2934
2935fn binop_from(tok: &Token) -> Option<(BinOp, u8)> {
2936 let pair = match tok {
2937 Token::Or => (BinOp::Or, 1),
2938 Token::And => (BinOp::And, 2),
2939 Token::Eq => (BinOp::Eq, 4),
2940 Token::NotEq => (BinOp::NotEq, 4),
2941 Token::Lt => (BinOp::Lt, 4),
2942 Token::LtEq => (BinOp::LtEq, 4),
2943 Token::Gt => (BinOp::Gt, 4),
2944 Token::GtEq => (BinOp::GtEq, 4),
2945 Token::L2Distance => (BinOp::L2Distance, 5),
2948 Token::InnerProduct => (BinOp::InnerProduct, 5),
2949 Token::CosineDistance => (BinOp::CosineDistance, 5),
2950 Token::Plus => (BinOp::Add, 6),
2951 Token::Minus => (BinOp::Sub, 6),
2952 Token::Concat => (BinOp::Concat, 6),
2955 Token::Star => (BinOp::Mul, 7),
2956 Token::Slash => (BinOp::Div, 7),
2957 Token::JsonGet => (BinOp::JsonGet, 7),
2961 Token::JsonGetText => (BinOp::JsonGetText, 7),
2962 Token::JsonGetPath => (BinOp::JsonGetPath, 7),
2963 Token::JsonGetPathText => (BinOp::JsonGetPathText, 7),
2964 Token::JsonContains => (BinOp::JsonContains, 7),
2965 _ => return None,
2966 };
2967 Some(pair)
2968}
2969
2970#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
2971fn extract_numeric_literal(e: &Expr) -> Option<f32> {
2976 match e {
2977 Expr::Literal(Literal::Integer(n)) => Some(*n as f32),
2978 Expr::Literal(Literal::Float(x)) => Some(*x as f32),
2979 Expr::Unary {
2980 op: UnOp::Neg,
2981 expr,
2982 } => extract_numeric_literal(expr).map(|x| -x),
2983 _ => None,
2984 }
2985}
2986
2987pub fn parse_interval_text(s: &str) -> Option<(i32, i64)> {
2995 let parts: Vec<&str> = s.split_whitespace().collect();
2996 if parts.is_empty() || !parts.len().is_multiple_of(2) {
2997 return None;
2998 }
2999 let mut months: i32 = 0;
3000 let mut micros: i64 = 0;
3001 let mut i = 0;
3002 while i < parts.len() {
3003 let n: i64 = parts[i].parse().ok()?;
3004 let unit = parts[i + 1].to_ascii_lowercase();
3005 let unit_stripped = unit.strip_suffix('s').unwrap_or(&unit);
3006 match unit_stripped {
3007 "microsecond" => micros = micros.checked_add(n)?,
3008 "millisecond" => micros = micros.checked_add(n.checked_mul(1_000)?)?,
3009 "second" => micros = micros.checked_add(n.checked_mul(1_000_000)?)?,
3010 "minute" => micros = micros.checked_add(n.checked_mul(60_000_000)?)?,
3011 "hour" => micros = micros.checked_add(n.checked_mul(3_600_000_000)?)?,
3012 "day" => micros = micros.checked_add(n.checked_mul(86_400_000_000)?)?,
3013 "week" => micros = micros.checked_add(n.checked_mul(604_800_000_000)?)?,
3014 "month" => {
3015 let n32 = i32::try_from(n).ok()?;
3016 months = months.checked_add(n32)?;
3017 }
3018 "year" => {
3019 let n32 = i32::try_from(n).ok()?;
3020 months = months.checked_add(n32.checked_mul(12)?)?;
3021 }
3022 _ => return None,
3023 }
3024 i += 2;
3025 }
3026 Some((months, micros))
3027}
3028
3029#[cfg(test)]
3030mod tests {
3031 use super::*;
3032 use alloc::string::ToString;
3033
3034 fn parse(s: &str) -> Statement {
3035 parse_statement(s).expect("parse ok")
3036 }
3037
3038 fn lit_int(n: i64) -> Expr {
3039 Expr::Literal(Literal::Integer(n))
3040 }
3041
3042 fn col(name: &str) -> Expr {
3043 Expr::Column(ColumnName {
3044 qualifier: None,
3045 name: name.into(),
3046 })
3047 }
3048
3049 #[test]
3050 fn select_single_integer() {
3051 let s = parse("SELECT 1");
3052 let Statement::Select(s) = s else {
3053 panic!("expected SELECT")
3054 };
3055 assert_eq!(s.items.len(), 1);
3056 assert!(s.from.is_none());
3057 assert!(s.where_.is_none());
3058 }
3059
3060 #[test]
3061 fn select_multiple_literal_kinds() {
3062 let s = parse("SELECT 1, 'hi', NULL, TRUE, 1.5");
3063 let Statement::Select(s) = s else {
3064 panic!("expected SELECT")
3065 };
3066 assert_eq!(s.items.len(), 5);
3067 }
3068
3069 #[test]
3070 fn select_wildcard_from_table() {
3071 let s = parse("SELECT * FROM users");
3072 let Statement::Select(s) = s else {
3073 panic!("expected SELECT")
3074 };
3075 assert!(matches!(s.items[..], [SelectItem::Wildcard]));
3076 assert_eq!(s.from.as_ref().unwrap().primary.name, "users");
3077 }
3078
3079 #[test]
3080 fn select_with_table_alias() {
3081 let s = parse("SELECT * FROM users AS u");
3082 let Statement::Select(s) = s else {
3083 panic!("expected SELECT")
3084 };
3085 let t = &s.from.as_ref().unwrap().primary;
3086 assert_eq!(t.name, "users");
3087 assert_eq!(t.alias.as_deref(), Some("u"));
3088 }
3089
3090 #[test]
3091 fn select_with_where_eq() {
3092 let s = parse("SELECT a FROM t WHERE a = 1");
3093 let Statement::Select(s) = s else {
3094 panic!("expected SELECT")
3095 };
3096 let w = s.where_.unwrap();
3097 assert_eq!(
3098 w,
3099 Expr::Binary {
3100 lhs: Box::new(col("a")),
3101 op: BinOp::Eq,
3102 rhs: Box::new(lit_int(1)),
3103 }
3104 );
3105 }
3106
3107 #[test]
3108 fn arithmetic_precedence() {
3109 let s = parse("SELECT 1 + 2 * 3");
3110 let Statement::Select(s) = s else {
3111 panic!("expected SELECT")
3112 };
3113 let SelectItem::Expr { expr, .. } = &s.items[0] else {
3114 panic!("wildcard?")
3115 };
3116 assert_eq!(
3117 expr,
3118 &Expr::Binary {
3119 lhs: Box::new(lit_int(1)),
3120 op: BinOp::Add,
3121 rhs: Box::new(Expr::Binary {
3122 lhs: Box::new(lit_int(2)),
3123 op: BinOp::Mul,
3124 rhs: Box::new(lit_int(3)),
3125 }),
3126 }
3127 );
3128 }
3129
3130 #[test]
3131 fn parentheses_override_precedence() {
3132 let s = parse("SELECT (1 + 2) * 3");
3133 let Statement::Select(s) = s else {
3134 panic!("expected SELECT")
3135 };
3136 let SelectItem::Expr { expr, .. } = &s.items[0] else {
3137 panic!()
3138 };
3139 assert_eq!(
3140 expr,
3141 &Expr::Binary {
3142 lhs: Box::new(Expr::Binary {
3143 lhs: Box::new(lit_int(1)),
3144 op: BinOp::Add,
3145 rhs: Box::new(lit_int(2)),
3146 }),
3147 op: BinOp::Mul,
3148 rhs: Box::new(lit_int(3)),
3149 }
3150 );
3151 }
3152
3153 #[test]
3154 fn not_binds_below_comparison() {
3155 let s = parse("SELECT NOT a = 1 FROM t");
3157 let Statement::Select(s) = s else {
3158 panic!("expected SELECT")
3159 };
3160 let SelectItem::Expr { expr, .. } = &s.items[0] else {
3161 panic!()
3162 };
3163 assert_eq!(
3164 expr,
3165 &Expr::Unary {
3166 op: UnOp::Not,
3167 expr: Box::new(Expr::Binary {
3168 lhs: Box::new(col("a")),
3169 op: BinOp::Eq,
3170 rhs: Box::new(lit_int(1)),
3171 }),
3172 }
3173 );
3174 }
3175
3176 #[test]
3177 fn unary_minus_binds_above_multiplication() {
3178 let s = parse("SELECT -a * 2 FROM t");
3180 let Statement::Select(s) = s else {
3181 panic!("expected SELECT")
3182 };
3183 let SelectItem::Expr { expr, .. } = &s.items[0] else {
3184 panic!()
3185 };
3186 assert_eq!(
3187 expr,
3188 &Expr::Binary {
3189 lhs: Box::new(Expr::Unary {
3190 op: UnOp::Neg,
3191 expr: Box::new(col("a")),
3192 }),
3193 op: BinOp::Mul,
3194 rhs: Box::new(lit_int(2)),
3195 }
3196 );
3197 }
3198
3199 #[test]
3200 fn qualified_column() {
3201 let s = parse("SELECT t.col FROM t");
3202 let Statement::Select(s) = s else {
3203 panic!("expected SELECT")
3204 };
3205 let SelectItem::Expr { expr, .. } = &s.items[0] else {
3206 panic!()
3207 };
3208 assert_eq!(
3209 expr,
3210 &Expr::Column(ColumnName {
3211 qualifier: Some("t".into()),
3212 name: "col".into()
3213 })
3214 );
3215 }
3216
3217 #[test]
3218 fn select_item_alias_with_as() {
3219 let s = parse("SELECT a AS y FROM t");
3220 let Statement::Select(s) = s else {
3221 panic!("expected SELECT")
3222 };
3223 let SelectItem::Expr { alias, .. } = &s.items[0] else {
3224 panic!()
3225 };
3226 assert_eq!(alias.as_deref(), Some("y"));
3227 }
3228
3229 #[test]
3230 fn trailing_semicolon_accepted() {
3231 let s = parse("SELECT 1;");
3232 let Statement::Select(s) = s else {
3233 panic!("expected SELECT")
3234 };
3235 assert_eq!(s.items.len(), 1);
3236 }
3237
3238 #[test]
3239 fn boolean_chain_with_and_or_not() {
3240 let s = parse("SELECT NOT a OR b AND NOT c FROM t");
3242 let Statement::Select(s) = s else {
3243 panic!("expected SELECT")
3244 };
3245 let SelectItem::Expr { expr, .. } = &s.items[0] else {
3246 panic!()
3247 };
3248 let expected = Expr::Binary {
3249 lhs: Box::new(Expr::Unary {
3250 op: UnOp::Not,
3251 expr: Box::new(col("a")),
3252 }),
3253 op: BinOp::Or,
3254 rhs: Box::new(Expr::Binary {
3255 lhs: Box::new(col("b")),
3256 op: BinOp::And,
3257 rhs: Box::new(Expr::Unary {
3258 op: UnOp::Not,
3259 expr: Box::new(col("c")),
3260 }),
3261 }),
3262 };
3263 assert_eq!(expr, &expected);
3264 }
3265
3266 #[test]
3267 fn empty_input_errors() {
3268 let err = parse_statement("").unwrap_err();
3269 assert!(err.message.contains("SELECT"));
3270 }
3271
3272 #[test]
3273 fn unmatched_paren_errors() {
3274 assert!(parse_statement("SELECT (1 + 2").is_err());
3275 }
3276
3277 #[test]
3278 fn display_round_trip_simple_select() {
3279 let original = parse("SELECT a + 1 FROM t WHERE a > 0");
3280 let text = original.to_string();
3281 let again = parse_statement(&text).expect("re-parse");
3282 assert_eq!(original, again);
3283 }
3284
3285 #[test]
3288 fn create_table_single_column() {
3289 let s = parse("CREATE TABLE foo (a INT)");
3290 let Statement::CreateTable(c) = s else {
3291 panic!("expected CreateTable")
3292 };
3293 assert_eq!(c.name, "foo");
3294 assert_eq!(c.columns.len(), 1);
3295 assert_eq!(c.columns[0].name, "a");
3296 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
3297 assert!(c.columns[0].nullable);
3298 }
3299
3300 #[test]
3301 fn create_table_multi_column_with_not_null_mix() {
3302 let s = parse("CREATE TABLE u (id INT NOT NULL, name TEXT, score FLOAT NOT NULL, ok BOOL)");
3303 let Statement::CreateTable(c) = s else {
3304 panic!()
3305 };
3306 assert_eq!(c.columns.len(), 4);
3307 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
3308 assert!(!c.columns[0].nullable);
3309 assert_eq!(c.columns[1].ty, ColumnTypeName::Text);
3310 assert!(c.columns[1].nullable);
3311 assert_eq!(c.columns[2].ty, ColumnTypeName::Float);
3312 assert!(!c.columns[2].nullable);
3313 assert_eq!(c.columns[3].ty, ColumnTypeName::Bool);
3314 }
3315
3316 #[test]
3317 fn create_table_bigint_supported() {
3318 let s = parse("CREATE TABLE accounts (id BIGINT NOT NULL)");
3319 let Statement::CreateTable(c) = s else {
3320 panic!()
3321 };
3322 assert_eq!(c.columns[0].ty, ColumnTypeName::BigInt);
3323 }
3324
3325 #[test]
3326 fn create_table_vector_default_is_f32() {
3327 let s = parse("CREATE TABLE t (v VECTOR(128))");
3328 let Statement::CreateTable(c) = s else {
3329 panic!()
3330 };
3331 assert_eq!(
3332 c.columns[0].ty,
3333 ColumnTypeName::Vector {
3334 dim: 128,
3335 encoding: VecEncoding::F32,
3336 },
3337 );
3338 }
3339
3340 #[test]
3341 fn create_table_vector_using_sq8() {
3342 for sql in [
3345 "CREATE TABLE t (v VECTOR(128) USING SQ8)",
3346 "CREATE TABLE t (v VECTOR(128) using sq8)",
3347 ] {
3348 let s = parse(sql);
3349 let Statement::CreateTable(c) = s else {
3350 panic!()
3351 };
3352 assert_eq!(
3353 c.columns[0].ty,
3354 ColumnTypeName::Vector {
3355 dim: 128,
3356 encoding: VecEncoding::Sq8,
3357 },
3358 "{sql}",
3359 );
3360 }
3361 }
3362
3363 #[test]
3364 fn create_table_vector_using_unknown_errors() {
3365 let err = parse_statement("CREATE TABLE t (v VECTOR(8) USING PQ8)").unwrap_err();
3366 assert!(
3367 err.message.contains("unknown vector encoding"),
3368 "got: {}",
3369 err.message
3370 );
3371 }
3372
3373 #[test]
3374 fn vector_using_sq8_display_roundtrips() {
3375 let s = parse("CREATE TABLE t (v VECTOR(64) USING SQ8)");
3378 let Statement::CreateTable(c) = s else {
3379 panic!()
3380 };
3381 assert_eq!(c.columns[0].ty.to_string(), "VECTOR(64) USING SQ8");
3382 }
3383
3384 #[test]
3385 fn parser_recognises_placeholders() {
3386 use crate::ast::{Expr, SelectItem, Statement};
3387 let s = parse("SELECT $1, $2 + 1 FROM t WHERE x = $3");
3389 let Statement::Select(sel) = s else { panic!() };
3390 assert!(matches!(
3391 sel.items[0],
3392 SelectItem::Expr {
3393 expr: Expr::Placeholder(1),
3394 alias: None
3395 }
3396 ));
3397 let SelectItem::Expr {
3399 expr: Expr::Binary { lhs, rhs, .. },
3400 ..
3401 } = &sel.items[1]
3402 else {
3403 panic!()
3404 };
3405 assert!(matches!(**lhs, Expr::Placeholder(2)));
3406 assert!(matches!(**rhs, Expr::Literal(Literal::Integer(1))));
3407 let Some(Expr::Binary { rhs, .. }) = sel.where_.as_ref() else {
3409 panic!()
3410 };
3411 assert!(matches!(**rhs, Expr::Placeholder(3)));
3412 }
3413
3414 #[test]
3415 fn parser_rejects_dollar_zero() {
3416 assert!(parse_statement("SELECT $0").is_err());
3418 }
3419
3420 #[test]
3421 fn placeholder_display_roundtrips() {
3422 let s = parse("SELECT $42 FROM t");
3425 let printed = s.to_string();
3426 assert!(printed.contains("$42"));
3427 let again = parse(&printed);
3428 assert_eq!(s, again);
3429 }
3430
3431 #[test]
3432 fn alter_index_rebuild_bare() {
3433 use crate::ast::{AlterIndexTarget, Statement};
3434 let s = parse("ALTER INDEX my_idx REBUILD");
3435 let Statement::AlterIndex(a) = s else {
3436 panic!("expected AlterIndex, got {s:?}")
3437 };
3438 assert_eq!(a.name, "my_idx");
3439 assert_eq!(a.target, AlterIndexTarget::Rebuild { encoding: None });
3440 }
3441
3442 #[test]
3443 fn alter_index_rebuild_with_encoding() {
3444 use crate::ast::{AlterIndexTarget, Statement};
3445 for (sql, want) in [
3446 (
3447 "ALTER INDEX my_idx REBUILD WITH (encoding = F32)",
3448 VecEncoding::F32,
3449 ),
3450 (
3451 "ALTER INDEX my_idx REBUILD WITH (encoding = sq8)",
3452 VecEncoding::Sq8,
3453 ),
3454 (
3455 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
3456 VecEncoding::F16,
3457 ),
3458 ] {
3459 let s = parse(sql);
3460 let Statement::AlterIndex(a) = s else {
3461 panic!("{sql}: expected AlterIndex")
3462 };
3463 assert_eq!(a.name, "my_idx");
3464 assert_eq!(
3465 a.target,
3466 AlterIndexTarget::Rebuild {
3467 encoding: Some(want)
3468 },
3469 "{sql}"
3470 );
3471 }
3472 }
3473
3474 #[test]
3475 fn alter_index_rebuild_unknown_encoding_errors() {
3476 let err = parse_statement("ALTER INDEX my_idx REBUILD WITH (encoding = PQ8)").unwrap_err();
3477 assert!(
3478 err.message.contains("unknown vector encoding"),
3479 "got: {}",
3480 err.message
3481 );
3482 }
3483
3484 #[test]
3485 fn alter_index_rebuild_display_roundtrips() {
3486 for (input, want) in [
3487 ("ALTER INDEX my_idx REBUILD", "ALTER INDEX my_idx REBUILD"),
3488 (
3489 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
3490 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
3491 ),
3492 (
3493 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
3494 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
3495 ),
3496 ] {
3497 let s = parse(input);
3498 assert_eq!(s.to_string(), want);
3499 }
3500 }
3501
3502 #[test]
3503 fn create_table_unknown_type_errors() {
3504 let err = parse_statement("CREATE TABLE x (a xml)").unwrap_err();
3507 assert!(err.message.contains("unsupported column type"));
3508 }
3509
3510 #[test]
3511 fn create_table_missing_table_keyword_errors() {
3512 assert!(parse_statement("CREATE x (a INT)").is_err());
3513 }
3514
3515 #[test]
3516 fn insert_single_value() {
3517 let s = parse("INSERT INTO foo VALUES (42)");
3518 let Statement::Insert(i) = s else {
3519 panic!("expected Insert")
3520 };
3521 assert_eq!(i.table, "foo");
3522 assert_eq!(i.rows.len(), 1);
3523 assert_eq!(i.rows[0].len(), 1);
3524 assert!(matches!(i.rows[0][0], Expr::Literal(Literal::Integer(42))));
3525 }
3526
3527 #[test]
3528 fn insert_multi_value_with_mixed_literals() {
3529 let s = parse("INSERT INTO foo VALUES (1, 'hi', 3.14, TRUE, NULL)");
3530 let Statement::Insert(i) = s else { panic!() };
3531 assert_eq!(i.rows.len(), 1);
3532 assert_eq!(i.rows[0].len(), 5);
3533 }
3534
3535 #[test]
3536 fn insert_missing_into_errors() {
3537 assert!(parse_statement("INSERT foo VALUES (1)").is_err());
3538 }
3539
3540 #[test]
3541 fn create_table_round_trip() {
3542 let original =
3543 parse("CREATE TABLE foo (id BIGINT NOT NULL, label TEXT, score FLOAT NOT NULL)");
3544 let text = original.to_string();
3545 let again = parse_statement(&text).expect("re-parse");
3546 assert_eq!(original, again);
3547 }
3548
3549 #[test]
3550 fn insert_round_trip_with_negation_and_string() {
3551 let original = parse("INSERT INTO t VALUES (-1, 'it''s', NULL)");
3552 let text = original.to_string();
3553 let again = parse_statement(&text).expect("re-parse");
3554 assert_eq!(original, again);
3555 }
3556
3557 #[test]
3558 fn unknown_keyword_at_statement_start_errors() {
3559 let err = parse_statement("FROBNICATE foo SET x = 1").unwrap_err();
3562 assert!(err.message.contains("expected SELECT"));
3563 }
3564
3565 #[test]
3568 fn create_index_basic() {
3569 let s = parse("CREATE INDEX idx_id ON users (id)");
3570 let Statement::CreateIndex(c) = s else {
3571 panic!("expected CreateIndex")
3572 };
3573 assert_eq!(c.name, "idx_id");
3574 assert_eq!(c.table, "users");
3575 assert_eq!(c.column, "id");
3576 }
3577
3578 #[test]
3579 fn create_index_missing_on_errors() {
3580 assert!(parse_statement("CREATE INDEX foo users (id)").is_err());
3581 }
3582
3583 #[test]
3584 fn create_index_missing_paren_errors() {
3585 assert!(parse_statement("CREATE INDEX foo ON users id").is_err());
3586 }
3587
3588 #[test]
3589 fn create_index_round_trip() {
3590 let original = parse("CREATE INDEX by_name ON users (name)");
3591 let again = parse_statement(&original.to_string()).unwrap();
3592 assert_eq!(original, again);
3593 }
3594
3595 #[test]
3598 fn begin_commit_rollback_parse_as_unit_variants() {
3599 assert_eq!(parse("BEGIN"), Statement::Begin);
3600 assert_eq!(parse("COMMIT"), Statement::Commit);
3601 assert_eq!(parse("ROLLBACK"), Statement::Rollback);
3602 assert_eq!(parse("BEGIN;"), Statement::Begin);
3604 }
3605
3606 #[test]
3609 fn inner_product_binop_parses() {
3610 let s = parse("SELECT v <#> [1.0, 2.0] FROM t");
3611 let Statement::Select(s) = s else { panic!() };
3612 let SelectItem::Expr { expr, .. } = &s.items[0] else {
3613 panic!()
3614 };
3615 assert!(matches!(
3616 expr,
3617 Expr::Binary {
3618 op: BinOp::InnerProduct,
3619 ..
3620 }
3621 ));
3622 }
3623
3624 #[test]
3625 fn cosine_distance_binop_parses() {
3626 let s = parse("SELECT v <=> [1.0, 2.0] FROM t");
3627 let Statement::Select(s) = s else { panic!() };
3628 let SelectItem::Expr { expr, .. } = &s.items[0] else {
3629 panic!()
3630 };
3631 assert!(matches!(
3632 expr,
3633 Expr::Binary {
3634 op: BinOp::CosineDistance,
3635 ..
3636 }
3637 ));
3638 }
3639
3640 #[test]
3641 fn vector_cast_postfix_wraps_string_literal() {
3642 let s = parse("SELECT '[1,2,3]'::vector FROM t");
3643 let Statement::Select(s) = s else { panic!() };
3644 let SelectItem::Expr { expr, .. } = &s.items[0] else {
3645 panic!()
3646 };
3647 assert!(matches!(
3648 expr,
3649 Expr::Cast {
3650 target: CastTarget::Vector,
3651 ..
3652 }
3653 ));
3654 }
3655
3656 #[test]
3657 fn unsupported_cast_target_errors() {
3658 let err = parse_statement("SELECT 1::numeric FROM t").unwrap_err();
3660 assert!(err.message.contains("unsupported cast target"));
3661 }
3662
3663 #[test]
3664 fn tx_statements_round_trip() {
3665 for q in ["BEGIN", "COMMIT", "ROLLBACK"] {
3666 let original = parse(q);
3667 let again = parse_statement(&original.to_string()).unwrap();
3668 assert_eq!(original, again);
3669 }
3670 }
3671
3672 #[test]
3673 fn interval_text_parsing_units() {
3674 assert_eq!(parse_interval_text("1 day"), Some((0, 86_400_000_000)));
3676 assert_eq!(parse_interval_text("1 second"), Some((0, 1_000_000)));
3677 assert_eq!(parse_interval_text("1 month"), Some((1, 0)));
3678 assert_eq!(parse_interval_text("2 years"), Some((24, 0)));
3679 assert_eq!(parse_interval_text("1 year 6 months"), Some((18, 0)));
3681 assert_eq!(
3682 parse_interval_text("1 day 2 hours"),
3683 Some((0, 86_400_000_000 + 7_200_000_000))
3684 );
3685 assert_eq!(parse_interval_text("-1 day"), Some((0, -86_400_000_000)));
3687 assert_eq!(parse_interval_text(""), None);
3689 assert_eq!(parse_interval_text("garbage"), None);
3690 assert_eq!(parse_interval_text("1 fortnight"), None);
3691 assert_eq!(parse_interval_text("1"), None);
3692 }
3693
3694 #[test]
3695 fn interval_literal_roundtrips_via_display() {
3696 let parsed = parse("SELECT INTERVAL '1 day 2 hours'");
3697 let s = parsed.to_string();
3698 assert!(s.contains("INTERVAL '1 day 2 hours'"), "got: {s}");
3700 let again = parse_statement(&s).unwrap();
3702 assert_eq!(parsed, again);
3703 }
3704
3705 #[test]
3708 fn parser_recognises_create_publication_bare() {
3709 let s = parse("CREATE PUBLICATION pub_a");
3710 let Statement::CreatePublication(p) = s else {
3711 panic!("expected CreatePublication, got {s:?}")
3712 };
3713 assert_eq!(p.name, "pub_a");
3714 assert_eq!(p.scope, PublicationScope::AllTables);
3715 }
3716
3717 #[test]
3718 fn parser_recognises_create_publication_for_all_tables() {
3719 let s = parse("CREATE PUBLICATION pub_a FOR ALL TABLES");
3720 let Statement::CreatePublication(p) = s else {
3721 panic!("expected CreatePublication, got {s:?}")
3722 };
3723 assert_eq!(p.name, "pub_a");
3724 assert_eq!(p.scope, PublicationScope::AllTables);
3725 }
3726
3727 #[test]
3728 fn parser_recognises_drop_publication() {
3729 let s = parse("DROP PUBLICATION pub_a");
3730 let Statement::DropPublication(name) = s else {
3731 panic!("expected DropPublication, got {s:?}")
3732 };
3733 assert_eq!(name, "pub_a");
3734 }
3735
3736 #[test]
3737 fn parser_recognises_for_table_list() {
3738 let s = parse("CREATE PUBLICATION pub_a FOR TABLE t1, t2, t3");
3739 let Statement::CreatePublication(p) = s else {
3740 panic!("expected CreatePublication, got {s:?}")
3741 };
3742 assert_eq!(p.name, "pub_a");
3743 let PublicationScope::ForTables(ts) = p.scope else {
3744 panic!("expected ForTables scope")
3745 };
3746 assert_eq!(ts, alloc::vec!["t1", "t2", "t3"]);
3747 }
3748
3749 #[test]
3750 fn parser_recognises_for_tables_plural() {
3751 let s = parse("CREATE PUBLICATION pub_a FOR TABLES t1, t2");
3753 let Statement::CreatePublication(p) = s else {
3754 panic!("expected CreatePublication, got {s:?}")
3755 };
3756 let PublicationScope::ForTables(ts) = p.scope else {
3757 panic!("expected ForTables")
3758 };
3759 assert_eq!(ts, alloc::vec!["t1", "t2"]);
3760 }
3761
3762 #[test]
3763 fn parser_recognises_for_all_tables_except_list() {
3764 let s = parse("CREATE PUBLICATION p FOR ALL TABLES EXCEPT t1, t2");
3765 let Statement::CreatePublication(p) = s else {
3766 panic!()
3767 };
3768 let PublicationScope::AllTablesExcept(ts) = p.scope else {
3769 panic!("expected AllTablesExcept")
3770 };
3771 assert_eq!(ts, alloc::vec!["t1", "t2"]);
3772 }
3773
3774 #[test]
3775 fn parser_rejects_for_table_with_empty_list() {
3776 let err = parse_statement("CREATE PUBLICATION p FOR TABLE")
3778 .expect_err("must error on empty list");
3779 assert!(!err.message.is_empty());
3782 }
3783
3784 #[test]
3785 fn parser_recognises_show_publications() {
3786 let s = parse("SHOW PUBLICATIONS");
3789 assert!(matches!(s, Statement::ShowPublications));
3790 }
3791
3792 #[test]
3795 fn parser_recognises_create_subscription_single_publication() {
3796 let s = parse("CREATE SUBSCRIPTION sub_a CONNECTION 'host=127.0.0.1 port=20002' PUBLICATION pub_a");
3797 let Statement::CreateSubscription(c) = s else {
3798 panic!("expected CreateSubscription, got {s:?}")
3799 };
3800 assert_eq!(c.name, "sub_a");
3801 assert_eq!(c.conn_str, "host=127.0.0.1 port=20002");
3802 assert_eq!(c.publications, alloc::vec!["pub_a"]);
3803 }
3804
3805 #[test]
3806 fn parser_recognises_create_subscription_multi_publication() {
3807 let s = parse(
3808 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=h' PUBLICATION p1, p2, p3",
3809 );
3810 let Statement::CreateSubscription(c) = s else {
3811 panic!()
3812 };
3813 assert_eq!(c.publications, alloc::vec!["p1", "p2", "p3"]);
3814 }
3815
3816 #[test]
3817 fn parser_rejects_create_subscription_missing_connection() {
3818 let err = parse_statement("CREATE SUBSCRIPTION s PUBLICATION p")
3819 .expect_err("must error on missing CONNECTION");
3820 assert!(err.message.contains("CONNECTION"), "got: {}", err.message);
3821 }
3822
3823 #[test]
3824 fn parser_rejects_create_subscription_missing_publication() {
3825 let err = parse_statement("CREATE SUBSCRIPTION s CONNECTION 'host=x'")
3826 .expect_err("must error on missing PUBLICATION");
3827 assert!(err.message.contains("PUBLICATION"), "got: {}", err.message);
3828 }
3829
3830 #[test]
3831 fn parser_recognises_drop_subscription() {
3832 let s = parse("DROP SUBSCRIPTION sub_a");
3833 let Statement::DropSubscription(name) = s else {
3834 panic!("expected DropSubscription, got {s:?}")
3835 };
3836 assert_eq!(name, "sub_a");
3837 }
3838
3839 #[test]
3840 fn parser_recognises_show_subscriptions() {
3841 let s = parse("SHOW SUBSCRIPTIONS");
3842 assert!(matches!(s, Statement::ShowSubscriptions));
3843 }
3844
3845 #[test]
3846 fn parser_recognises_wait_for_wal_position_no_timeout() {
3847 let s = parse("WAIT FOR WAL POSITION 12345");
3848 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
3849 panic!("expected WaitForWalPosition, got {s:?}")
3850 };
3851 assert_eq!(pos, 12345);
3852 assert!(timeout_ms.is_none());
3853 }
3854
3855 #[test]
3856 fn parser_recognises_wait_for_wal_position_with_timeout() {
3857 let s = parse("WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000");
3858 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
3859 panic!()
3860 };
3861 assert_eq!(pos, 67890);
3862 assert_eq!(timeout_ms, Some(5000));
3863 }
3864
3865 #[test]
3866 fn parser_rejects_wait_with_negative_position() {
3867 let err = parse_statement("WAIT FOR WAL POSITION -1").unwrap_err();
3873 assert!(!err.message.is_empty());
3874 }
3875
3876 #[test]
3877 fn parser_recognises_bare_analyze() {
3878 let s = parse("ANALYZE");
3879 assert!(matches!(s, Statement::Analyze(None)));
3880 }
3881
3882 #[test]
3883 fn parser_recognises_analyze_with_table() {
3884 let s = parse("ANALYZE users");
3885 let Statement::Analyze(Some(name)) = s else {
3886 panic!("expected Analyze, got {s:?}")
3887 };
3888 assert_eq!(name, "users");
3889 }
3890
3891 #[test]
3892 fn parser_recognises_analyze_with_quoted_table() {
3893 let s = parse("ANALYZE \"Mixed Case\"");
3894 let Statement::Analyze(Some(name)) = s else {
3895 panic!()
3896 };
3897 assert_eq!(name, "Mixed Case");
3898 }
3899
3900 #[test]
3901 fn parser_rejects_analyze_with_garbage_token() {
3902 let err = parse_statement("ANALYZE 42").expect_err("must error");
3903 assert!(!err.message.is_empty());
3904 }
3905
3906 #[test]
3907 fn analyze_display_roundtrips() {
3908 for sql in ["ANALYZE", "ANALYZE users"] {
3909 let s = parse(sql);
3910 let printed = s.to_string();
3911 let again = parse_statement(&printed)
3912 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
3913 assert_eq!(s, again);
3914 }
3915 }
3916
3917 #[test]
3918 fn wait_for_display_roundtrips() {
3919 for sql in [
3920 "WAIT FOR WAL POSITION 12345",
3921 "WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000",
3922 ] {
3923 let s = parse(sql);
3924 let printed = s.to_string();
3925 let again = parse_statement(&printed)
3926 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
3927 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
3928 }
3929 }
3930
3931 #[test]
3932 fn subscription_ddl_display_roundtrips() {
3933 for sql in [
3934 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=h port=20002' PUBLICATION pub_a",
3935 "CREATE SUBSCRIPTION sub_b CONNECTION 'host=h' PUBLICATION p1, p2",
3936 "DROP SUBSCRIPTION sub_a",
3937 "SHOW SUBSCRIPTIONS",
3938 ] {
3939 let s = parse(sql);
3940 let printed = s.to_string();
3941 let again = parse_statement(&printed)
3942 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
3943 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
3944 }
3945 }
3946
3947 #[test]
3948 fn parser_drop_dispatches_user_vs_publication() {
3949 let s = parse("DROP USER 'alice'");
3952 let Statement::DropUser(name) = s else {
3953 panic!("expected DropUser, got {s:?}")
3954 };
3955 assert_eq!(name, "alice");
3956 let s = parse("DROP PUBLICATION p1");
3958 assert!(matches!(s, Statement::DropPublication(_)));
3959 }
3960
3961 #[test]
3962 fn publication_ddl_display_roundtrips() {
3963 for sql in [
3966 "CREATE PUBLICATION pub_a",
3967 "CREATE PUBLICATION pub_a FOR ALL TABLES",
3968 "CREATE PUBLICATION pub_a FOR TABLE t1, t2",
3969 "CREATE PUBLICATION pub_a FOR ALL TABLES EXCEPT t1",
3970 "DROP PUBLICATION pub_a",
3971 "SHOW PUBLICATIONS",
3972 ] {
3973 let s = parse(sql);
3974 let printed = s.to_string();
3975 let again = parse_statement(&printed)
3976 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
3977 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
3978 }
3979 }
3980}