1use std::error::Error;
6use std::fmt;
7use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
8
9use fsqlite_ast::{
10 AlterTableAction, AlterTableStatement, Assignment, AssignmentTarget, AttachStatement,
11 BeginStatement, ColumnConstraint, ColumnConstraintKind, ColumnDef, ColumnRef, CompoundOp,
12 ConflictAction, CreateIndexStatement, CreateTableBody, CreateTableStatement,
13 CreateTriggerStatement, CreateViewStatement, CreateVirtualTableStatement, Cte, CteMaterialized,
14 DefaultValue, Deferrable, DeferrableInitially, DeleteStatement, Distinctness, DropObjectType,
15 DropStatement, Expr, ForeignKeyAction, ForeignKeyActionType, ForeignKeyClause,
16 ForeignKeyTrigger, FrameBound, FrameExclude, FrameSpec, FrameType, FromClause,
17 GeneratedStorage, IndexHint, IndexedColumn, InsertSource, InsertStatement, JoinClause,
18 JoinConstraint, JoinKind, JoinType, LimitClause, Literal, NullsOrder, OrderingTerm,
19 PragmaStatement, PragmaValue, QualifiedName, QualifiedTableRef, ResultColumn,
20 RollbackStatement, SelectBody, SelectCore, SelectStatement, SortDirection, Span, Statement,
21 TableConstraint, TableConstraintKind, TableOrSubquery, TimeTravelClause, TimeTravelTarget,
22 TransactionMode, TriggerEvent, TriggerTiming, TypeName, UpdateStatement, UpsertAction,
23 UpsertClause, UpsertTarget, VacuumStatement, WindowDef, WindowSpec, WithClause,
24};
25
26use crate::lexer::Lexer;
27use crate::token::{Token, TokenKind};
28
29static FSQLITE_PARSE_STATEMENTS_TOTAL: AtomicU64 = AtomicU64::new(0);
35static FSQLITE_PARSE_TOKENS_TOTAL: AtomicU64 = AtomicU64::new(0);
37static FSQLITE_PARSE_ERRORS_TOTAL: AtomicU64 = AtomicU64::new(0);
39static FSQLITE_PARSE_METRICS_ENABLED: AtomicBool = AtomicBool::new(false);
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
48pub struct ParseMetricsSnapshot {
49 pub fsqlite_parse_statements_total: u64,
51 pub fsqlite_parse_tokens_total: u64,
53 pub fsqlite_parse_errors_total: u64,
55}
56
57#[must_use]
59pub fn parse_metrics_snapshot() -> ParseMetricsSnapshot {
60 ParseMetricsSnapshot {
61 fsqlite_parse_statements_total: FSQLITE_PARSE_STATEMENTS_TOTAL.load(Ordering::Relaxed),
62 fsqlite_parse_tokens_total: FSQLITE_PARSE_TOKENS_TOTAL.load(Ordering::Relaxed),
63 fsqlite_parse_errors_total: FSQLITE_PARSE_ERRORS_TOTAL.load(Ordering::Relaxed),
64 }
65}
66
67pub fn set_parse_metrics_enabled(enabled: bool) {
69 FSQLITE_PARSE_METRICS_ENABLED.store(enabled, Ordering::Relaxed);
70}
71
72#[must_use]
74pub fn parse_metrics_enabled() -> bool {
75 FSQLITE_PARSE_METRICS_ENABLED.load(Ordering::Relaxed)
76}
77
78pub fn reset_parse_metrics() {
80 FSQLITE_PARSE_STATEMENTS_TOTAL.store(0, Ordering::Relaxed);
81 FSQLITE_PARSE_TOKENS_TOTAL.store(0, Ordering::Relaxed);
82 FSQLITE_PARSE_ERRORS_TOTAL.store(0, Ordering::Relaxed);
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91pub enum ParseErrorKind {
92 Syntax,
94 Limit,
96}
97
98#[derive(Debug, Clone, PartialEq, Eq)]
99pub struct ParseError {
100 pub kind: ParseErrorKind,
102 pub message: String,
103 pub span: Span,
104 pub line: u32,
105 pub col: u32,
106}
107
108impl ParseError {
109 #[must_use]
110 pub(crate) fn at(message: impl Into<String>, token: Option<&Token>) -> Self {
111 if let Some(t) = token {
112 Self {
113 kind: ParseErrorKind::Syntax,
114 message: message.into(),
115 span: t.span,
116 line: t.line,
117 col: t.col,
118 }
119 } else {
120 Self {
121 kind: ParseErrorKind::Syntax,
122 message: message.into(),
123 span: Span::ZERO,
124 line: 0,
125 col: 0,
126 }
127 }
128 }
129
130 #[must_use]
131 pub(crate) fn limit(message: impl Into<String>, token: Option<&Token>) -> Self {
132 let mut error = Self::at(message, token);
133 error.kind = ParseErrorKind::Limit;
134 error
135 }
136}
137
138impl fmt::Display for ParseError {
139 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140 write!(f, "{}:{}: {}", self.line, self.col, self.message)
141 }
142}
143
144impl Error for ParseError {}
145
146#[derive(Debug, Default)]
152pub struct StatementParseScratch {
153 tokens: Vec<Token>,
154 errors: Vec<ParseError>,
155 identifier_interner: crate::lexer::IdentifierInterner,
156}
157
158impl StatementParseScratch {
159 pub fn reset(&mut self) {
161 self.tokens.clear();
162 self.errors.clear();
163 self.identifier_interner.reset();
164 }
165
166 #[must_use]
167 pub fn token_capacity(&self) -> usize {
168 self.tokens.capacity()
169 }
170
171 #[must_use]
172 pub fn error_capacity(&self) -> usize {
173 self.errors.capacity()
174 }
175
176 #[must_use]
177 pub fn retained_bytes(&self) -> usize {
178 self.tokens
179 .capacity()
180 .saturating_mul(std::mem::size_of::<Token>())
181 .saturating_add(
182 self.errors
183 .capacity()
184 .saturating_mul(std::mem::size_of::<ParseError>()),
185 )
186 .saturating_add(self.identifier_interner.retained_bytes())
187 }
188
189 #[cfg(test)]
190 fn identifier_interner_is_empty(&self) -> bool {
191 self.identifier_interner.is_empty()
192 }
193
194 #[cfg(test)]
195 fn identifier_interner_len(&self) -> usize {
196 self.identifier_interner.len()
197 }
198}
199
200pub const MAX_PARSE_DEPTH: u32 = 128;
214
215pub struct Parser {
216 pub(crate) tokens: Vec<Token>,
217 pub(crate) pos: usize,
218 pub(crate) errors: Vec<ParseError>,
219 pub(crate) depth: u32,
220 pub(crate) statement_class: &'static str,
221}
222
223impl Parser {
224 #[must_use]
225 pub fn new(tokens: Vec<Token>) -> Self {
226 Self {
227 tokens,
228 pos: 0,
229 errors: Vec::new(),
230 depth: 0,
231 statement_class: "EXPRESSION",
232 }
233 }
234
235 pub(crate) fn enter_recursion(&mut self) -> Result<(), ParseError> {
236 if self.depth >= MAX_PARSE_DEPTH {
237 return Err(ParseError::limit(
238 format!(
239 "parser recursion limit exceeded \
240 (statement={}, depth={}, limit={MAX_PARSE_DEPTH})",
241 self.statement_class, self.depth
242 ),
243 self.current(),
244 ));
245 }
246 self.depth += 1;
247 Ok(())
248 }
249
250 pub(crate) fn leave_recursion(&mut self) {
251 self.depth = self.depth.saturating_sub(1);
252 }
253
254 pub(crate) fn with_recursion_guard<T>(
255 &mut self,
256 f: impl FnOnce(&mut Self) -> Result<T, ParseError>,
257 ) -> Result<T, ParseError> {
258 self.enter_recursion()?;
259 let result = f(self);
260 self.leave_recursion();
261 result
262 }
263
264 #[must_use]
265 pub fn from_sql(sql: &str) -> Self {
266 Self::new(Lexer::tokenize(sql))
267 }
268
269 pub fn parse_all(&mut self) -> (Vec<Statement>, Vec<ParseError>) {
270 let parse_debug_enabled = tracing::enabled!(target: "fsqlite.parse", tracing::Level::DEBUG);
271 let collect_parse_metrics = parse_metrics_enabled();
272 if collect_parse_metrics {
273 let token_count = u64::try_from(self.tokens.len()).unwrap_or(u64::MAX);
274 FSQLITE_PARSE_TOKENS_TOTAL.fetch_add(token_count, Ordering::Relaxed);
275 }
276 let span = parse_debug_enabled.then(|| {
277 tracing::debug_span!(
278 target: "fsqlite.parse",
279 "parse",
280 ast_node_count = tracing::field::Empty,
281 parse_errors = tracing::field::Empty,
282 )
283 });
284 let _guard = span.as_ref().map(|span| span.enter());
285
286 let mut stmts = Vec::new();
287 while !self.at_eof() {
288 if self.check(&TokenKind::Semicolon) {
289 self.advance();
290 continue;
291 }
292 match self.parse_statement() {
293 Ok(s) => {
294 if collect_parse_metrics {
295 FSQLITE_PARSE_STATEMENTS_TOTAL.fetch_add(1, Ordering::Relaxed);
296 }
297 stmts.push(s);
298 let _ = self.eat(&TokenKind::Semicolon);
299 }
300 Err(e) => {
301 if collect_parse_metrics {
302 FSQLITE_PARSE_ERRORS_TOTAL.fetch_add(1, Ordering::Relaxed);
303 }
304 tracing::warn!(
305 target: "fsqlite.parse",
306 error = %e,
307 "parse recovery: skipping malformed statement"
308 );
309 self.errors.push(e);
310 self.synchronize();
311 }
312 }
313 }
314
315 let errors = std::mem::take(&mut self.errors);
316 if let Some(span) = span.as_ref() {
317 span.record("ast_node_count", stmts.len() as u64);
318 span.record("parse_errors", errors.len() as u64);
319 }
320
321 (stmts, errors)
322 }
323
324 pub fn parse_statement(&mut self) -> Result<Statement, ParseError> {
325 let prior_class = self.statement_class;
326 self.statement_class = self.classify_current_statement();
327 let result = self.parse_statement_inner();
328 self.statement_class = prior_class;
329 result
330 }
331
332 fn classify_current_statement(&self) -> &'static str {
333 match self.peek() {
334 TokenKind::KwSelect | TokenKind::KwValues => "SELECT",
335 TokenKind::KwWith => "WITH",
336 TokenKind::KwInsert | TokenKind::KwReplace => "INSERT",
337 TokenKind::KwUpdate => "UPDATE",
338 TokenKind::KwDelete => "DELETE",
339 TokenKind::KwCreate => {
340 for offset in 1..=4 {
341 match self.peek_nth(offset) {
342 TokenKind::KwTable => return "CREATE TABLE",
343 TokenKind::KwIndex => return "CREATE INDEX",
344 TokenKind::KwView => return "CREATE VIEW",
345 TokenKind::KwTrigger => return "CREATE TRIGGER",
346 TokenKind::KwVirtual => return "CREATE VIRTUAL TABLE",
347 _ => {}
348 }
349 }
350 "CREATE"
351 }
352 TokenKind::KwDrop => "DROP",
353 TokenKind::KwAlter => "ALTER TABLE",
354 TokenKind::KwBegin => "BEGIN",
355 TokenKind::KwCommit | TokenKind::KwEnd => "COMMIT",
356 TokenKind::KwRollback => "ROLLBACK",
357 TokenKind::KwSavepoint => "SAVEPOINT",
358 TokenKind::KwRelease => "RELEASE",
359 TokenKind::KwAttach => "ATTACH",
360 TokenKind::KwDetach => "DETACH",
361 TokenKind::KwPragma => "PRAGMA",
362 TokenKind::KwVacuum => "VACUUM",
363 TokenKind::KwReindex => "REINDEX",
364 TokenKind::KwAnalyze => "ANALYZE",
365 TokenKind::KwExplain => "EXPLAIN",
366 _ => "UNKNOWN",
367 }
368 }
369
370 #[must_use]
371 pub fn errors(&self) -> &[ParseError] {
372 &self.errors
373 }
374
375 pub(crate) fn peek(&self) -> &TokenKind {
380 self.current().map_or(&TokenKind::Eof, |t| &t.kind)
381 }
382
383 pub(crate) fn current(&self) -> Option<&Token> {
384 self.tokens.get(self.pos)
385 }
386
387 pub(crate) fn peek_nth(&self, n: usize) -> &TokenKind {
388 self.tokens
389 .get(self.pos + n)
390 .map_or(&TokenKind::Eof, |t| &t.kind)
391 }
392
393 pub(crate) fn at_eof(&self) -> bool {
394 matches!(self.peek(), TokenKind::Eof)
395 }
396
397 pub(crate) fn advance(&mut self) -> Option<&Token> {
398 let t = self.tokens.get(self.pos);
399 if self.pos < self.tokens.len().saturating_sub(1) {
400 self.pos += 1;
401 }
402 t
403 }
404
405 pub(crate) fn check(&self, kind: &TokenKind) -> bool {
406 std::mem::discriminant(self.peek()) == std::mem::discriminant(kind)
407 }
408
409 pub(crate) fn check_kw(&self, kw: &TokenKind) -> bool {
410 self.peek() == kw
411 }
412
413 pub(crate) fn eat(&mut self, kind: &TokenKind) -> bool {
414 if self.check(kind) {
415 self.advance();
416 true
417 } else {
418 false
419 }
420 }
421
422 pub(crate) fn eat_kw(&mut self, kw: &TokenKind) -> bool {
423 if self.peek() == kw {
424 self.advance();
425 true
426 } else {
427 false
428 }
429 }
430
431 pub(crate) fn expect_kw(&mut self, kw: &TokenKind) -> Result<Span, ParseError> {
432 if self.peek() == kw {
433 let sp = self.current_span();
434 self.advance();
435 Ok(sp)
436 } else {
437 Err(self.err_expected(&format!("{kw:?}")))
438 }
439 }
440
441 pub(crate) fn expect_token(&mut self, kind: &TokenKind) -> Result<Span, ParseError> {
442 if self.check(kind) {
443 let sp = self.current_span();
444 self.advance();
445 Ok(sp)
446 } else {
447 Err(self.err_expected(&format!("{kind:?}")))
448 }
449 }
450
451 pub(crate) fn current_span(&self) -> Span {
452 self.current().map_or(Span::ZERO, |t| t.span)
453 }
454
455 pub(crate) fn err_expected(&self, what: &str) -> ParseError {
456 ParseError::at(format!("expected {what}"), self.current())
457 }
458
459 pub(crate) fn err_msg(&self, msg: impl Into<String>) -> ParseError {
460 ParseError::at(msg, self.current())
461 }
462
463 fn synchronize(&mut self) {
464 loop {
465 match self.peek() {
466 TokenKind::Eof => return,
467 TokenKind::Semicolon => {
468 self.advance();
469 return;
470 }
471 k if k.is_statement_start() => return,
472 _ => {
473 self.advance();
474 }
475 }
476 }
477 }
478
479 fn recover_trigger_body_after_error(&mut self) {
480 let mut case_depth = 0_usize;
481
482 loop {
483 match self.peek() {
484 TokenKind::Eof => return,
485 TokenKind::KwCase => {
486 case_depth = case_depth.saturating_add(1);
487 self.advance();
488 }
489 TokenKind::KwEnd if case_depth > 0 => {
490 case_depth = case_depth.saturating_sub(1);
491 self.advance();
492 }
493 TokenKind::KwEnd => {
498 self.advance();
499 let _ = self.eat(&TokenKind::Semicolon);
500 return;
501 }
502 _ => {
503 self.advance();
504 }
505 }
506 }
507 }
508
509 pub(crate) fn parse_identifier(&mut self) -> Result<String, ParseError> {
514 match self.peek().clone() {
515 TokenKind::Id(s) | TokenKind::QuotedId(s, _) => {
516 self.advance();
517 Ok(s.to_string())
518 }
519 TokenKind::String(s) => {
520 self.advance();
521 Ok(s)
522 }
523 ref k if is_nonreserved_kw(k) => {
524 let s = kw_to_str(k);
525 self.advance();
526 Ok(s)
527 }
528 _ => Err(self.err_expected("identifier")),
529 }
530 }
531
532 pub(crate) fn parse_qualified_name(&mut self) -> Result<QualifiedName, ParseError> {
533 let first = self.parse_identifier()?;
534 if self.eat(&TokenKind::Dot) {
535 let second = self.parse_identifier()?;
536 Ok(QualifiedName::qualified(first, second))
537 } else {
538 Ok(QualifiedName::bare(first))
539 }
540 }
541
542 fn parse_qualified_table_ref(&mut self) -> Result<QualifiedTableRef, ParseError> {
543 let name = self.parse_qualified_name()?;
544 let alias = self.try_alias()?;
545 let index_hint = self.parse_index_hint()?;
546 let time_travel = self.parse_time_travel_clause()?;
547 Ok(QualifiedTableRef {
548 name,
549 alias,
550 index_hint,
551 time_travel,
552 })
553 }
554
555 fn try_alias(&mut self) -> Result<Option<String>, ParseError> {
556 if self.eat_kw(&TokenKind::KwAs) {
557 return Ok(Some(self.parse_identifier()?));
558 }
559 match self.peek() {
562 TokenKind::Id(_) | TokenKind::QuotedId(_, _) => {
563 return Ok(Some(self.parse_identifier()?));
564 }
565 k if is_nonreserved_kw(k) && !is_alias_terminator_kw(k) => {
566 return Ok(Some(self.parse_identifier()?));
567 }
568 _ => {}
569 }
570 Ok(None)
571 }
572
573 fn parse_index_hint(&mut self) -> Result<Option<IndexHint>, ParseError> {
574 if self.eat_kw(&TokenKind::KwIndexed) {
575 self.expect_kw(&TokenKind::KwBy)?;
576 Ok(Some(IndexHint::IndexedBy(self.parse_identifier()?)))
577 } else if self.check_kw(&TokenKind::KwNot) && self.peek_nth(1) == &TokenKind::KwIndexed {
578 self.advance();
579 self.advance();
580 Ok(Some(IndexHint::NotIndexed))
581 } else {
582 Ok(None)
583 }
584 }
585
586 fn parse_time_travel_clause(&mut self) -> Result<Option<TimeTravelClause>, ParseError> {
594 if !self.check_kw(&TokenKind::KwFor) {
595 return Ok(None);
596 }
597 if !matches!(self.peek_nth(1), TokenKind::Id(s) if s.eq_ignore_ascii_case("SYSTEM_TIME")) {
599 return Ok(None);
600 }
601 self.advance(); self.advance(); self.expect_kw(&TokenKind::KwAs)?;
604 self.expect_kw(&TokenKind::KwOf)?;
605
606 let target = if self.eat_kw(&TokenKind::KwCommitseq) {
607 match self.peek().clone() {
608 TokenKind::Integer(n) if n >= 0 => {
609 self.advance();
610 TimeTravelTarget::CommitSequence(n as u64)
611 }
612 TokenKind::OversizedInt(s) => {
613 if let Ok(n) = s.parse::<u64>() {
614 self.advance();
615 TimeTravelTarget::CommitSequence(n)
616 } else {
617 return Err(self.err_expected("non-negative integer after COMMITSEQ"));
618 }
619 }
620 _ => return Err(self.err_expected("non-negative integer after COMMITSEQ")),
621 }
622 } else {
623 match self.peek().clone() {
624 TokenKind::String(s) => {
625 self.advance();
626 TimeTravelTarget::Timestamp(s)
627 }
628 _ => {
629 return Err(self.err_expected(
630 "COMMITSEQ <n> or '<timestamp>' after FOR SYSTEM_TIME AS OF",
631 ));
632 }
633 }
634 };
635
636 Ok(Some(TimeTravelClause { target }))
637 }
638
639 pub(crate) fn parse_comma_sep<T>(
640 &mut self,
641 f: fn(&mut Self) -> Result<T, ParseError>,
642 ) -> Result<Vec<T>, ParseError> {
643 let mut v = Vec::with_capacity(4);
644 v.push(f(self)?);
645 while self.eat(&TokenKind::Comma) {
646 v.push(f(self)?);
647 }
648 Ok(v)
649 }
650
651 fn parse_statement_inner(&mut self) -> Result<Statement, ParseError> {
656 self.with_recursion_guard(|parser| match parser.peek().clone() {
657 TokenKind::KwSelect | TokenKind::KwValues => {
658 Ok(Statement::Select(parser.parse_select_stmt(None)?))
659 }
660 TokenKind::KwWith => parser.parse_with_leading(),
661 TokenKind::KwInsert | TokenKind::KwReplace => parser.parse_insert_stmt(None),
662 TokenKind::KwUpdate => parser.parse_update_stmt(None),
663 TokenKind::KwDelete => parser.parse_delete_stmt(None),
664 TokenKind::KwCreate => parser.parse_create(),
665 TokenKind::KwDrop => parser.parse_drop(),
666 TokenKind::KwAlter => parser.parse_alter(),
667 TokenKind::KwBegin => parser.parse_begin(),
668 TokenKind::KwCommit | TokenKind::KwEnd => {
669 parser.advance();
670 let _ = parser.eat_kw(&TokenKind::KwTransaction);
671 Ok(Statement::Commit)
672 }
673 TokenKind::KwRollback => parser.parse_rollback(),
674 TokenKind::KwSavepoint => {
675 parser.advance();
676 Ok(Statement::Savepoint(parser.parse_identifier()?))
677 }
678 TokenKind::KwRelease => {
679 parser.advance();
680 let _ = parser.eat_kw(&TokenKind::KwSavepoint);
681 Ok(Statement::Release(parser.parse_identifier()?))
682 }
683 TokenKind::KwAttach => parser.parse_attach(),
684 TokenKind::KwDetach => {
685 parser.advance();
686 let _ = parser.eat_kw(&TokenKind::KwDatabase);
687 Ok(Statement::Detach(parser.parse_identifier()?))
688 }
689 TokenKind::KwPragma => parser.parse_pragma(),
690 TokenKind::KwVacuum => parser.parse_vacuum(),
691 TokenKind::KwReindex => {
692 parser.advance();
693 let name = if !parser.at_eof() && !parser.check(&TokenKind::Semicolon) {
694 Some(parser.parse_qualified_name()?)
695 } else {
696 None
697 };
698 Ok(Statement::Reindex(name))
699 }
700 TokenKind::KwAnalyze => {
701 parser.advance();
702 let name = if !parser.at_eof() && !parser.check(&TokenKind::Semicolon) {
703 Some(parser.parse_qualified_name()?)
704 } else {
705 None
706 };
707 Ok(Statement::Analyze(name))
708 }
709 TokenKind::KwExplain => parser.parse_explain(),
710 _ => Err(parser.err_msg("unexpected token at start of statement")),
711 })
712 }
713
714 fn parse_with_leading(&mut self) -> Result<Statement, ParseError> {
719 let with = self.parse_with_clause()?;
720 match self.peek() {
721 TokenKind::KwSelect | TokenKind::KwValues => {
722 Ok(Statement::Select(self.parse_select_stmt(Some(with))?))
723 }
724 TokenKind::KwInsert | TokenKind::KwReplace => self.parse_insert_stmt(Some(with)),
725 TokenKind::KwUpdate => self.parse_update_stmt(Some(with)),
726 TokenKind::KwDelete => self.parse_delete_stmt(Some(with)),
727 _ => Err(self.err_expected("SELECT, INSERT, UPDATE, or DELETE after WITH")),
728 }
729 }
730
731 pub(crate) fn parse_with_clause(&mut self) -> Result<WithClause, ParseError> {
732 self.expect_kw(&TokenKind::KwWith)?;
733 let recursive = self.eat_kw(&TokenKind::KwRecursive);
734 let ctes = self.parse_comma_sep(Self::parse_cte)?;
735 Ok(WithClause { recursive, ctes })
736 }
737
738 fn parse_cte(&mut self) -> Result<Cte, ParseError> {
739 let name = self.parse_identifier()?;
740 let columns = if self.eat(&TokenKind::LeftParen) {
741 let cols = self.parse_comma_sep(Self::parse_identifier)?;
742 self.expect_token(&TokenKind::RightParen)?;
743 cols
744 } else {
745 vec![]
746 };
747 self.expect_kw(&TokenKind::KwAs)?;
749 let materialized = if self.check_kw(&TokenKind::KwNot) {
750 self.advance();
751 self.expect_kw(&TokenKind::KwMaterialized)?;
752 Some(CteMaterialized::NotMaterialized)
753 } else if self.eat_kw(&TokenKind::KwMaterialized) {
754 Some(CteMaterialized::Materialized)
755 } else {
756 None
757 };
758 self.expect_token(&TokenKind::LeftParen)?;
759 let query = self.parse_select_stmt(None)?;
760 self.expect_token(&TokenKind::RightParen)?;
761 Ok(Cte {
762 name,
763 columns,
764 materialized,
765 query,
766 })
767 }
768
769 pub(crate) fn parse_select_stmt(
774 &mut self,
775 with: Option<WithClause>,
776 ) -> Result<SelectStatement, ParseError> {
777 self.with_recursion_guard(|parser| parser.parse_select_stmt_inner(with))
778 }
779
780 fn parse_select_stmt_inner(
781 &mut self,
782 with: Option<WithClause>,
783 ) -> Result<SelectStatement, ParseError> {
784 let body = self.parse_select_body()?;
785 let order_by = if self.eat_kw(&TokenKind::KwOrder) {
786 self.expect_kw(&TokenKind::KwBy)?;
787 self.parse_comma_sep(Self::parse_ordering_term)?
788 } else {
789 vec![]
790 };
791 let limit = self.parse_limit()?;
792 if !body.compounds.is_empty()
798 && matches!(
799 body.compounds.last().map(|(_, core)| core),
800 Some(SelectCore::Values(_))
801 )
802 && (!order_by.is_empty() || limit.is_some())
803 {
804 return Err(self.err_msg(
805 "ORDER BY / LIMIT clause is not allowed after a VALUES term in a compound SELECT",
806 ));
807 }
808 Ok(SelectStatement {
809 with,
810 body,
811 order_by,
812 limit,
813 })
814 }
815
816 fn parse_select_body(&mut self) -> Result<SelectBody, ParseError> {
817 let select = self.parse_select_core()?;
818 let mut compounds = Vec::new();
819 loop {
820 let op = if self.eat_kw(&TokenKind::KwUnion) {
821 if self.eat_kw(&TokenKind::KwAll) {
822 CompoundOp::UnionAll
823 } else {
824 CompoundOp::Union
825 }
826 } else if self.eat_kw(&TokenKind::KwIntersect) {
827 CompoundOp::Intersect
828 } else if self.eat_kw(&TokenKind::KwExcept) {
829 CompoundOp::Except
830 } else {
831 break;
832 };
833 compounds.push((op, self.parse_select_core()?));
834 }
835 Ok(SelectBody { select, compounds })
836 }
837
838 fn parse_select_core(&mut self) -> Result<SelectCore, ParseError> {
839 if self.eat_kw(&TokenKind::KwValues) {
840 return self.parse_values_core();
841 }
842 self.expect_kw(&TokenKind::KwSelect)?;
843 let distinct = if self.eat_kw(&TokenKind::KwDistinct) {
844 Distinctness::Distinct
845 } else {
846 let _ = self.eat_kw(&TokenKind::KwAll);
847 Distinctness::All
848 };
849 let columns = self.parse_comma_sep(Self::parse_result_column)?;
850 let from = if self.eat_kw(&TokenKind::KwFrom) {
851 Some(self.parse_from_clause()?)
852 } else {
853 None
854 };
855 let where_clause = if self.eat_kw(&TokenKind::KwWhere) {
856 Some(Box::new(self.parse_expr()?))
857 } else {
858 None
859 };
860 let group_by = if self.eat_kw(&TokenKind::KwGroup) {
861 self.expect_kw(&TokenKind::KwBy)?;
862 self.parse_comma_sep(Self::parse_expr)?
863 } else {
864 vec![]
865 };
866 let having = if self.eat_kw(&TokenKind::KwHaving) {
867 Some(Box::new(self.parse_expr()?))
868 } else {
869 None
870 };
871 let windows = if self.eat_kw(&TokenKind::KwWindow) {
872 self.parse_comma_sep(Self::parse_window_def)?
873 } else {
874 vec![]
875 };
876 Ok(SelectCore::Select {
877 distinct,
878 columns,
879 from,
880 where_clause,
881 group_by,
882 having,
883 windows,
884 })
885 }
886
887 fn parse_values_core(&mut self) -> Result<SelectCore, ParseError> {
888 let mut rows = Vec::new();
889 loop {
890 self.expect_token(&TokenKind::LeftParen)?;
891 let row = self.parse_comma_sep(Self::parse_expr)?;
892 self.expect_token(&TokenKind::RightParen)?;
893 rows.push(row);
894 if !self.eat(&TokenKind::Comma) {
895 break;
896 }
897 }
898 Ok(SelectCore::Values(rows))
899 }
900
901 fn parse_result_column(&mut self) -> Result<ResultColumn, ParseError> {
902 if self.eat(&TokenKind::Star) {
903 return Ok(ResultColumn::Star);
904 }
905 if matches!(self.peek(), TokenKind::Id(_) | TokenKind::QuotedId(_, _))
906 && self.peek_nth(1) == &TokenKind::Dot
907 {
908 if self.peek_nth(2) == &TokenKind::Star {
909 let table = self.parse_identifier()?;
910 self.expect_token(&TokenKind::Dot)?;
911 self.expect_token(&TokenKind::Star)?;
912 return Ok(ResultColumn::TableStar(QualifiedName::bare(table)));
913 }
914 if matches!(
915 self.peek_nth(2),
916 TokenKind::Id(_) | TokenKind::QuotedId(_, _)
917 ) && self.peek_nth(3) == &TokenKind::Dot
918 && self.peek_nth(4) == &TokenKind::Star
919 {
920 let schema = self.parse_identifier()?;
921 self.expect_token(&TokenKind::Dot)?;
922 let table = self.parse_identifier()?;
923 self.expect_token(&TokenKind::Dot)?;
924 self.expect_token(&TokenKind::Star)?;
925 return Ok(ResultColumn::TableStar(QualifiedName::qualified(
926 schema, table,
927 )));
928 }
929 }
930 let expr = self.parse_expr()?;
931 let alias = self.try_alias()?;
932 Ok(ResultColumn::Expr { expr, alias })
933 }
934
935 fn parse_from_clause(&mut self) -> Result<FromClause, ParseError> {
940 let source = self.parse_table_or_subquery()?;
941 let mut joins = Vec::new();
942 loop {
943 if let Some(jt) = self.try_join_type()? {
944 let table = self.parse_table_or_subquery()?;
945 let constraint = self.parse_join_constraint()?;
946 if jt.natural && constraint.is_some() {
947 return Err(self.err_msg("a NATURAL join may not have an ON or USING clause"));
948 }
949 joins.push(JoinClause {
950 join_type: jt,
951 table,
952 constraint,
953 });
954 } else if self.eat(&TokenKind::Comma) {
955 let table = self.parse_table_or_subquery()?;
956 joins.push(JoinClause {
957 join_type: JoinType {
958 natural: false,
959 kind: JoinKind::Cross,
960 },
961 table,
962 constraint: None,
963 });
964 } else {
965 break;
966 }
967 }
968 Ok(FromClause { source, joins })
969 }
970
971 fn parse_table_or_subquery(&mut self) -> Result<TableOrSubquery, ParseError> {
972 self.with_recursion_guard(|parser| parser.parse_table_or_subquery_inner())
973 }
974
975 fn parse_table_or_subquery_inner(&mut self) -> Result<TableOrSubquery, ParseError> {
976 if self.check(&TokenKind::LeftParen) {
977 self.advance();
978 if matches!(
979 self.peek(),
980 TokenKind::KwSelect | TokenKind::KwWith | TokenKind::KwValues
981 ) {
982 let with = if self.check_kw(&TokenKind::KwWith) {
983 Some(self.parse_with_clause()?)
984 } else {
985 None
986 };
987 let q = self.parse_select_stmt(with)?;
988 self.expect_token(&TokenKind::RightParen)?;
989 let alias = self.try_alias()?;
990 return Ok(TableOrSubquery::Subquery {
991 query: Box::new(q),
992 alias,
993 });
994 }
995 let fc = self.parse_from_clause()?;
997 self.expect_token(&TokenKind::RightParen)?;
998 return Ok(TableOrSubquery::ParenJoin(Box::new(fc)));
999 }
1000
1001 let name = self.parse_qualified_name()?;
1002
1003 if self.check(&TokenKind::LeftParen) && name.schema.is_none() {
1005 self.advance();
1006 let args = if self.check(&TokenKind::RightParen) {
1007 vec![]
1008 } else {
1009 self.parse_comma_sep(Self::parse_expr)?
1010 };
1011 self.expect_token(&TokenKind::RightParen)?;
1012 let alias = self.try_alias()?;
1013 return Ok(TableOrSubquery::TableFunction {
1014 name: name.name,
1015 args,
1016 alias,
1017 });
1018 }
1019
1020 let alias = self.try_alias()?;
1021 let index_hint = self.parse_index_hint()?;
1022 let time_travel = self.parse_time_travel_clause()?;
1023 Ok(TableOrSubquery::Table {
1024 name,
1025 alias,
1026 index_hint,
1027 time_travel,
1028 })
1029 }
1030
1031 fn try_join_type(&mut self) -> Result<Option<JoinType>, ParseError> {
1032 let natural = self.eat_kw(&TokenKind::KwNatural);
1033 let kind = if self.eat_kw(&TokenKind::KwJoin) {
1034 Some(JoinKind::Inner)
1035 } else if self.eat_kw(&TokenKind::KwInner) {
1036 self.expect_kw(&TokenKind::KwJoin)?;
1037 Some(JoinKind::Inner)
1038 } else if self.eat_kw(&TokenKind::KwCross) {
1039 self.expect_kw(&TokenKind::KwJoin)?;
1040 Some(JoinKind::Cross)
1041 } else if self.eat_kw(&TokenKind::KwLeft) {
1042 let _ = self.eat_kw(&TokenKind::KwOuter);
1043 self.expect_kw(&TokenKind::KwJoin)?;
1044 Some(JoinKind::Left)
1045 } else if self.eat_kw(&TokenKind::KwRight) {
1046 let _ = self.eat_kw(&TokenKind::KwOuter);
1047 self.expect_kw(&TokenKind::KwJoin)?;
1048 Some(JoinKind::Right)
1049 } else if self.eat_kw(&TokenKind::KwFull) {
1050 let _ = self.eat_kw(&TokenKind::KwOuter);
1051 self.expect_kw(&TokenKind::KwJoin)?;
1052 Some(JoinKind::Full)
1053 } else {
1054 None
1055 };
1056 match kind {
1057 Some(k) => Ok(Some(JoinType { natural, kind: k })),
1058 None if natural => Err(self.err_expected("JOIN after NATURAL")),
1059 None => Ok(None),
1060 }
1061 }
1062
1063 fn parse_join_constraint(&mut self) -> Result<Option<JoinConstraint>, ParseError> {
1064 if self.eat_kw(&TokenKind::KwOn) {
1065 Ok(Some(JoinConstraint::On(self.parse_expr()?)))
1066 } else if self.eat_kw(&TokenKind::KwUsing) {
1067 self.expect_token(&TokenKind::LeftParen)?;
1068 let cols = self.parse_comma_sep(Self::parse_identifier)?;
1069 self.expect_token(&TokenKind::RightParen)?;
1070 Ok(Some(JoinConstraint::Using(cols)))
1071 } else {
1072 Ok(None)
1073 }
1074 }
1075
1076 pub(crate) fn parse_ordering_term(&mut self) -> Result<OrderingTerm, ParseError> {
1081 let expr = self.parse_expr()?;
1082 let direction = if self.eat_kw(&TokenKind::KwAsc) {
1083 Some(SortDirection::Asc)
1084 } else if self.eat_kw(&TokenKind::KwDesc) {
1085 Some(SortDirection::Desc)
1086 } else {
1087 None
1088 };
1089 let nulls = if self.eat_kw(&TokenKind::KwNulls) {
1090 if self.eat_kw(&TokenKind::KwFirst) {
1091 Some(NullsOrder::First)
1092 } else {
1093 self.expect_kw(&TokenKind::KwLast)?;
1094 Some(NullsOrder::Last)
1095 }
1096 } else {
1097 None
1098 };
1099 Ok(OrderingTerm {
1100 expr,
1101 direction,
1102 nulls,
1103 })
1104 }
1105
1106 pub(crate) fn parse_limit(&mut self) -> Result<Option<LimitClause>, ParseError> {
1107 if !self.eat_kw(&TokenKind::KwLimit) {
1108 return Ok(None);
1109 }
1110 let first = self.parse_expr()?;
1111 if self.eat_kw(&TokenKind::KwOffset) {
1112 return Ok(Some(LimitClause {
1113 limit: first,
1114 offset: Some(self.parse_expr()?),
1115 }));
1116 }
1117
1118 if self.eat(&TokenKind::Comma) {
1119 let second = self.parse_expr()?;
1121 return Ok(Some(LimitClause {
1122 limit: second,
1123 offset: Some(first),
1124 }));
1125 }
1126
1127 Ok(Some(LimitClause {
1128 limit: first,
1129 offset: None,
1130 }))
1131 }
1132
1133 fn parse_returning(&mut self) -> Result<Vec<ResultColumn>, ParseError> {
1138 if self.eat_kw(&TokenKind::KwReturning) {
1139 self.parse_comma_sep(Self::parse_result_column)
1140 } else {
1141 Ok(vec![])
1142 }
1143 }
1144
1145 fn parse_insert_stmt(&mut self, with: Option<WithClause>) -> Result<Statement, ParseError> {
1150 let or_conflict = if self.eat_kw(&TokenKind::KwReplace) {
1151 Some(ConflictAction::Replace)
1152 } else {
1153 self.expect_kw(&TokenKind::KwInsert)?;
1154 if self.eat_kw(&TokenKind::KwOr) {
1155 Some(self.parse_conflict_action()?)
1156 } else {
1157 None
1158 }
1159 };
1160 self.eat_kw(&TokenKind::KwInto);
1161 let table = self.parse_qualified_name()?;
1162 let alias = if self.eat_kw(&TokenKind::KwAs) {
1163 Some(self.parse_identifier()?)
1164 } else {
1165 None
1166 };
1167 let columns = if self.check(&TokenKind::LeftParen)
1168 && !matches!(
1169 self.peek_nth(1),
1170 TokenKind::KwSelect | TokenKind::KwWith | TokenKind::KwValues
1171 ) {
1172 self.advance();
1173 let cols = self.parse_comma_sep(Self::parse_identifier)?;
1174 self.expect_token(&TokenKind::RightParen)?;
1175 cols
1176 } else {
1177 vec![]
1178 };
1179 let source = if self.eat_kw(&TokenKind::KwDefault) {
1180 self.expect_kw(&TokenKind::KwValues)?;
1181 InsertSource::DefaultValues
1182 } else if self.eat_kw(&TokenKind::KwValues) {
1183 match self.parse_values_core()? {
1184 SelectCore::Values(rows) => InsertSource::Values(rows),
1185 SelectCore::Select { .. } => unreachable!("parse_values_core must return VALUES"),
1186 }
1187 } else {
1188 let inner_with = if self.check_kw(&TokenKind::KwWith) {
1189 Some(self.parse_with_clause()?)
1190 } else {
1191 None
1192 };
1193 InsertSource::Select(Box::new(self.parse_select_stmt(inner_with)?))
1194 };
1195 let upsert = self.parse_upsert_clauses()?;
1196 let returning = self.parse_returning()?;
1197 Ok(Statement::Insert(InsertStatement {
1198 with,
1199 or_conflict,
1200 table,
1201 alias,
1202 columns,
1203 source,
1204 upsert,
1205 returning,
1206 }))
1207 }
1208
1209 fn parse_conflict_action(&mut self) -> Result<ConflictAction, ParseError> {
1210 if self.eat_kw(&TokenKind::KwRollback) {
1211 Ok(ConflictAction::Rollback)
1212 } else if self.eat_kw(&TokenKind::KwAbort) {
1213 Ok(ConflictAction::Abort)
1214 } else if self.eat_kw(&TokenKind::KwFail) {
1215 Ok(ConflictAction::Fail)
1216 } else if self.eat_kw(&TokenKind::KwIgnore) {
1217 Ok(ConflictAction::Ignore)
1218 } else if self.eat_kw(&TokenKind::KwReplace) {
1219 Ok(ConflictAction::Replace)
1220 } else {
1221 Err(self.err_expected("conflict action"))
1222 }
1223 }
1224
1225 fn parse_upsert_clauses(&mut self) -> Result<Vec<UpsertClause>, ParseError> {
1226 let mut clauses = Vec::new();
1227 while self.check_kw(&TokenKind::KwOn) && self.peek_nth(1) == &TokenKind::KwConflict {
1228 if clauses
1231 .last()
1232 .is_some_and(|clause: &UpsertClause| clause.target.is_none())
1233 {
1234 return Err(self.err_msg(
1235 "ON CONFLICT clause without a conflict target must be the last ON CONFLICT clause",
1236 ));
1237 }
1238 self.advance(); self.advance(); let target = if self.check(&TokenKind::LeftParen) {
1241 self.advance();
1242 let columns = self.parse_comma_sep(Self::parse_indexed_column)?;
1243 self.expect_token(&TokenKind::RightParen)?;
1244 let wh = if self.eat_kw(&TokenKind::KwWhere) {
1245 Some(self.parse_expr()?)
1246 } else {
1247 None
1248 };
1249 Some(UpsertTarget {
1250 columns,
1251 where_clause: wh,
1252 })
1253 } else {
1254 None
1255 };
1256 self.expect_kw(&TokenKind::KwDo)?;
1257 let action = if self.eat_kw(&TokenKind::KwNothing) {
1258 UpsertAction::Nothing
1259 } else {
1260 self.expect_kw(&TokenKind::KwUpdate)?;
1261 self.expect_kw(&TokenKind::KwSet)?;
1262 let assignments = self.parse_comma_sep(Self::parse_assignment)?;
1263 let wh = if self.eat_kw(&TokenKind::KwWhere) {
1264 Some(Box::new(self.parse_expr()?))
1265 } else {
1266 None
1267 };
1268 UpsertAction::Update {
1269 assignments,
1270 where_clause: wh,
1271 }
1272 };
1273 clauses.push(UpsertClause { target, action });
1274 }
1275 Ok(clauses)
1276 }
1277
1278 fn parse_update_stmt(&mut self, with: Option<WithClause>) -> Result<Statement, ParseError> {
1283 self.expect_kw(&TokenKind::KwUpdate)?;
1284 let or_conflict = if self.eat_kw(&TokenKind::KwOr) {
1285 Some(self.parse_conflict_action()?)
1286 } else {
1287 None
1288 };
1289 let table = self.parse_qualified_table_ref()?;
1290 self.expect_kw(&TokenKind::KwSet)?;
1291 let assignments = self.parse_comma_sep(Self::parse_assignment)?;
1292 let from = if self.eat_kw(&TokenKind::KwFrom) {
1293 Some(self.parse_from_clause()?)
1294 } else {
1295 None
1296 };
1297 let where_clause = if self.eat_kw(&TokenKind::KwWhere) {
1298 Some(self.parse_expr()?)
1299 } else {
1300 None
1301 };
1302 let returning = self.parse_returning()?;
1303 let order_by = if self.eat_kw(&TokenKind::KwOrder) {
1304 self.expect_kw(&TokenKind::KwBy)?;
1305 self.parse_comma_sep(Self::parse_ordering_term)?
1306 } else {
1307 vec![]
1308 };
1309 let limit = self.parse_limit()?;
1310 Ok(Statement::Update(UpdateStatement {
1311 with,
1312 or_conflict,
1313 table,
1314 assignments,
1315 from,
1316 where_clause,
1317 returning,
1318 order_by,
1319 limit,
1320 }))
1321 }
1322
1323 fn parse_assignment(&mut self) -> Result<Assignment, ParseError> {
1324 let target = if self.check(&TokenKind::LeftParen) {
1325 self.advance();
1326 let cols = self.parse_comma_sep(Self::parse_identifier)?;
1327 self.expect_token(&TokenKind::RightParen)?;
1328 AssignmentTarget::ColumnList(cols)
1329 } else {
1330 AssignmentTarget::Column(self.parse_identifier()?)
1331 };
1332 self.expect_token(&TokenKind::Eq)?;
1333 let value = self.parse_expr()?;
1334 Ok(Assignment { target, value })
1335 }
1336
1337 fn parse_delete_stmt(&mut self, with: Option<WithClause>) -> Result<Statement, ParseError> {
1342 self.expect_kw(&TokenKind::KwDelete)?;
1343 self.expect_kw(&TokenKind::KwFrom)?;
1344 let table = self.parse_qualified_table_ref()?;
1345 let where_clause = if self.eat_kw(&TokenKind::KwWhere) {
1346 Some(self.parse_expr()?)
1347 } else {
1348 None
1349 };
1350 let returning = self.parse_returning()?;
1351 let order_by = if self.eat_kw(&TokenKind::KwOrder) {
1352 self.expect_kw(&TokenKind::KwBy)?;
1353 self.parse_comma_sep(Self::parse_ordering_term)?
1354 } else {
1355 vec![]
1356 };
1357 let limit = self.parse_limit()?;
1358 Ok(Statement::Delete(DeleteStatement {
1359 with,
1360 table,
1361 where_clause,
1362 returning,
1363 order_by,
1364 limit,
1365 }))
1366 }
1367
1368 fn parse_create(&mut self) -> Result<Statement, ParseError> {
1373 self.expect_kw(&TokenKind::KwCreate)?;
1374 let temporary = self.eat_kw(&TokenKind::KwTemp) || self.eat_kw(&TokenKind::KwTemporary);
1375 let unique = self.eat_kw(&TokenKind::KwUnique);
1376
1377 if self.eat_kw(&TokenKind::KwTable) {
1378 return self.parse_create_table(temporary);
1379 }
1380 if self.eat_kw(&TokenKind::KwIndex) {
1381 return self.parse_create_index(unique);
1382 }
1383 if self.eat_kw(&TokenKind::KwView) {
1384 return self.parse_create_view(temporary);
1385 }
1386 if self.eat_kw(&TokenKind::KwTrigger) {
1387 return self.parse_create_trigger(temporary);
1388 }
1389 if self.eat_kw(&TokenKind::KwVirtual) {
1390 self.expect_kw(&TokenKind::KwTable)?;
1391 return self.parse_create_virtual_table();
1392 }
1393 Err(self.err_expected("TABLE, INDEX, VIEW, TRIGGER, or VIRTUAL"))
1394 }
1395
1396 fn parse_if_not_exists(&mut self) -> bool {
1397 if self.check_kw(&TokenKind::KwIf)
1398 && self.peek_nth(1) == &TokenKind::KwNot
1399 && self.peek_nth(2) == &TokenKind::KwExists
1400 {
1401 self.advance();
1402 self.advance();
1403 self.advance();
1404 true
1405 } else {
1406 false
1407 }
1408 }
1409
1410 fn parse_create_table(&mut self, temporary: bool) -> Result<Statement, ParseError> {
1411 let if_not_exists = self.parse_if_not_exists();
1412 let name = self.parse_qualified_name()?;
1413 let body = if self.eat_kw(&TokenKind::KwAs) {
1414 let with = if self.check_kw(&TokenKind::KwWith) {
1415 Some(self.parse_with_clause()?)
1416 } else {
1417 None
1418 };
1419 CreateTableBody::AsSelect(Box::new(self.parse_select_stmt(with)?))
1420 } else {
1421 self.expect_token(&TokenKind::LeftParen)?;
1422 let mut columns = Vec::new();
1423 let mut constraints = Vec::new();
1424 loop {
1425 if self.is_table_constraint_start() {
1426 constraints.push(self.parse_table_constraint()?);
1427 } else {
1428 columns.push(self.parse_column_def()?);
1429 }
1430 if !self.eat(&TokenKind::Comma) {
1431 break;
1432 }
1433 }
1434 self.expect_token(&TokenKind::RightParen)?;
1435 CreateTableBody::Columns {
1436 columns,
1437 constraints,
1438 }
1439 };
1440 let mut without_rowid = false;
1441 let mut strict = false;
1442 if self.check_kw(&TokenKind::KwWithout) || self.check_kw(&TokenKind::KwStrict) {
1444 loop {
1445 if self.check_kw(&TokenKind::KwWithout) {
1446 self.advance();
1447 let id = self.parse_identifier()?;
1449 if !id.eq_ignore_ascii_case("ROWID") {
1450 return Err(self.err_expected("ROWID after WITHOUT"));
1451 }
1452 without_rowid = true;
1453 } else if self.eat_kw(&TokenKind::KwStrict) {
1454 strict = true;
1455 } else {
1456 return Err(self.err_expected("table option"));
1457 }
1458 if !self.eat(&TokenKind::Comma) {
1459 break;
1460 }
1461 }
1462 }
1463 Ok(Statement::CreateTable(CreateTableStatement {
1464 if_not_exists,
1465 temporary,
1466 name,
1467 body,
1468 without_rowid,
1469 strict,
1470 }))
1471 }
1472
1473 fn is_table_constraint_start(&self) -> bool {
1474 matches!(
1475 self.peek(),
1476 TokenKind::KwPrimary | TokenKind::KwUnique | TokenKind::KwCheck | TokenKind::KwForeign
1477 ) || (self.check_kw(&TokenKind::KwConstraint))
1478 }
1479
1480 fn parse_column_def(&mut self) -> Result<ColumnDef, ParseError> {
1481 let name = self.parse_identifier()?;
1482 let type_name = self.try_type_name()?;
1483 let mut constraints = Vec::new();
1484 while let Some(c) = self.try_column_constraint()? {
1485 constraints.push(c);
1486 }
1487 Ok(ColumnDef {
1488 name,
1489 type_name,
1490 constraints,
1491 })
1492 }
1493
1494 fn try_type_name(&mut self) -> Result<Option<TypeName>, ParseError> {
1495 if self.is_column_constraint_start()
1497 || matches!(
1498 self.peek(),
1499 TokenKind::Comma | TokenKind::RightParen | TokenKind::Eof
1500 )
1501 {
1502 return Ok(None);
1503 }
1504 let mut words = Vec::new();
1506 loop {
1507 match self.peek() {
1508 TokenKind::Id(_) | TokenKind::QuotedId(_, _) => {
1509 words.push(self.parse_identifier()?);
1510 }
1511 k if is_nonreserved_kw(k) => {
1512 words.push(self.parse_identifier()?);
1513 }
1514 _ => break,
1515 }
1516 if self.is_column_constraint_start()
1517 || matches!(
1518 self.peek(),
1519 TokenKind::Comma | TokenKind::RightParen | TokenKind::LeftParen
1520 )
1521 {
1522 break;
1523 }
1524 }
1525 if words.is_empty() {
1526 return Ok(None);
1527 }
1528 let type_name = words.join(" ");
1529 let (arg1, arg2) = if self.eat(&TokenKind::LeftParen) {
1530 let a1 = self.parse_signed_number_str()?;
1531 let a2 = if self.eat(&TokenKind::Comma) {
1532 Some(self.parse_signed_number_str()?)
1533 } else {
1534 None
1535 };
1536 self.expect_token(&TokenKind::RightParen)?;
1537 (Some(a1), a2)
1538 } else {
1539 (None, None)
1540 };
1541 Ok(Some(TypeName {
1542 name: type_name,
1543 arg1,
1544 arg2,
1545 }))
1546 }
1547
1548 fn parse_signed_number_str(&mut self) -> Result<String, ParseError> {
1549 let neg = self.eat(&TokenKind::Minus);
1550 let plus = if neg {
1551 false
1552 } else {
1553 self.eat(&TokenKind::Plus)
1554 };
1555 let _ = plus; match self.peek().clone() {
1557 TokenKind::Integer(n) => {
1558 self.advance();
1559 Ok(if neg { format!("-{n}") } else { n.to_string() })
1560 }
1561 TokenKind::OversizedInt(s) => {
1562 self.advance();
1563 Ok(if neg { format!("-{s}") } else { s.clone() })
1564 }
1565 TokenKind::Float(f) => {
1566 self.advance();
1567 Ok(if neg { format!("-{f}") } else { f.to_string() })
1568 }
1569 _ => Err(self.err_expected("number")),
1570 }
1571 }
1572
1573 fn is_column_constraint_start(&self) -> bool {
1574 matches!(
1575 self.peek(),
1576 TokenKind::KwPrimary
1577 | TokenKind::KwNot
1578 | TokenKind::KwNull
1579 | TokenKind::KwUnique
1580 | TokenKind::KwCheck
1581 | TokenKind::KwDefault
1582 | TokenKind::KwCollate
1583 | TokenKind::KwReferences
1584 | TokenKind::KwGenerated
1585 | TokenKind::KwConstraint
1586 | TokenKind::KwAs
1587 )
1588 }
1589
1590 fn try_column_constraint(&mut self) -> Result<Option<ColumnConstraint>, ParseError> {
1591 let name = if self.eat_kw(&TokenKind::KwConstraint) {
1592 Some(self.parse_identifier()?)
1593 } else {
1594 None
1595 };
1596 let kind = if self.eat_kw(&TokenKind::KwPrimary) {
1597 self.expect_kw(&TokenKind::KwKey)?;
1598 let direction = if self.eat_kw(&TokenKind::KwAsc) {
1599 Some(SortDirection::Asc)
1600 } else if self.eat_kw(&TokenKind::KwDesc) {
1601 Some(SortDirection::Desc)
1602 } else {
1603 None
1604 };
1605 let conflict = self.parse_on_conflict()?;
1606 let autoincrement = self.eat_kw(&TokenKind::KwAutoincrement);
1607 ColumnConstraintKind::PrimaryKey {
1608 direction,
1609 conflict,
1610 autoincrement,
1611 }
1612 } else if self.check_kw(&TokenKind::KwNot) && self.peek_nth(1) == &TokenKind::KwNull {
1613 self.advance();
1614 self.advance();
1615 let conflict = self.parse_on_conflict()?;
1616 ColumnConstraintKind::NotNull { conflict }
1617 } else if self.eat_kw(&TokenKind::KwNull) {
1618 ColumnConstraintKind::Null
1619 } else if self.eat_kw(&TokenKind::KwUnique) {
1620 let conflict = self.parse_on_conflict()?;
1621 ColumnConstraintKind::Unique { conflict }
1622 } else if self.eat_kw(&TokenKind::KwCheck) {
1623 self.expect_token(&TokenKind::LeftParen)?;
1624 let expr = self.parse_expr()?;
1625 self.expect_token(&TokenKind::RightParen)?;
1626 ColumnConstraintKind::Check(expr)
1627 } else if self.eat_kw(&TokenKind::KwDefault) {
1628 if self.eat(&TokenKind::LeftParen) {
1629 let expr = self.parse_expr()?;
1630 self.expect_token(&TokenKind::RightParen)?;
1631 ColumnConstraintKind::Default(DefaultValue::ParenExpr(expr))
1632 } else {
1633 let expr = self.parse_expr()?;
1634 ColumnConstraintKind::Default(DefaultValue::Expr(expr))
1635 }
1636 } else if self.eat_kw(&TokenKind::KwCollate) {
1637 ColumnConstraintKind::Collate(self.parse_identifier()?)
1638 } else if self.eat_kw(&TokenKind::KwReferences) {
1639 ColumnConstraintKind::ForeignKey(self.parse_fk_clause()?)
1640 } else if self.eat_kw(&TokenKind::KwGenerated) || self.eat_kw(&TokenKind::KwAs) {
1641 if self.tokens[self.pos.saturating_sub(1)].kind == TokenKind::KwGenerated {
1642 let _ = self.eat_kw(&TokenKind::KwAlways);
1643 let _ = self.eat_kw(&TokenKind::KwAs);
1644 }
1645 self.expect_token(&TokenKind::LeftParen)?;
1646 let expr = self.parse_expr()?;
1647 self.expect_token(&TokenKind::RightParen)?;
1648 let storage = if self.eat_kw(&TokenKind::KwStored) {
1649 Some(GeneratedStorage::Stored)
1650 } else if self.eat_kw(&TokenKind::KwVirtual) {
1651 Some(GeneratedStorage::Virtual)
1652 } else {
1653 None
1654 };
1655 ColumnConstraintKind::Generated { expr, storage }
1656 } else if name.is_some() {
1657 return Err(self.err_expected("constraint kind after CONSTRAINT name"));
1658 } else {
1659 return Ok(None);
1660 };
1661 Ok(Some(ColumnConstraint { name, kind }))
1662 }
1663
1664 fn parse_on_conflict(&mut self) -> Result<Option<ConflictAction>, ParseError> {
1665 if self.check_kw(&TokenKind::KwOn) && self.peek_nth(1) == &TokenKind::KwConflict {
1666 self.advance();
1667 self.advance();
1668 Ok(Some(self.parse_conflict_action()?))
1669 } else {
1670 Ok(None)
1671 }
1672 }
1673
1674 fn parse_fk_clause(&mut self) -> Result<ForeignKeyClause, ParseError> {
1675 let table = self.parse_identifier()?;
1676 let columns = if self.eat(&TokenKind::LeftParen) {
1677 let cols = self.parse_comma_sep(Self::parse_identifier)?;
1678 self.expect_token(&TokenKind::RightParen)?;
1679 cols
1680 } else {
1681 vec![]
1682 };
1683 let mut actions = Vec::new();
1684 let mut deferrable = None;
1685 loop {
1686 if self.check_kw(&TokenKind::KwOn) {
1687 self.advance();
1688 let trigger = if self.eat_kw(&TokenKind::KwDelete) {
1689 ForeignKeyTrigger::OnDelete
1690 } else {
1691 self.expect_kw(&TokenKind::KwUpdate)?;
1692 ForeignKeyTrigger::OnUpdate
1693 };
1694 let action = self.parse_fk_action_type()?;
1695 actions.push(ForeignKeyAction { trigger, action });
1696 } else if self.check_kw(&TokenKind::KwNot) || self.check_kw(&TokenKind::KwDeferrable) {
1697 let not = self.eat_kw(&TokenKind::KwNot);
1698 self.expect_kw(&TokenKind::KwDeferrable)?;
1699 let initially = if self.eat_kw(&TokenKind::KwInitially) {
1700 if self.eat_kw(&TokenKind::KwDeferred) {
1701 Some(DeferrableInitially::Deferred)
1702 } else {
1703 self.expect_kw(&TokenKind::KwImmediate)?;
1704 Some(DeferrableInitially::Immediate)
1705 }
1706 } else {
1707 None
1708 };
1709 deferrable = Some(Deferrable { not, initially });
1710 } else if self.eat_kw(&TokenKind::KwMatch) {
1711 self.parse_identifier()?;
1713 } else {
1714 break;
1715 }
1716 }
1717 Ok(ForeignKeyClause {
1718 table,
1719 columns,
1720 actions,
1721 deferrable,
1722 })
1723 }
1724
1725 fn parse_fk_action_type(&mut self) -> Result<ForeignKeyActionType, ParseError> {
1726 if self.eat_kw(&TokenKind::KwSet) {
1727 if self.eat_kw(&TokenKind::KwNull) {
1728 Ok(ForeignKeyActionType::SetNull)
1729 } else {
1730 self.expect_kw(&TokenKind::KwDefault)?;
1731 Ok(ForeignKeyActionType::SetDefault)
1732 }
1733 } else if self.eat_kw(&TokenKind::KwCascade) {
1734 Ok(ForeignKeyActionType::Cascade)
1735 } else if self.eat_kw(&TokenKind::KwRestrict) {
1736 Ok(ForeignKeyActionType::Restrict)
1737 } else if self.check_kw(&TokenKind::KwNo) {
1738 self.advance();
1739 let id = self.parse_identifier()?;
1740 if !id.eq_ignore_ascii_case("ACTION") {
1741 return Err(self.err_expected("ACTION after NO"));
1742 }
1743 Ok(ForeignKeyActionType::NoAction)
1744 } else {
1745 Err(self.err_expected("foreign key action"))
1746 }
1747 }
1748
1749 fn parse_table_constraint(&mut self) -> Result<TableConstraint, ParseError> {
1750 let name = if self.eat_kw(&TokenKind::KwConstraint) {
1751 Some(self.parse_identifier()?)
1752 } else {
1753 None
1754 };
1755 let kind = if self.eat_kw(&TokenKind::KwPrimary) {
1756 self.expect_kw(&TokenKind::KwKey)?;
1757 self.expect_token(&TokenKind::LeftParen)?;
1758 let columns = self.parse_comma_sep(Self::parse_indexed_column)?;
1759 self.expect_token(&TokenKind::RightParen)?;
1760 let conflict = self.parse_on_conflict()?;
1761 TableConstraintKind::PrimaryKey { columns, conflict }
1762 } else if self.eat_kw(&TokenKind::KwUnique) {
1763 self.expect_token(&TokenKind::LeftParen)?;
1764 let columns = self.parse_comma_sep(Self::parse_indexed_column)?;
1765 self.expect_token(&TokenKind::RightParen)?;
1766 let conflict = self.parse_on_conflict()?;
1767 TableConstraintKind::Unique { columns, conflict }
1768 } else if self.eat_kw(&TokenKind::KwCheck) {
1769 self.expect_token(&TokenKind::LeftParen)?;
1770 let expr = self.parse_expr()?;
1771 self.expect_token(&TokenKind::RightParen)?;
1772 TableConstraintKind::Check(expr)
1773 } else if self.eat_kw(&TokenKind::KwForeign) {
1774 self.expect_kw(&TokenKind::KwKey)?;
1775 self.expect_token(&TokenKind::LeftParen)?;
1776 let columns = self.parse_comma_sep(Self::parse_identifier)?;
1777 self.expect_token(&TokenKind::RightParen)?;
1778 self.expect_kw(&TokenKind::KwReferences)?;
1779 let clause = self.parse_fk_clause()?;
1780 TableConstraintKind::ForeignKey { columns, clause }
1781 } else {
1782 return Err(self.err_expected("table constraint"));
1783 };
1784 Ok(TableConstraint { name, kind })
1785 }
1786
1787 fn parse_indexed_column(&mut self) -> Result<IndexedColumn, ParseError> {
1788 let expr = self.parse_expr()?;
1789 let collation = if self.eat_kw(&TokenKind::KwCollate) {
1790 Some(self.parse_identifier()?)
1791 } else {
1792 None
1793 };
1794 let direction = if self.eat_kw(&TokenKind::KwAsc) {
1795 Some(SortDirection::Asc)
1796 } else if self.eat_kw(&TokenKind::KwDesc) {
1797 Some(SortDirection::Desc)
1798 } else {
1799 None
1800 };
1801 Ok(IndexedColumn {
1802 expr,
1803 collation,
1804 direction,
1805 })
1806 }
1807
1808 fn parse_create_index(&mut self, unique: bool) -> Result<Statement, ParseError> {
1809 let if_not_exists = self.parse_if_not_exists();
1810 let name = self.parse_qualified_name()?;
1811 self.expect_kw(&TokenKind::KwOn)?;
1812 let table = self.parse_identifier()?;
1813 self.expect_token(&TokenKind::LeftParen)?;
1814 let columns = self.parse_comma_sep(Self::parse_indexed_column)?;
1815 self.expect_token(&TokenKind::RightParen)?;
1816 let where_clause = if self.eat_kw(&TokenKind::KwWhere) {
1817 Some(self.parse_expr()?)
1818 } else {
1819 None
1820 };
1821 Ok(Statement::CreateIndex(CreateIndexStatement {
1822 unique,
1823 if_not_exists,
1824 name,
1825 table,
1826 columns,
1827 where_clause,
1828 }))
1829 }
1830
1831 fn parse_create_view(&mut self, temporary: bool) -> Result<Statement, ParseError> {
1832 let if_not_exists = self.parse_if_not_exists();
1833 let name = self.parse_qualified_name()?;
1834 let columns = if self.check(&TokenKind::LeftParen) {
1835 self.advance();
1836 let cols = self.parse_comma_sep(Self::parse_identifier)?;
1837 self.expect_token(&TokenKind::RightParen)?;
1838 cols
1839 } else {
1840 vec![]
1841 };
1842 self.expect_kw(&TokenKind::KwAs)?;
1843 let with = if self.check_kw(&TokenKind::KwWith) {
1844 Some(self.parse_with_clause()?)
1845 } else {
1846 None
1847 };
1848 let query = self.parse_select_stmt(with)?;
1849 Ok(Statement::CreateView(CreateViewStatement {
1850 if_not_exists,
1851 temporary,
1852 name,
1853 columns,
1854 query,
1855 }))
1856 }
1857
1858 fn parse_create_trigger(&mut self, temporary: bool) -> Result<Statement, ParseError> {
1859 let if_not_exists = self.parse_if_not_exists();
1860 let name = self.parse_qualified_name()?;
1861 let timing = if self.eat_kw(&TokenKind::KwBefore) {
1862 TriggerTiming::Before
1863 } else if self.eat_kw(&TokenKind::KwAfter) {
1864 TriggerTiming::After
1865 } else if self.eat_kw(&TokenKind::KwInstead) {
1866 self.expect_kw(&TokenKind::KwOf)?;
1867 TriggerTiming::InsteadOf
1868 } else {
1869 TriggerTiming::Before };
1871 let event = if self.eat_kw(&TokenKind::KwInsert) {
1872 TriggerEvent::Insert
1873 } else if self.eat_kw(&TokenKind::KwDelete) {
1874 TriggerEvent::Delete
1875 } else {
1876 self.expect_kw(&TokenKind::KwUpdate)?;
1877 let cols = if self.eat_kw(&TokenKind::KwOf) {
1878 self.parse_comma_sep(Self::parse_identifier)?
1879 } else {
1880 vec![]
1881 };
1882 TriggerEvent::Update(cols)
1883 };
1884 self.expect_kw(&TokenKind::KwOn)?;
1885 let table = self.parse_identifier()?;
1886 let for_each_row = if self.eat_kw(&TokenKind::KwFor) {
1887 self.expect_kw(&TokenKind::KwEach)?;
1888 self.expect_kw(&TokenKind::KwRow)?;
1889 true
1890 } else {
1891 false
1892 };
1893 let when = if self.eat_kw(&TokenKind::KwWhen) {
1894 Some(self.parse_expr()?)
1895 } else {
1896 None
1897 };
1898 self.expect_kw(&TokenKind::KwBegin)?;
1899 let mut body = Vec::new();
1900 loop {
1901 if self.check_kw(&TokenKind::KwEnd) {
1902 break;
1903 }
1904 let stmt = match self.parse_statement_inner() {
1905 Ok(stmt) => stmt,
1906 Err(err) => {
1907 self.recover_trigger_body_after_error();
1908 return Err(err);
1909 }
1910 };
1911 body.push(stmt);
1912 let _ = self.eat(&TokenKind::Semicolon);
1913 }
1914 self.expect_kw(&TokenKind::KwEnd)?;
1915 Ok(Statement::CreateTrigger(CreateTriggerStatement {
1916 if_not_exists,
1917 temporary,
1918 name,
1919 timing,
1920 event,
1921 table,
1922 for_each_row,
1923 when,
1924 body,
1925 }))
1926 }
1927
1928 fn parse_create_virtual_table(&mut self) -> Result<Statement, ParseError> {
1929 let if_not_exists = self.parse_if_not_exists();
1930 let name = self.parse_qualified_name()?;
1931 self.expect_kw(&TokenKind::KwUsing)?;
1932 let module = self.parse_identifier()?;
1933 let args = if self.eat(&TokenKind::LeftParen) {
1934 if self.check(&TokenKind::RightParen) {
1935 self.advance();
1936 vec![]
1937 } else {
1938 let mut args = Vec::new();
1940 let mut depth = 0i32;
1941 let mut current_arg = String::new();
1942 loop {
1943 match self.peek() {
1944 TokenKind::RightParen if depth == 0 => {
1945 self.advance();
1946 args.push(current_arg.trim().to_owned());
1947 break;
1948 }
1949 TokenKind::LeftParen => {
1950 depth += 1;
1951 current_arg.push('(');
1952 self.advance();
1953 }
1954 TokenKind::RightParen => {
1955 depth -= 1;
1956 current_arg.push(')');
1957 self.advance();
1958 }
1959 TokenKind::Comma if depth == 0 => {
1960 args.push(current_arg.trim().to_owned());
1961 current_arg = String::new();
1962 self.advance();
1963 }
1964 TokenKind::Eof => {
1965 return Err(self.err_expected("closing parenthesis"));
1966 }
1967 _ => {
1968 let t = self
1970 .current()
1971 .ok_or_else(|| self.err_expected("virtual table argument token"))?;
1972 let text = t.kind.to_sql();
1973 if !current_arg.is_empty()
1974 && !current_arg.ends_with(' ')
1975 && !text.is_empty()
1976 {
1977 current_arg.push(' ');
1978 }
1979 current_arg.push_str(&text);
1980 self.advance();
1981 }
1982 }
1983 }
1984 args
1985 }
1986 } else {
1987 vec![]
1988 };
1989 Ok(Statement::CreateVirtualTable(CreateVirtualTableStatement {
1990 if_not_exists,
1991 name,
1992 module,
1993 args,
1994 }))
1995 }
1996
1997 fn parse_drop(&mut self) -> Result<Statement, ParseError> {
2002 self.expect_kw(&TokenKind::KwDrop)?;
2003 let object_type = if self.eat_kw(&TokenKind::KwTable) {
2004 DropObjectType::Table
2005 } else if self.eat_kw(&TokenKind::KwView) {
2006 DropObjectType::View
2007 } else if self.eat_kw(&TokenKind::KwIndex) {
2008 DropObjectType::Index
2009 } else if self.eat_kw(&TokenKind::KwTrigger) {
2010 DropObjectType::Trigger
2011 } else {
2012 return Err(self.err_expected("TABLE, VIEW, INDEX, or TRIGGER"));
2013 };
2014 let if_exists =
2015 if self.check_kw(&TokenKind::KwIf) && self.peek_nth(1) == &TokenKind::KwExists {
2016 self.advance();
2017 self.advance();
2018 true
2019 } else {
2020 false
2021 };
2022 let name = self.parse_qualified_name()?;
2023 Ok(Statement::Drop(DropStatement {
2024 object_type,
2025 if_exists,
2026 name,
2027 }))
2028 }
2029
2030 fn parse_alter(&mut self) -> Result<Statement, ParseError> {
2035 self.expect_kw(&TokenKind::KwAlter)?;
2036 self.expect_kw(&TokenKind::KwTable)?;
2037 let table = self.parse_qualified_name()?;
2038 let action = if self.eat_kw(&TokenKind::KwRename) {
2039 if self.eat_kw(&TokenKind::KwTo) {
2040 AlterTableAction::RenameTo(self.parse_identifier()?)
2041 } else {
2042 let _ = self.eat_kw(&TokenKind::KwColumn);
2043 let old = self.parse_identifier()?;
2044 self.expect_kw(&TokenKind::KwTo)?;
2045 let new = self.parse_identifier()?;
2046 AlterTableAction::RenameColumn { old, new }
2047 }
2048 } else if self.eat_kw(&TokenKind::KwAdd) {
2049 let _ = self.eat_kw(&TokenKind::KwColumn);
2050 AlterTableAction::AddColumn(self.parse_column_def()?)
2051 } else if self.eat_kw(&TokenKind::KwDrop) {
2052 let _ = self.eat_kw(&TokenKind::KwColumn);
2053 AlterTableAction::DropColumn(self.parse_identifier()?)
2054 } else {
2055 return Err(self.err_expected("RENAME, ADD, or DROP"));
2056 };
2057 Ok(Statement::AlterTable(AlterTableStatement { table, action }))
2058 }
2059
2060 fn parse_begin(&mut self) -> Result<Statement, ParseError> {
2065 self.expect_kw(&TokenKind::KwBegin)?;
2066 let mode = if self.eat_kw(&TokenKind::KwDeferred) {
2067 Some(TransactionMode::Deferred)
2068 } else if self.eat_kw(&TokenKind::KwImmediate) {
2069 Some(TransactionMode::Immediate)
2070 } else if self.eat_kw(&TokenKind::KwExclusive) {
2071 Some(TransactionMode::Exclusive)
2072 } else if self.eat_kw(&TokenKind::KwConcurrent) {
2073 Some(TransactionMode::Concurrent)
2074 } else {
2075 None
2076 };
2077 let _ = self.eat_kw(&TokenKind::KwTransaction);
2079 Ok(Statement::Begin(BeginStatement { mode }))
2080 }
2081
2082 fn parse_rollback(&mut self) -> Result<Statement, ParseError> {
2083 self.expect_kw(&TokenKind::KwRollback)?;
2084 let _ = self.eat_kw(&TokenKind::KwTransaction);
2085 let to_savepoint = if self.eat_kw(&TokenKind::KwTo) {
2086 let _ = self.eat_kw(&TokenKind::KwSavepoint);
2087 Some(self.parse_identifier()?)
2088 } else {
2089 None
2090 };
2091 Ok(Statement::Rollback(RollbackStatement { to_savepoint }))
2092 }
2093
2094 fn parse_attach(&mut self) -> Result<Statement, ParseError> {
2099 self.expect_kw(&TokenKind::KwAttach)?;
2100 let _ = self.eat_kw(&TokenKind::KwDatabase);
2101 let expr = self.parse_expr()?;
2102 self.expect_kw(&TokenKind::KwAs)?;
2103 let schema = self.parse_identifier()?;
2104 Ok(Statement::Attach(AttachStatement { expr, schema }))
2105 }
2106
2107 fn parse_pragma_value_expr(&mut self) -> Result<Expr, ParseError> {
2108 if self.check_kw(&TokenKind::KwOn) {
2112 let sp = self.current_span();
2113 self.advance();
2114 return Ok(Expr::Literal(Literal::True, sp));
2115 }
2116 let pragma_value_keyword = match self.peek() {
2124 TokenKind::KwDelete => Some("delete"),
2125 TokenKind::KwDefault => Some("default"),
2126 _ => None,
2127 };
2128 if let Some(name) = pragma_value_keyword {
2129 let sp = self.current_span();
2130 self.advance();
2131 return Ok(Expr::Column(ColumnRef::bare(name), sp));
2132 }
2133 self.parse_expr()
2134 }
2135
2136 fn parse_pragma(&mut self) -> Result<Statement, ParseError> {
2137 self.expect_kw(&TokenKind::KwPragma)?;
2138 let name = self.parse_qualified_name()?;
2139 let value = if self.eat(&TokenKind::Eq) || self.eat(&TokenKind::EqEq) {
2140 Some(PragmaValue::Assign(self.parse_pragma_value_expr()?))
2141 } else if self.eat(&TokenKind::LeftParen) {
2142 let v = self.parse_pragma_value_expr()?;
2143 self.expect_token(&TokenKind::RightParen)?;
2144 Some(PragmaValue::Call(v))
2145 } else {
2146 None
2147 };
2148 Ok(Statement::Pragma(PragmaStatement { name, value }))
2149 }
2150
2151 fn parse_vacuum(&mut self) -> Result<Statement, ParseError> {
2152 self.expect_kw(&TokenKind::KwVacuum)?;
2153 let schema = if !self.at_eof()
2154 && !self.check(&TokenKind::Semicolon)
2155 && !self.check_kw(&TokenKind::KwInto)
2156 {
2157 Some(self.parse_identifier()?)
2158 } else {
2159 None
2160 };
2161 let into = if self.eat_kw(&TokenKind::KwInto) {
2162 Some(self.parse_expr()?)
2163 } else {
2164 None
2165 };
2166 Ok(Statement::Vacuum(VacuumStatement { schema, into }))
2167 }
2168
2169 fn parse_explain(&mut self) -> Result<Statement, ParseError> {
2170 self.expect_kw(&TokenKind::KwExplain)?;
2171 let query_plan = if self.eat_kw(&TokenKind::KwQuery) {
2172 self.expect_kw(&TokenKind::KwPlan)?;
2173 true
2174 } else {
2175 false
2176 };
2177 let stmt = self.parse_statement_inner()?;
2178 Ok(Statement::Explain {
2179 query_plan,
2180 stmt: Box::new(stmt),
2181 })
2182 }
2183
2184 fn parse_window_def(&mut self) -> Result<WindowDef, ParseError> {
2189 let name = self.parse_identifier()?;
2190 self.expect_kw(&TokenKind::KwAs)?;
2191 self.expect_token(&TokenKind::LeftParen)?;
2192 let spec = self.parse_window_spec()?;
2193 self.expect_token(&TokenKind::RightParen)?;
2194 Ok(WindowDef { name, spec })
2195 }
2196
2197 pub(crate) fn parse_window_spec(&mut self) -> Result<WindowSpec, ParseError> {
2198 let has_base_window = match self.peek() {
2200 TokenKind::Id(_) | TokenKind::QuotedId(_, _) => true,
2201 k if is_nonreserved_kw(k) => !matches!(
2202 k,
2203 TokenKind::KwPartition
2204 | TokenKind::KwOrder
2205 | TokenKind::KwRange
2206 | TokenKind::KwRows
2207 | TokenKind::KwGroups
2208 ),
2209 _ => false,
2210 };
2211 let base_window = if has_base_window {
2212 Some(self.parse_identifier()?)
2213 } else {
2214 None
2215 };
2216 let partition_by = if self.eat_kw(&TokenKind::KwPartition) {
2217 self.expect_kw(&TokenKind::KwBy)?;
2218 self.parse_comma_sep(Self::parse_expr)?
2219 } else {
2220 vec![]
2221 };
2222 let order_by = if self.eat_kw(&TokenKind::KwOrder) {
2223 self.expect_kw(&TokenKind::KwBy)?;
2224 self.parse_comma_sep(Self::parse_ordering_term)?
2225 } else {
2226 vec![]
2227 };
2228 let frame = self.try_frame_spec()?;
2229 Ok(WindowSpec {
2230 base_window,
2231 partition_by,
2232 order_by,
2233 frame,
2234 })
2235 }
2236
2237 fn try_frame_spec(&mut self) -> Result<Option<FrameSpec>, ParseError> {
2238 let frame_type = if self.eat_kw(&TokenKind::KwRows) {
2239 FrameType::Rows
2240 } else if self.eat_kw(&TokenKind::KwRange) {
2241 FrameType::Range
2242 } else if self.eat_kw(&TokenKind::KwGroups) {
2243 FrameType::Groups
2244 } else {
2245 return Ok(None);
2246 };
2247 let (start, end) = if self.eat_kw(&TokenKind::KwBetween) {
2248 let s = self.parse_frame_bound()?;
2249 self.expect_kw(&TokenKind::KwAnd)?;
2250 let e = self.parse_frame_bound()?;
2251 (s, Some(e))
2252 } else {
2253 (self.parse_frame_bound()?, None)
2254 };
2255 let exclude = if self.eat_kw(&TokenKind::KwExclude) {
2256 if self.check_kw(&TokenKind::KwNo) {
2257 self.advance();
2258 let id = self.parse_identifier()?;
2260 if !id.eq_ignore_ascii_case("OTHERS") {
2261 return Err(self.err_expected("OTHERS"));
2262 }
2263 Some(FrameExclude::NoOthers)
2264 } else if self.eat_kw(&TokenKind::KwTies) {
2265 Some(FrameExclude::Ties)
2266 } else if self.eat_kw(&TokenKind::KwGroup) {
2267 Some(FrameExclude::Group)
2268 } else if matches!(self.peek(), TokenKind::Id(s) if s.eq_ignore_ascii_case("CURRENT")) {
2269 self.advance();
2270 self.expect_kw(&TokenKind::KwRow)?;
2271 Some(FrameExclude::CurrentRow)
2272 } else {
2273 return Err(
2274 self.err_expected("NO OTHERS, TIES, GROUP, or CURRENT ROW after EXCLUDE")
2275 );
2276 }
2277 } else {
2278 None
2279 };
2280 Ok(Some(FrameSpec {
2281 frame_type,
2282 start,
2283 end,
2284 exclude,
2285 }))
2286 }
2287
2288 fn parse_frame_bound(&mut self) -> Result<FrameBound, ParseError> {
2289 if self.eat_kw(&TokenKind::KwUnbounded) {
2290 if self.eat_kw(&TokenKind::KwPreceding) {
2291 Ok(FrameBound::UnboundedPreceding)
2292 } else {
2293 self.expect_kw(&TokenKind::KwFollowing)?;
2294 Ok(FrameBound::UnboundedFollowing)
2295 }
2296 } else if matches!(self.peek(), TokenKind::Id(s) if s.eq_ignore_ascii_case("CURRENT")) {
2297 self.advance();
2298 self.expect_kw(&TokenKind::KwRow)?;
2299 Ok(FrameBound::CurrentRow)
2300 } else {
2301 let expr = self.parse_expr()?;
2302 if self.eat_kw(&TokenKind::KwPreceding) {
2303 Ok(FrameBound::Preceding(Box::new(expr)))
2304 } else {
2305 self.expect_kw(&TokenKind::KwFollowing)?;
2306 Ok(FrameBound::Following(Box::new(expr)))
2307 }
2308 }
2309 }
2310}
2311
2312fn parse_statements_with_scratch_inner(
2313 sql: &str,
2314 scratch: &mut StatementParseScratch,
2315) -> (Vec<Statement>, Option<ParseError>) {
2316 Lexer::tokenize_into_with_interner(sql, &mut scratch.tokens, &mut scratch.identifier_interner);
2317 let mut parser = Parser {
2318 tokens: std::mem::take(&mut scratch.tokens),
2319 pos: 0,
2320 errors: std::mem::take(&mut scratch.errors),
2321 depth: 0,
2322 statement_class: "UNKNOWN",
2323 };
2324 let (statements, errors) = parser.parse_all();
2325 scratch.tokens = parser.tokens;
2326 scratch.tokens.clear();
2327 scratch.identifier_interner.prepare_for_next_parse();
2328 scratch.errors = errors;
2329 let first_error = scratch.errors.first().cloned();
2330 scratch.errors.clear();
2331 (statements, first_error)
2332}
2333
2334pub fn parse_statements_with_scratch(
2336 sql: &str,
2337 scratch: &mut StatementParseScratch,
2338) -> Result<Vec<Statement>, ParseError> {
2339 let (statements, first_error) = parse_statements_with_scratch_inner(sql, scratch);
2340 if let Some(error) = first_error {
2341 return Err(error);
2342 }
2343 if statements.is_empty() {
2344 return Err(ParseError::at("no SQL statement provided", None));
2345 }
2346 Ok(statements)
2347}
2348
2349pub fn parse_single_statement_with_scratch(
2351 sql: &str,
2352 scratch: &mut StatementParseScratch,
2353) -> Result<Statement, ParseError> {
2354 let statements = parse_statements_with_scratch(sql, scratch)?;
2355 let mut iter = statements.into_iter();
2356 let statement = iter
2357 .next()
2358 .ok_or_else(|| ParseError::at("no SQL statement provided", None))?;
2359 if iter.next().is_some() {
2360 return Err(ParseError::at(
2361 "multiple statements are not supported in this API path",
2362 None,
2363 ));
2364 }
2365 Ok(statement)
2366}
2367
2368pub fn parse_first_statement_with_tail(
2376 sql: &str,
2377) -> Result<Option<(Statement, usize)>, ParseError> {
2378 let mut parser = Parser::from_sql(sql);
2379
2380 while parser.eat(&TokenKind::Semicolon) {}
2381 if parser.at_eof() {
2382 return Ok(None);
2383 }
2384
2385 let statement = parser.parse_statement()?;
2386 let tail_offset = if parser.eat(&TokenKind::Semicolon) {
2387 parser
2388 .tokens
2389 .get(parser.pos.saturating_sub(1))
2390 .map_or(sql.len(), |token| token.span.end as usize)
2391 } else if parser.at_eof() {
2392 sql.len()
2393 } else {
2394 return Err(ParseError::at(
2395 "unexpected token after end of statement; expected ';' separator",
2396 parser.current(),
2397 ));
2398 };
2399
2400 Ok(Some((statement, tail_offset)))
2401}
2402
2403pub(crate) fn is_nonreserved_kw(k: &TokenKind) -> bool {
2408 matches!(
2409 k,
2410 TokenKind::KwAbort
2411 | TokenKind::KwAction
2412 | TokenKind::KwAfter
2413 | TokenKind::KwAlways
2414 | TokenKind::KwAnalyze
2415 | TokenKind::KwAsc
2416 | TokenKind::KwBefore
2417 | TokenKind::KwCascade
2418 | TokenKind::KwColumn
2419 | TokenKind::KwConcurrent
2420 | TokenKind::KwConflict
2421 | TokenKind::KwDatabase
2422 | TokenKind::KwDeferred
2423 | TokenKind::KwDesc
2424 | TokenKind::KwDo
2425 | TokenKind::KwEach
2426 | TokenKind::KwEnd
2427 | TokenKind::KwExclude
2428 | TokenKind::KwExclusive
2429 | TokenKind::KwFail
2430 | TokenKind::KwFilter
2431 | TokenKind::KwFirst
2432 | TokenKind::KwFollowing
2433 | TokenKind::KwFull
2434 | TokenKind::KwGenerated
2435 | TokenKind::KwGroups
2436 | TokenKind::KwIf
2437 | TokenKind::KwIgnore
2438 | TokenKind::KwImmediate
2439 | TokenKind::KwIndex
2440 | TokenKind::KwInitially
2441 | TokenKind::KwInstead
2442 | TokenKind::KwKey
2443 | TokenKind::KwLast
2444 | TokenKind::KwMatch
2445 | TokenKind::KwMaterialized
2446 | TokenKind::KwNo
2447 | TokenKind::KwNothing
2448 | TokenKind::KwNulls
2449 | TokenKind::KwOf
2450 | TokenKind::KwOffset
2451 | TokenKind::KwOthers
2452 | TokenKind::KwOver
2453 | TokenKind::KwPartition
2454 | TokenKind::KwPlan
2455 | TokenKind::KwPragma
2456 | TokenKind::KwPreceding
2457 | TokenKind::KwQuery
2458 | TokenKind::KwRange
2459 | TokenKind::KwRecursive
2460 | TokenKind::KwReindex
2461 | TokenKind::KwRelease
2462 | TokenKind::KwRename
2463 | TokenKind::KwReplace
2464 | TokenKind::KwRestrict
2465 | TokenKind::KwReturning
2466 | TokenKind::KwRow
2467 | TokenKind::KwRows
2468 | TokenKind::KwSavepoint
2469 | TokenKind::KwStored
2470 | TokenKind::KwStrict
2471 | TokenKind::KwTable
2472 | TokenKind::KwTemp
2473 | TokenKind::KwTemporary
2474 | TokenKind::KwTies
2475 | TokenKind::KwTransaction
2476 | TokenKind::KwTrigger
2477 | TokenKind::KwUnbounded
2478 | TokenKind::KwVacuum
2479 | TokenKind::KwView
2480 | TokenKind::KwVirtual
2481 | TokenKind::KwWindow
2482 | TokenKind::KwWithout
2483 )
2484}
2485
2486fn is_alias_terminator_kw(k: &TokenKind) -> bool {
2489 matches!(
2490 k,
2491 TokenKind::KwCross
2492 | TokenKind::KwExcept
2493 | TokenKind::KwFull
2494 | TokenKind::KwGroup
2495 | TokenKind::KwHaving
2496 | TokenKind::KwInner
2497 | TokenKind::KwIntersect
2498 | TokenKind::KwJoin
2499 | TokenKind::KwLeft
2500 | TokenKind::KwLimit
2501 | TokenKind::KwNatural
2502 | TokenKind::KwOffset
2503 | TokenKind::KwOn
2504 | TokenKind::KwOrder
2505 | TokenKind::KwOuter
2506 | TokenKind::KwReturning
2507 | TokenKind::KwRight
2508 | TokenKind::KwUnion
2509 | TokenKind::KwUsing
2510 | TokenKind::KwWhere
2511 | TokenKind::KwWindow
2512 )
2513}
2514
2515pub(crate) fn kw_to_str(k: &TokenKind) -> String {
2516 k.keyword_str()
2517 .map(|s| s.to_ascii_lowercase())
2518 .unwrap_or_else(|| {
2519 let dbg = format!("{k:?}");
2520 dbg.strip_prefix("Kw").unwrap_or(&dbg).to_ascii_lowercase()
2521 })
2522}
2523
2524#[cfg(test)]
2529mod tests {
2530 use super::*;
2531
2532 static PARSE_OBSERVABILITY_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
2533
2534 fn parse_ok(sql: &str) -> Vec<Statement> {
2535 let mut p = Parser::from_sql(sql);
2536 let (stmts, errs) = p.parse_all();
2537 assert!(errs.is_empty(), "unexpected errors: {errs:?}");
2538 stmts
2539 }
2540
2541 fn parse_one(sql: &str) -> Statement {
2542 let stmts = parse_ok(sql);
2543 assert_eq!(stmts.len(), 1, "expected 1 statement, got {}", stmts.len());
2544 stmts.into_iter().next().unwrap()
2545 }
2546
2547 fn maximum_parenthesis_depth(sql: &str) -> usize {
2548 let mut depth = 0_usize;
2549 let mut maximum = 0_usize;
2550 for byte in sql.bytes() {
2551 match byte {
2552 b'(' => {
2553 depth = depth.saturating_add(1);
2554 maximum = maximum.max(depth);
2555 }
2556 b')' => depth = depth.saturating_sub(1),
2557 _ => {}
2558 }
2559 }
2560 maximum
2561 }
2562
2563 #[test]
2564 fn ddl_boolean_chain_rendering_has_bounded_parenthesis_depth() {
2565 let predicate = (0..256)
2566 .map(|term| format!("NEW.value != {term}"))
2567 .collect::<Vec<_>>()
2568 .join(" AND ");
2569 let sql = format!(
2570 "CREATE TRIGGER trg_guard AFTER INSERT ON guarded WHEN {predicate} \
2571 BEGIN SELECT 1; END"
2572 );
2573 let rendered = parse_one(&sql).to_string();
2574 let original_depth = maximum_parenthesis_depth(&sql);
2575 let rendered_depth = maximum_parenthesis_depth(&rendered);
2576 eprintln!(
2577 "scenario=ddl_boolean_chain terms=256 original_bytes={} rendered_bytes={} \
2578 original_parenthesis_depth={original_depth} rendered_parenthesis_depth={rendered_depth}",
2579 sql.len(),
2580 rendered.len()
2581 );
2582
2583 assert!(
2584 rendered_depth <= original_depth.saturating_add(2),
2585 "flat boolean DDL must not become a physical-stack-sized parenthesis tree: \
2586 original_depth={original_depth}, rendered_depth={rendered_depth}"
2587 );
2588 }
2589
2590 #[test]
2591 fn ddl_expression_matrix_is_idempotent_without_depth_growth() {
2592 let flat_or = (0..96)
2593 .map(|term| format!("NEW.value = {term}"))
2594 .collect::<Vec<_>>()
2595 .join(" OR ");
2596 let cases = [
2597 (
2598 "flat_or_trigger_when",
2599 format!(
2600 "CREATE TRIGGER trg_flat_or AFTER INSERT ON guarded WHEN {flat_or} \
2601 BEGIN SELECT 1; END"
2602 ),
2603 ),
2604 (
2605 "mixed_and_or_trigger_when",
2606 "CREATE TRIGGER trg_mixed AFTER INSERT ON guarded \
2607 WHEN NEW.a = 1 OR NEW.b = 2 AND NEW.c = 3 OR NEW.d = 4 \
2608 BEGIN SELECT 1; END"
2609 .to_owned(),
2610 ),
2611 (
2612 "nested_not_trigger_when",
2613 "CREATE TRIGGER trg_not AFTER INSERT ON guarded \
2614 WHEN NOT (NOT (NEW.a = 1)) AND NEW.b = 2 \
2615 BEGIN SELECT 1; END"
2616 .to_owned(),
2617 ),
2618 (
2619 "collate_trigger_when",
2620 "CREATE TRIGGER trg_collate AFTER INSERT ON guarded \
2621 WHEN NEW.name COLLATE NOCASE = 'alpha' AND NEW.a = 1 \
2622 BEGIN SELECT 1; END"
2623 .to_owned(),
2624 ),
2625 (
2626 "between_trigger_when",
2627 "CREATE TRIGGER trg_between AFTER INSERT ON guarded \
2628 WHEN NEW.a BETWEEN 1 AND 3 AND NEW.b NOT BETWEEN 4 AND 6 \
2629 BEGIN SELECT 1; END"
2630 .to_owned(),
2631 ),
2632 (
2633 "case_trigger_when",
2634 "CREATE TRIGGER trg_case AFTER INSERT ON guarded \
2635 WHEN CASE WHEN NEW.a = 1 THEN 1 ELSE 0 END = 1 AND NEW.b = 2 \
2636 BEGIN SELECT 1; END"
2637 .to_owned(),
2638 ),
2639 (
2640 "scalar_subquery_trigger_when",
2641 "CREATE TRIGGER trg_scalar AFTER INSERT ON guarded \
2642 WHEN (SELECT COUNT(*) FROM lookup) > 0 AND NEW.a = 1 \
2643 BEGIN SELECT 1; END"
2644 .to_owned(),
2645 ),
2646 (
2647 "exists_trigger_when",
2648 "CREATE TRIGGER trg_exists AFTER INSERT ON guarded \
2649 WHEN EXISTS (SELECT 1 FROM lookup WHERE lookup.id = NEW.a) AND NEW.b = 2 \
2650 BEGIN SELECT 1; END"
2651 .to_owned(),
2652 ),
2653 (
2654 "check_constraint",
2655 "CREATE TABLE check_guard (\
2656 a INTEGER, b INTEGER, \
2657 CHECK (a BETWEEN 1 AND 9 AND NOT (b = 4 OR b = 5))\
2658 )"
2659 .to_owned(),
2660 ),
2661 (
2662 "partial_index",
2663 "CREATE INDEX idx_partial_guard ON guarded(a) \
2664 WHERE a COLLATE BINARY = 1 OR b BETWEEN 2 AND 8"
2665 .to_owned(),
2666 ),
2667 (
2668 "generated_column",
2669 "CREATE TABLE generated_guard (\
2670 a INTEGER, b INTEGER, \
2671 c INTEGER GENERATED ALWAYS AS (\
2672 CASE WHEN a = 1 AND b = 2 THEN 1 ELSE 0 END\
2673 ) STORED\
2674 )"
2675 .to_owned(),
2676 ),
2677 (
2678 "view_predicate",
2679 "CREATE VIEW guarded_view AS \
2680 SELECT a, CASE WHEN a BETWEEN 1 AND 3 THEN 'inside' ELSE 'outside' END AS bucket \
2681 FROM guarded WHERE a = 1 OR b = 2 AND c = 3"
2682 .to_owned(),
2683 ),
2684 ];
2685
2686 for (case, sql) in cases {
2687 let parsed_once = parse_one(&sql);
2688 let rendered_once = parsed_once.to_string();
2689 let parsed_twice = parse_one(&rendered_once);
2690 let rendered_twice = parsed_twice.to_string();
2691 let first_depth = maximum_parenthesis_depth(&rendered_once);
2692 let second_depth = maximum_parenthesis_depth(&rendered_twice);
2693 eprintln!(
2694 "scenario=ddl_expression_matrix case={case} original_bytes={} \
2695 rendered_bytes={} replay_bytes={} rendered_parenthesis_depth={first_depth} \
2696 replay_parenthesis_depth={second_depth}",
2697 sql.len(),
2698 rendered_once.len(),
2699 rendered_twice.len()
2700 );
2701
2702 assert_eq!(
2703 rendered_twice, rendered_once,
2704 "DDL rendering must reach a byte-stable fixed point after one parse: case={case}"
2705 );
2706 assert_eq!(
2707 parsed_twice, parsed_once,
2708 "DDL rendering must preserve the parsed semantics: case={case}"
2709 );
2710 assert_eq!(
2711 second_depth, first_depth,
2712 "DDL rendering must not grow parenthesis depth on replay: case={case}"
2713 );
2714 }
2715 }
2716
2717 #[test]
2718 fn test_parse_metrics_emitted_when_enabled() {
2719 let _guard = PARSE_OBSERVABILITY_LOCK
2720 .lock()
2721 .unwrap_or_else(|poisoned| poisoned.into_inner());
2722 let prev_metrics_enabled = parse_metrics_enabled();
2723 reset_parse_metrics();
2724 set_parse_metrics_enabled(true);
2725
2726 let mut parser = Parser::from_sql("SELECT 1; SELECT 2;");
2727 let (stmts, errs) = parser.parse_all();
2728 assert!(errs.is_empty(), "unexpected errors: {errs:?}");
2729 assert_eq!(stmts.len(), 2);
2730
2731 let snapshot = parse_metrics_snapshot();
2732 assert!(snapshot.fsqlite_parse_statements_total >= 2);
2733
2734 set_parse_metrics_enabled(prev_metrics_enabled);
2735 reset_parse_metrics();
2736 }
2737
2738 #[test]
2739 fn test_parse_metrics_can_be_disabled_off_hot_path() {
2740 let _guard = PARSE_OBSERVABILITY_LOCK
2741 .lock()
2742 .unwrap_or_else(|poisoned| poisoned.into_inner());
2743 let prev_metrics_enabled = parse_metrics_enabled();
2744 reset_parse_metrics();
2745 set_parse_metrics_enabled(false);
2746
2747 let mut parser = Parser::from_sql("SELECT 1; SELECT 2;");
2748 let (stmts, errs) = parser.parse_all();
2749 assert!(errs.is_empty(), "unexpected errors: {errs:?}");
2750 assert_eq!(stmts.len(), 2);
2751
2752 let snapshot = parse_metrics_snapshot();
2753 assert_eq!(snapshot.fsqlite_parse_statements_total, 0);
2754
2755 set_parse_metrics_enabled(prev_metrics_enabled);
2756 reset_parse_metrics();
2757 }
2758
2759 #[test]
2760 fn test_parse_depth_overflow_does_not_poison_following_statement() {
2761 let mut parser = Parser::from_sql("SELECT 1; SELECT 42;");
2762 parser.depth = MAX_PARSE_DEPTH - 1;
2763
2764 let first = parser.parse_statement();
2767 assert!(first.is_err(), "first statement should hit depth guard");
2768 assert_eq!(
2769 parser.depth,
2770 MAX_PARSE_DEPTH - 1,
2771 "depth must not leak upward on recursion-limit error"
2772 );
2773
2774 let _ = parser.eat(&TokenKind::Semicolon);
2778 let second = parser.parse_statement();
2779 assert!(
2780 second.is_err(),
2781 "second statement should still be parseable"
2782 );
2783 assert_eq!(
2784 parser.depth,
2785 MAX_PARSE_DEPTH - 1,
2786 "depth must remain stable across repeated recursion-limit errors"
2787 );
2788 }
2789
2790 #[test]
2791 fn deeply_nested_trigger_returns_typed_limit_on_two_mib_stack() {
2792 const STACK_BYTES: usize = 2 * 1024 * 1024;
2793 let handle = std::thread::Builder::new()
2794 .name("parser-depth-2mib".to_owned())
2795 .stack_size(STACK_BYTES)
2796 .spawn(|| {
2797 let admitted_nesting = 64_usize;
2798 let admitted_sql = format!(
2799 "CREATE TRIGGER trg_admitted AFTER INSERT ON guarded WHEN {}1{} \
2800 BEGIN SELECT 1; END",
2801 "(".repeat(admitted_nesting),
2802 ")".repeat(admitted_nesting)
2803 );
2804 let mut admitted_scratch = StatementParseScratch::default();
2805 parse_single_statement_with_scratch(&admitted_sql, &mut admitted_scratch)
2806 .expect("a practical nested expression must remain supported");
2807
2808 let nesting = usize::try_from(MAX_PARSE_DEPTH).unwrap_or(usize::MAX) + 64;
2809 let sql = format!(
2810 "CREATE TRIGGER trg_depth AFTER INSERT ON guarded WHEN {}1{} \
2811 BEGIN SELECT 1; END",
2812 "(".repeat(nesting),
2813 ")".repeat(nesting)
2814 );
2815 let mut scratch = StatementParseScratch::default();
2816 let error = parse_single_statement_with_scratch(&sql, &mut scratch)
2817 .expect_err("over-limit DDL must return a typed parse error");
2818 eprintln!(
2819 "scenario=parser_recursion_limit stack_bytes={STACK_BYTES} \
2820 admitted_depth={admitted_nesting} requested_depth={nesting} \
2821 error_kind={:?} error={}",
2822 error.kind, error
2823 );
2824 assert_eq!(error.kind, ParseErrorKind::Limit);
2825 assert!(
2826 error.message.contains("statement=CREATE TRIGGER")
2827 && error.message.contains(&format!("limit={MAX_PARSE_DEPTH}")),
2828 "limit error must identify the statement class and configured depth: {error}"
2829 );
2830 })
2831 .expect("spawn fixed-stack parser test");
2832
2833 handle
2834 .join()
2835 .expect("typed recursion refusal must not panic or abort");
2836 }
2837
2838 #[test]
2839 fn test_parse_first_statement_with_tail_consumes_full_trigger_body() {
2840 let sql = "CREATE TRIGGER trg AFTER INSERT ON t BEGIN INSERT INTO audit VALUES('first'); INSERT INTO audit VALUES('second'); END; SELECT 1;";
2841 let Some((statement, tail_offset)) =
2842 parse_first_statement_with_tail(sql).expect("trigger statement should parse")
2843 else {
2844 panic!("expected a trigger statement");
2845 };
2846
2847 assert!(matches!(statement, Statement::CreateTrigger(_)));
2848 assert_eq!(&sql[tail_offset..], " SELECT 1;");
2849 }
2850
2851 #[test]
2852 fn test_parse_first_statement_with_tail_rejects_adjacent_statements_without_separator() {
2853 let error = parse_first_statement_with_tail("SELECT 1 SELECT 2")
2854 .expect_err("adjacent statements without a semicolon must be rejected");
2855
2856 assert!(
2857 error.message.contains("expected ';' separator"),
2858 "unexpected error: {error:?}"
2859 );
2860 }
2861
2862 #[test]
2863 fn test_create_table_without_rowid_and_strict_round_trips_display() {
2864 let sql = "CREATE TABLE s (id INTEGER PRIMARY KEY) WITHOUT ROWID, STRICT";
2865 let Some((statement, _)) =
2866 parse_first_statement_with_tail(sql).expect("statement should parse")
2867 else {
2868 panic!("expected CREATE TABLE statement");
2869 };
2870
2871 assert_eq!(statement.to_string(), sql);
2872 }
2873
2874 #[test]
2875 fn test_error_recovery_does_not_fabricate_top_level_statements_from_trigger_body() {
2876 let mut parser = Parser::from_sql(
2877 "CREATE TRIGGER trg AFTER INSERT ON t BEGIN XYZZY; SELECT 1; END; SELECT 2;",
2878 );
2879 let (stmts, errs) = parser.parse_all();
2880
2881 assert_eq!(errs.len(), 1, "expected one trigger-body parse error");
2882 assert_eq!(
2883 stmts.len(),
2884 1,
2885 "only the trailing top-level SELECT should remain"
2886 );
2887 assert!(
2888 matches!(
2889 &stmts[0],
2890 Statement::Select(select)
2891 if matches!(
2892 &select.body.select,
2893 SelectCore::Select { columns, .. }
2894 if matches!(
2895 columns.as_slice(),
2896 [ResultColumn::Expr {
2897 expr: Expr::Literal(Literal::Integer(2), _),
2898 alias: None,
2899 }]
2900 )
2901 )
2902 ),
2903 "parser must skip the malformed trigger instead of reinterpreting body tokens as top-level SQL: {stmts:?}"
2904 );
2905 }
2906
2907 #[test]
2908 fn test_error_recovery_recovers_values_statement_after_garbage() {
2909 let mut parser = Parser::from_sql("XYZZY VALUES (1);");
2910 let (stmts, errs) = parser.parse_all();
2911
2912 assert_eq!(errs.len(), 1, "expected one error for leading garbage");
2913 assert_eq!(stmts.len(), 1, "VALUES statement should still be recovered");
2914 assert!(matches!(stmts[0], Statement::Select(_)));
2915 }
2916
2917 #[test]
2918 fn test_error_recovery_does_not_swallow_top_level_sql_after_unbalanced_trigger_paren() {
2919 let mut parser = Parser::from_sql(
2920 "CREATE TRIGGER trg AFTER INSERT ON t BEGIN SELECT (1; END; SELECT 2;",
2921 );
2922 let (stmts, errs) = parser.parse_all();
2923
2924 assert_eq!(errs.len(), 1, "expected one trigger-body parse error");
2925 assert_eq!(
2926 stmts.len(),
2927 1,
2928 "malformed trigger recovery must still preserve the trailing top-level SELECT"
2929 );
2930 assert!(
2931 matches!(
2932 &stmts[0],
2933 Statement::Select(select)
2934 if matches!(
2935 &select.body.select,
2936 SelectCore::Select { columns, .. }
2937 if matches!(
2938 columns.as_slice(),
2939 [ResultColumn::Expr {
2940 expr: Expr::Literal(Literal::Integer(2), _),
2941 alias: None,
2942 }]
2943 )
2944 )
2945 ),
2946 "parser must stop at the trigger END even when parentheses are left unbalanced: {stmts:?}"
2947 );
2948 }
2949
2950 #[test]
2951 fn select_literal() {
2952 let stmt = parse_one("SELECT 1");
2953 assert!(matches!(stmt, Statement::Select(_)));
2954 }
2955
2956 #[test]
2957 fn select_star_from() {
2958 let stmt = parse_one("SELECT * FROM t");
2959 if let Statement::Select(s) = stmt {
2960 if let SelectCore::Select { columns, from, .. } = &s.body.select {
2961 assert!(matches!(columns[0], ResultColumn::Star));
2962 assert!(from.is_some());
2963 } else {
2964 unreachable!("expected Select core");
2965 }
2966 } else {
2967 unreachable!("expected Select");
2968 }
2969 }
2970
2971 #[test]
2972 fn select_where_order_limit() {
2973 let stmt = parse_one("SELECT a FROM t WHERE a > 1 ORDER BY a LIMIT 10 OFFSET 5");
2974 if let Statement::Select(s) = stmt {
2975 assert!(s.limit.is_some());
2976 assert_eq!(s.order_by.len(), 1);
2977 } else {
2978 unreachable!("expected Select");
2979 }
2980 }
2981
2982 #[test]
2983 fn select_limit_comma_syntax_uses_offset_then_count() {
2984 let stmt = parse_one("SELECT a FROM t LIMIT 5, 10");
2985 if let Statement::Select(s) = stmt {
2986 let limit = s.limit.expect("LIMIT clause");
2987 assert!(matches!(
2988 limit.limit,
2989 Expr::Literal(Literal::Integer(10), _)
2990 ));
2991 assert!(matches!(
2992 limit.offset,
2993 Some(Expr::Literal(Literal::Integer(5), _))
2994 ));
2995 } else {
2996 unreachable!("expected Select");
2997 }
2998 }
2999
3000 #[test]
3001 fn select_order_by_nulls_first_last() {
3002 let stmt = parse_one("SELECT a FROM t ORDER BY a ASC NULLS FIRST, b DESC NULLS LAST");
3003 if let Statement::Select(s) = stmt {
3004 assert_eq!(s.order_by.len(), 2);
3005 assert_eq!(s.order_by[0].direction, Some(SortDirection::Asc));
3006 assert_eq!(s.order_by[0].nulls, Some(NullsOrder::First));
3007 assert_eq!(s.order_by[1].direction, Some(SortDirection::Desc));
3008 assert_eq!(s.order_by[1].nulls, Some(NullsOrder::Last));
3009 } else {
3010 unreachable!("expected Select");
3011 }
3012 }
3013
3014 #[test]
3015 fn select_from_indexed_by_hint() {
3016 let stmt = parse_one("SELECT * FROM t INDEXED BY idx_t");
3017 if let Statement::Select(s) = stmt {
3018 if let SelectCore::Select { from, .. } = &s.body.select {
3019 let from = from.as_ref().expect("FROM clause");
3020 match &from.source {
3021 TableOrSubquery::Table {
3022 index_hint: Some(IndexHint::IndexedBy(name)),
3023 ..
3024 } => assert_eq!(name, "idx_t"),
3025 other => unreachable!("expected indexed table source, got {other:?}"),
3026 }
3027 } else {
3028 unreachable!("expected Select core");
3029 }
3030 } else {
3031 unreachable!("expected Select");
3032 }
3033 }
3034
3035 #[test]
3036 fn select_from_not_indexed_hint() {
3037 let stmt = parse_one("SELECT * FROM t NOT INDEXED");
3038 if let Statement::Select(s) = stmt {
3039 if let SelectCore::Select { from, .. } = &s.body.select {
3040 let from = from.as_ref().expect("FROM clause");
3041 match &from.source {
3042 TableOrSubquery::Table {
3043 index_hint: Some(IndexHint::NotIndexed),
3044 ..
3045 } => {}
3046 other => unreachable!("expected not-indexed table source, got {other:?}"),
3047 }
3048 } else {
3049 unreachable!("expected Select core");
3050 }
3051 } else {
3052 unreachable!("expected Select");
3053 }
3054 }
3055
3056 #[test]
3057 fn select_from_table_valued_function() {
3058 let stmt = parse_one("SELECT * FROM generate_series(1, 100) AS gs");
3059 if let Statement::Select(s) = stmt {
3060 if let SelectCore::Select { from, .. } = &s.body.select {
3061 let from = from.as_ref().expect("FROM clause");
3062 match &from.source {
3063 TableOrSubquery::TableFunction { name, args, alias } => {
3064 assert_eq!(name, "generate_series");
3065 assert_eq!(args.len(), 2);
3066 assert_eq!(alias.as_deref(), Some("gs"));
3067 }
3068 other => unreachable!("expected table-valued function source, got {other:?}"),
3069 }
3070 } else {
3071 unreachable!("expected Select core");
3072 }
3073 } else {
3074 unreachable!("expected Select");
3075 }
3076 }
3077
3078 #[test]
3079 fn select_window_function_over_clause() {
3080 let stmt = parse_one(
3081 "SELECT sum(x) OVER (PARTITION BY y ORDER BY z \
3082 ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) FROM t",
3083 );
3084 if let Statement::Select(s) = stmt {
3085 if let SelectCore::Select { columns, .. } = &s.body.select {
3086 match &columns[0] {
3087 ResultColumn::Expr {
3088 expr:
3089 Expr::FunctionCall {
3090 over: Some(over), ..
3091 },
3092 ..
3093 } => {
3094 assert_eq!(over.partition_by.len(), 1);
3095 assert_eq!(over.order_by.len(), 1);
3096 assert!(matches!(
3097 over.frame,
3098 Some(FrameSpec {
3099 frame_type: FrameType::Rows,
3100 ..
3101 })
3102 ));
3103 }
3104 other => unreachable!("expected window function result column, got {other:?}"),
3105 }
3106 } else {
3107 unreachable!("expected Select core");
3108 }
3109 } else {
3110 unreachable!("expected Select");
3111 }
3112 }
3113
3114 #[test]
3115 fn select_named_window_definition_and_reference() {
3116 let stmt = parse_one(
3117 "SELECT sum(x) OVER win FROM t \
3118 WINDOW win AS (PARTITION BY y ORDER BY z)",
3119 );
3120 if let Statement::Select(s) = stmt {
3121 if let SelectCore::Select {
3122 columns, windows, ..
3123 } = &s.body.select
3124 {
3125 assert_eq!(windows.len(), 1);
3126 assert_eq!(windows[0].name, "win");
3127 assert_eq!(windows[0].spec.partition_by.len(), 1);
3128 assert_eq!(windows[0].spec.order_by.len(), 1);
3129 match &columns[0] {
3130 ResultColumn::Expr {
3131 expr:
3132 Expr::FunctionCall {
3133 over: Some(over), ..
3134 },
3135 ..
3136 } => assert_eq!(over.base_window.as_deref(), Some("win")),
3137 other => unreachable!("expected named window function, got {other:?}"),
3138 }
3139 } else {
3140 unreachable!("expected Select core");
3141 }
3142 } else {
3143 unreachable!("expected Select");
3144 }
3145 }
3146
3147 #[test]
3148 fn insert_values() {
3149 let stmt = parse_one("INSERT INTO t (a, b) VALUES (1, 2), (3, 4)");
3150 assert!(matches!(stmt, Statement::Insert(_)));
3151 }
3152
3153 #[test]
3154 fn update_set() {
3155 let stmt = parse_one("UPDATE t SET a = 1, b = 2 WHERE id = 3");
3156 assert!(matches!(stmt, Statement::Update(_)));
3157 }
3158
3159 #[test]
3160 fn delete_from() {
3161 let stmt = parse_one("DELETE FROM t WHERE id = 1");
3162 assert!(matches!(stmt, Statement::Delete(_)));
3163 }
3164
3165 #[test]
3166 fn create_table_basic() {
3167 let stmt = parse_one("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT NOT NULL)");
3168 if let Statement::CreateTable(ct) = stmt {
3169 assert_eq!(ct.name.name, "t");
3170 if let CreateTableBody::Columns { columns, .. } = ct.body {
3171 assert_eq!(columns.len(), 2);
3172 } else {
3173 unreachable!("expected column defs");
3174 }
3175 } else {
3176 unreachable!("expected CreateTable");
3177 }
3178 }
3179
3180 #[test]
3181 fn create_index() {
3182 let stmt = parse_one("CREATE UNIQUE INDEX idx ON t (a, b DESC)");
3183 if let Statement::CreateIndex(ci) = stmt {
3184 assert!(ci.unique);
3185 assert_eq!(ci.columns.len(), 2);
3186 } else {
3187 unreachable!("expected CreateIndex");
3188 }
3189 }
3190
3191 #[test]
3192 fn drop_table_if_exists() {
3193 let stmt = parse_one("DROP TABLE IF EXISTS t");
3194 if let Statement::Drop(d) = stmt {
3195 assert!(d.if_exists);
3196 assert_eq!(d.object_type, DropObjectType::Table);
3197 } else {
3198 unreachable!("expected Drop");
3199 }
3200 }
3201
3202 #[test]
3203 fn begin_commit() {
3204 let stmts = parse_ok("BEGIN IMMEDIATE; COMMIT");
3205 assert_eq!(stmts.len(), 2);
3206 if let Statement::Begin(b) = &stmts[0] {
3207 assert_eq!(b.mode, Some(TransactionMode::Immediate));
3208 } else {
3209 unreachable!("expected Begin");
3210 }
3211 assert!(matches!(stmts[1], Statement::Commit));
3212 }
3213
3214 #[test]
3215 fn begin_concurrent() {
3216 let stmt = parse_one("BEGIN CONCURRENT");
3217 if let Statement::Begin(b) = stmt {
3218 assert_eq!(b.mode, Some(TransactionMode::Concurrent));
3219 } else {
3220 unreachable!("expected Begin");
3221 }
3222 }
3223
3224 #[test]
3225 fn rollback_to_savepoint() {
3226 let stmt = parse_one("ROLLBACK TO SAVEPOINT sp1");
3227 if let Statement::Rollback(r) = stmt {
3228 assert_eq!(r.to_savepoint.as_deref(), Some("sp1"));
3229 } else {
3230 unreachable!("expected Rollback");
3231 }
3232 }
3233
3234 #[test]
3235 fn explain_query_plan() {
3236 let stmt = parse_one("EXPLAIN QUERY PLAN SELECT 1");
3237 assert!(matches!(
3238 stmt,
3239 Statement::Explain {
3240 query_plan: true,
3241 ..
3242 }
3243 ));
3244 }
3245
3246 #[test]
3247 fn pragma() {
3248 let stmt = parse_one("PRAGMA journal_mode = WAL");
3249 assert!(matches!(stmt, Statement::Pragma(_)));
3250 }
3251
3252 #[test]
3253 fn pragma_allows_on_value() {
3254 let stmt = parse_one("PRAGMA fsqlite.serializable = ON");
3255 assert!(matches!(stmt, Statement::Pragma(_)));
3256 }
3257
3258 #[test]
3259 fn pragma_allows_delete_and_default_keyword_values() {
3260 for (sql, expected) in [
3264 ("PRAGMA journal_mode = DELETE", "delete"),
3265 ("PRAGMA temp_store = DEFAULT", "default"),
3266 ] {
3267 let Statement::Pragma(p) = parse_one(sql) else {
3268 unreachable!("expected Pragma for {sql}");
3269 };
3270 match p.value {
3271 Some(PragmaValue::Assign(Expr::Column(col, _))) => {
3272 assert!(col.table.is_none(), "sql={sql}");
3273 assert_eq!(&*col.column, expected, "sql={sql}");
3274 }
3275 other => unreachable!("expected Assign(Column) for {sql}, got {other:?}"),
3276 }
3277 }
3278 }
3279
3280 #[test]
3281 fn error_recovery_multiple_statements() {
3282 let mut p = Parser::from_sql("SELECT 1; XYZZY; SELECT 2");
3283 let (stmts, errs) = p.parse_all();
3284 assert_eq!(stmts.len(), 2, "should recover: stmts={stmts:?}");
3285 assert!(!errs.is_empty());
3286 }
3287
3288 #[test]
3289 fn compound_union() {
3290 let stmt = parse_one("SELECT 1 UNION ALL SELECT 2");
3291 if let Statement::Select(s) = stmt {
3292 assert_eq!(s.body.compounds.len(), 1);
3293 } else {
3294 unreachable!("expected Select");
3295 }
3296 }
3297
3298 #[test]
3299 fn alter_table_rename() {
3300 let stmt = parse_one("ALTER TABLE t RENAME TO t2");
3301 assert!(matches!(
3302 stmt,
3303 Statement::AlterTable(AlterTableStatement {
3304 action: AlterTableAction::RenameTo(_),
3305 ..
3306 })
3307 ));
3308 }
3309
3310 #[test]
3315 fn test_parser_join_inner() {
3316 let stmt = parse_one("SELECT * FROM a INNER JOIN b ON a.id = b.a_id");
3317 if let Statement::Select(s) = stmt {
3318 if let SelectCore::Select { from, .. } = &s.body.select {
3319 let from = from.as_ref().expect("FROM clause");
3320 assert!(!from.joins.is_empty());
3321 assert_eq!(from.joins[0].join_type.kind, JoinKind::Inner);
3322 } else {
3323 unreachable!("expected Select core");
3324 }
3325 } else {
3326 unreachable!("expected Select");
3327 }
3328 }
3329
3330 #[test]
3331 fn test_parser_join_left() {
3332 let stmt = parse_one("SELECT * FROM a LEFT JOIN b ON a.id = b.a_id");
3333 if let Statement::Select(s) = stmt {
3334 if let SelectCore::Select { from, .. } = &s.body.select {
3335 let from = from.as_ref().expect("FROM clause");
3336 assert_eq!(from.joins[0].join_type.kind, JoinKind::Left);
3337 } else {
3338 unreachable!("expected Select core");
3339 }
3340 } else {
3341 unreachable!("expected Select");
3342 }
3343 }
3344
3345 #[test]
3346 fn test_parser_join_left_outer() {
3347 let stmt = parse_one("SELECT * FROM a LEFT OUTER JOIN b ON a.id = b.a_id");
3348 if let Statement::Select(s) = stmt {
3349 if let SelectCore::Select { from, .. } = &s.body.select {
3350 let from = from.as_ref().expect("FROM clause");
3351 assert_eq!(from.joins[0].join_type.kind, JoinKind::Left);
3352 } else {
3353 unreachable!("expected Select core");
3354 }
3355 } else {
3356 unreachable!("expected Select");
3357 }
3358 }
3359
3360 #[test]
3361 fn test_parser_join_right() {
3362 let stmt = parse_one("SELECT * FROM a RIGHT JOIN b ON a.id = b.a_id");
3363 if let Statement::Select(s) = stmt {
3364 if let SelectCore::Select { from, .. } = &s.body.select {
3365 let from = from.as_ref().expect("FROM clause");
3366 assert_eq!(from.joins[0].join_type.kind, JoinKind::Right);
3367 } else {
3368 unreachable!("expected Select core");
3369 }
3370 } else {
3371 unreachable!("expected Select");
3372 }
3373 }
3374
3375 #[test]
3376 fn test_parser_join_full() {
3377 let stmt = parse_one("SELECT * FROM a FULL OUTER JOIN b ON a.id = b.a_id");
3378 if let Statement::Select(s) = stmt {
3379 if let SelectCore::Select { from, .. } = &s.body.select {
3380 let from = from.as_ref().expect("FROM clause");
3381 assert_eq!(from.joins[0].join_type.kind, JoinKind::Full);
3382 } else {
3383 unreachable!("expected Select core");
3384 }
3385 } else {
3386 unreachable!("expected Select");
3387 }
3388 }
3389
3390 #[test]
3391 fn test_parser_join_full_outer_with_semicolon() {
3392 let stmt = parse_one("SELECT l.name, r.tag FROM l FULL OUTER JOIN r ON l.id = r.l_id;");
3393 if let Statement::Select(s) = stmt {
3394 if let SelectCore::Select { from, .. } = &s.body.select {
3395 let from = from.as_ref().expect("FROM clause");
3396 assert_eq!(from.joins.len(), 1);
3397 assert_eq!(from.joins[0].join_type.kind, JoinKind::Full);
3398 } else {
3399 unreachable!("expected Select core");
3400 }
3401 } else {
3402 unreachable!("expected Select");
3403 }
3404 }
3405
3406 #[test]
3407 fn test_parser_join_cross() {
3408 let stmt = parse_one("SELECT * FROM a CROSS JOIN b");
3409 if let Statement::Select(s) = stmt {
3410 if let SelectCore::Select { from, .. } = &s.body.select {
3411 let from = from.as_ref().expect("FROM clause");
3412 assert_eq!(from.joins[0].join_type.kind, JoinKind::Cross);
3413 } else {
3414 unreachable!("expected Select core");
3415 }
3416 } else {
3417 unreachable!("expected Select");
3418 }
3419 }
3420
3421 #[test]
3422 fn test_parser_join_natural() {
3423 let stmt = parse_one("SELECT * FROM a NATURAL JOIN b");
3424 if let Statement::Select(s) = stmt {
3425 if let SelectCore::Select { from, .. } = &s.body.select {
3426 let from = from.as_ref().expect("FROM clause");
3427 assert!(from.joins[0].join_type.natural);
3428 } else {
3429 unreachable!("expected Select core");
3430 }
3431 } else {
3432 unreachable!("expected Select");
3433 }
3434 }
3435
3436 #[test]
3437 fn test_parser_join_using() {
3438 let stmt = parse_one("SELECT * FROM a JOIN b USING (id)");
3439 if let Statement::Select(s) = stmt {
3440 if let SelectCore::Select { from, .. } = &s.body.select {
3441 let from = from.as_ref().expect("FROM clause");
3442 assert!(matches!(
3443 from.joins[0].constraint,
3444 Some(JoinConstraint::Using(_))
3445 ));
3446 } else {
3447 unreachable!("expected Select core");
3448 }
3449 } else {
3450 unreachable!("expected Select");
3451 }
3452 }
3453
3454 #[test]
3455 fn test_parser_join_comma() {
3456 let stmt = parse_one("SELECT * FROM a, b WHERE a.id = b.a_id");
3458 if let Statement::Select(s) = stmt {
3459 if let SelectCore::Select { from, .. } = &s.body.select {
3460 let from = from.as_ref().expect("FROM clause");
3461 assert!(!from.joins.is_empty());
3462 assert_eq!(from.joins[0].join_type.kind, JoinKind::Cross);
3463 } else {
3464 unreachable!("expected Select core");
3465 }
3466 } else {
3467 unreachable!("expected Select");
3468 }
3469 }
3470
3471 #[test]
3476 fn test_parser_cte_basic() {
3477 let stmt = parse_one("WITH cte AS (SELECT 1 AS x) SELECT * FROM cte");
3478 if let Statement::Select(s) = stmt {
3479 let with = s.with.as_ref().expect("WITH clause");
3480 assert!(!with.recursive);
3481 assert_eq!(with.ctes.len(), 1);
3482 assert_eq!(with.ctes[0].name, "cte");
3483 } else {
3484 unreachable!("expected Select");
3485 }
3486 }
3487
3488 #[test]
3489 fn test_parser_cte_multiple() {
3490 let stmt = parse_one("WITH a AS (SELECT 1), b AS (SELECT 2) SELECT * FROM a, b");
3491 if let Statement::Select(s) = stmt {
3492 let with = s.with.as_ref().expect("WITH clause");
3493 assert_eq!(with.ctes.len(), 2);
3494 assert_eq!(with.ctes[0].name, "a");
3495 assert_eq!(with.ctes[1].name, "b");
3496 } else {
3497 unreachable!("expected Select");
3498 }
3499 }
3500
3501 #[test]
3502 fn test_parser_cte_recursive() {
3503 let stmt = parse_one(
3504 "WITH RECURSIVE cnt(x) AS (\
3505 SELECT 1 UNION ALL SELECT x+1 FROM cnt WHERE x<10\
3506 ) SELECT x FROM cnt",
3507 );
3508 if let Statement::Select(s) = stmt {
3509 let with = s.with.as_ref().expect("WITH clause");
3510 assert!(with.recursive);
3511 assert_eq!(with.ctes[0].name, "cnt");
3512 assert_eq!(with.ctes[0].columns, vec!["x".to_owned()]);
3513 } else {
3514 unreachable!("expected Select");
3515 }
3516 }
3517
3518 #[test]
3519 fn test_parser_cte_materialized() {
3520 let stmt = parse_one("WITH cte AS MATERIALIZED (SELECT 1) SELECT * FROM cte");
3521 if let Statement::Select(s) = stmt {
3522 let with = s.with.as_ref().expect("WITH clause");
3523 assert_eq!(
3524 with.ctes[0].materialized,
3525 Some(CteMaterialized::Materialized)
3526 );
3527 } else {
3528 unreachable!("expected Select");
3529 }
3530 }
3531
3532 #[test]
3537 fn test_select_table_star() {
3538 let stmt = parse_one("SELECT t1.* FROM t1, t2");
3539 if let Statement::Select(s) = stmt {
3540 if let SelectCore::Select { columns, .. } = &s.body.select {
3541 assert!(
3542 matches!(&columns[0], ResultColumn::TableStar(t) if t == &QualifiedName::bare("t1")),
3543 "expected TableStar(t1), got {:?}",
3544 columns[0]
3545 );
3546 } else {
3547 unreachable!("expected Select core");
3548 }
3549 } else {
3550 unreachable!("expected Select");
3551 }
3552 }
3553
3554 #[test]
3555 fn test_select_schema_table_star() {
3556 let stmt = parse_one("SELECT aux.t1.* FROM aux.t1");
3557 if let Statement::Select(s) = stmt {
3558 if let SelectCore::Select { columns, .. } = &s.body.select {
3559 assert!(
3560 matches!(&columns[0], ResultColumn::TableStar(t) if t == &QualifiedName::qualified("aux", "t1")),
3561 "expected TableStar(aux.t1), got {:?}",
3562 columns[0]
3563 );
3564 } else {
3565 unreachable!("expected Select core");
3566 }
3567 } else {
3568 unreachable!("expected Select");
3569 }
3570 }
3571
3572 #[test]
3573 fn test_select_expr_alias() {
3574 let stmt = parse_one("SELECT x + 1 AS result FROM t");
3575 if let Statement::Select(s) = stmt {
3576 if let SelectCore::Select { columns, .. } = &s.body.select {
3577 match &columns[0] {
3578 ResultColumn::Expr {
3579 alias: Some(alias), ..
3580 } => assert_eq!(alias, "result"),
3581 other => unreachable!("expected aliased expr column, got {other:?}"),
3582 }
3583 } else {
3584 unreachable!("expected Select core");
3585 }
3586 } else {
3587 unreachable!("expected Select");
3588 }
3589 }
3590
3591 #[test]
3592 fn test_select_distinct_keyword() {
3593 let stmt = parse_one("SELECT DISTINCT a, b FROM t");
3594 if let Statement::Select(s) = stmt {
3595 if let SelectCore::Select {
3596 distinct, columns, ..
3597 } = &s.body.select
3598 {
3599 assert_eq!(*distinct, Distinctness::Distinct);
3600 assert_eq!(columns.len(), 2);
3601 } else {
3602 unreachable!("expected Select core");
3603 }
3604 } else {
3605 unreachable!("expected Select");
3606 }
3607 }
3608
3609 #[test]
3610 fn test_select_values_clause() {
3611 let stmt = parse_one("VALUES (1, 2), (3, 4)");
3612 if let Statement::Select(s) = stmt {
3613 if let SelectCore::Values(rows) = &s.body.select {
3614 assert_eq!(rows.len(), 2);
3615 assert_eq!(rows[0].len(), 2);
3616 assert_eq!(rows[1].len(), 2);
3617 } else {
3618 unreachable!("expected Values core");
3619 }
3620 } else {
3621 unreachable!("expected Select");
3622 }
3623 }
3624
3625 #[test]
3626 fn test_select_group_by_having() {
3627 let stmt = parse_one("SELECT dept, count(*) FROM emp GROUP BY dept HAVING count(*) > 5");
3628 if let Statement::Select(s) = stmt {
3629 if let SelectCore::Select {
3630 group_by, having, ..
3631 } = &s.body.select
3632 {
3633 assert_eq!(group_by.len(), 1);
3634 assert!(having.is_some(), "HAVING clause must be present");
3635 } else {
3636 unreachable!("expected Select core");
3637 }
3638 } else {
3639 unreachable!("expected Select");
3640 }
3641 }
3642
3643 #[test]
3644 fn test_compound_union() {
3645 let stmt = parse_one("SELECT 1 UNION SELECT 2");
3646 if let Statement::Select(s) = stmt {
3647 assert_eq!(s.body.compounds.len(), 1);
3648 assert_eq!(s.body.compounds[0].0, CompoundOp::Union);
3649 } else {
3650 unreachable!("expected Select");
3651 }
3652 }
3653
3654 #[test]
3655 fn test_compound_union_all() {
3656 let stmt = parse_one("SELECT 1 UNION ALL SELECT 2");
3657 if let Statement::Select(s) = stmt {
3658 assert_eq!(s.body.compounds.len(), 1);
3659 assert_eq!(s.body.compounds[0].0, CompoundOp::UnionAll);
3660 } else {
3661 unreachable!("expected Select");
3662 }
3663 }
3664
3665 #[test]
3666 fn test_compound_intersect() {
3667 let stmt = parse_one("SELECT 1 INTERSECT SELECT 2");
3668 if let Statement::Select(s) = stmt {
3669 assert_eq!(s.body.compounds.len(), 1);
3670 assert_eq!(s.body.compounds[0].0, CompoundOp::Intersect);
3671 } else {
3672 unreachable!("expected Select");
3673 }
3674 }
3675
3676 #[test]
3677 fn test_compound_except() {
3678 let stmt = parse_one("SELECT 1 EXCEPT SELECT 2");
3679 if let Statement::Select(s) = stmt {
3680 assert_eq!(s.body.compounds.len(), 1);
3681 assert_eq!(s.body.compounds[0].0, CompoundOp::Except);
3682 } else {
3683 unreachable!("expected Select");
3684 }
3685 }
3686
3687 #[test]
3688 fn test_compound_order_applies_to_whole() {
3689 let stmt = parse_one("SELECT a FROM t1 UNION ALL SELECT b FROM t2 ORDER BY 1 LIMIT 10");
3691 if let Statement::Select(s) = stmt {
3692 assert_eq!(s.body.compounds.len(), 1);
3693 assert_eq!(s.order_by.len(), 1, "ORDER BY must be on compound");
3694 assert!(s.limit.is_some(), "LIMIT must be on compound");
3695 } else {
3696 unreachable!("expected Select");
3697 }
3698 }
3699
3700 #[test]
3701 fn test_compound_three_way() {
3702 let stmt = parse_one("SELECT 1 UNION SELECT 2 INTERSECT SELECT 3");
3703 if let Statement::Select(s) = stmt {
3704 assert_eq!(s.body.compounds.len(), 2);
3705 assert_eq!(s.body.compounds[0].0, CompoundOp::Union);
3706 assert_eq!(s.body.compounds[1].0, CompoundOp::Intersect);
3707 } else {
3708 unreachable!("expected Select");
3709 }
3710 }
3711
3712 #[test]
3713 fn test_cte_not_materialized() {
3714 let stmt = parse_one("WITH cte AS NOT MATERIALIZED (SELECT 1) SELECT * FROM cte");
3715 if let Statement::Select(s) = stmt {
3716 let with = s.with.as_ref().expect("WITH clause");
3717 assert_eq!(
3718 with.ctes[0].materialized,
3719 Some(CteMaterialized::NotMaterialized)
3720 );
3721 } else {
3722 unreachable!("expected Select");
3723 }
3724 }
3725
3726 #[test]
3727 fn test_cte_with_explicit_columns() {
3728 let stmt = parse_one("WITH cte(a, b, c) AS (SELECT 1, 2, 3) SELECT * FROM cte");
3729 if let Statement::Select(s) = stmt {
3730 let with = s.with.as_ref().expect("WITH clause");
3731 assert_eq!(with.ctes[0].columns, vec!["a", "b", "c"]);
3732 } else {
3733 unreachable!("expected Select");
3734 }
3735 }
3736
3737 #[test]
3738 fn test_window_frame_range() {
3739 let stmt = parse_one(
3740 "SELECT sum(x) OVER (ORDER BY y RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) FROM t",
3741 );
3742 if let Statement::Select(s) = stmt {
3743 if let SelectCore::Select { columns, .. } = &s.body.select {
3744 match &columns[0] {
3745 ResultColumn::Expr {
3746 expr:
3747 Expr::FunctionCall {
3748 over: Some(over), ..
3749 },
3750 ..
3751 } => {
3752 let frame = over.frame.as_ref().expect("frame spec");
3753 assert_eq!(frame.frame_type, FrameType::Range);
3754 assert!(matches!(frame.start, FrameBound::UnboundedPreceding));
3755 assert!(matches!(frame.end, Some(FrameBound::CurrentRow)));
3756 }
3757 other => unreachable!("expected window function, got {other:?}"),
3758 }
3759 } else {
3760 unreachable!("expected Select core");
3761 }
3762 } else {
3763 unreachable!("expected Select");
3764 }
3765 }
3766
3767 #[test]
3768 fn test_window_frame_groups() {
3769 let stmt = parse_one(
3770 "SELECT sum(x) OVER (ORDER BY y GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM t",
3771 );
3772 if let Statement::Select(s) = stmt {
3773 if let SelectCore::Select { columns, .. } = &s.body.select {
3774 match &columns[0] {
3775 ResultColumn::Expr {
3776 expr:
3777 Expr::FunctionCall {
3778 over: Some(over), ..
3779 },
3780 ..
3781 } => {
3782 let frame = over.frame.as_ref().expect("frame spec");
3783 assert_eq!(frame.frame_type, FrameType::Groups);
3784 assert!(matches!(frame.start, FrameBound::Preceding(_)));
3785 assert!(matches!(frame.end, Some(FrameBound::Following(_))));
3786 }
3787 other => unreachable!("expected window function, got {other:?}"),
3788 }
3789 } else {
3790 unreachable!("expected Select core");
3791 }
3792 } else {
3793 unreachable!("expected Select");
3794 }
3795 }
3796
3797 #[test]
3798 fn test_window_frame_exclude_current_row() {
3799 let stmt = parse_one(
3800 "SELECT sum(x) OVER (ORDER BY y ROWS BETWEEN UNBOUNDED PRECEDING AND \
3801 UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW) FROM t",
3802 );
3803 if let Statement::Select(s) = stmt {
3804 if let SelectCore::Select { columns, .. } = &s.body.select {
3805 match &columns[0] {
3806 ResultColumn::Expr {
3807 expr:
3808 Expr::FunctionCall {
3809 over: Some(over), ..
3810 },
3811 ..
3812 } => {
3813 let frame = over.frame.as_ref().expect("frame spec");
3814 assert_eq!(frame.exclude, Some(FrameExclude::CurrentRow));
3815 }
3816 other => unreachable!("expected window function, got {other:?}"),
3817 }
3818 } else {
3819 unreachable!("expected Select core");
3820 }
3821 } else {
3822 unreachable!("expected Select");
3823 }
3824 }
3825
3826 #[test]
3827 fn test_window_frame_exclude_ties() {
3828 let stmt = parse_one(
3829 "SELECT sum(x) OVER (ORDER BY y ROWS BETWEEN UNBOUNDED PRECEDING AND \
3830 UNBOUNDED FOLLOWING EXCLUDE TIES) FROM t",
3831 );
3832 if let Statement::Select(s) = stmt {
3833 if let SelectCore::Select { columns, .. } = &s.body.select {
3834 match &columns[0] {
3835 ResultColumn::Expr {
3836 expr:
3837 Expr::FunctionCall {
3838 over: Some(over), ..
3839 },
3840 ..
3841 } => {
3842 let frame = over.frame.as_ref().expect("frame spec");
3843 assert_eq!(frame.exclude, Some(FrameExclude::Ties));
3844 }
3845 other => unreachable!("expected window function, got {other:?}"),
3846 }
3847 } else {
3848 unreachable!("expected Select core");
3849 }
3850 } else {
3851 unreachable!("expected Select");
3852 }
3853 }
3854
3855 #[test]
3856 fn test_window_frame_exclude_group() {
3857 let stmt =
3858 parse_one("SELECT sum(x) OVER (ORDER BY y GROUPS CURRENT ROW EXCLUDE GROUP) FROM t");
3859 if let Statement::Select(s) = stmt {
3860 if let SelectCore::Select { columns, .. } = &s.body.select {
3861 match &columns[0] {
3862 ResultColumn::Expr {
3863 expr:
3864 Expr::FunctionCall {
3865 over: Some(over), ..
3866 },
3867 ..
3868 } => {
3869 let frame = over.frame.as_ref().expect("frame spec");
3870 assert_eq!(frame.frame_type, FrameType::Groups);
3871 assert_eq!(frame.exclude, Some(FrameExclude::Group));
3872 }
3873 other => unreachable!("expected window function, got {other:?}"),
3874 }
3875 } else {
3876 unreachable!("expected Select core");
3877 }
3878 } else {
3879 unreachable!("expected Select");
3880 }
3881 }
3882
3883 #[test]
3884 fn test_window_frame_unbounded_following() {
3885 let stmt = parse_one(
3886 "SELECT sum(x) OVER (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM t",
3887 );
3888 if let Statement::Select(s) = stmt {
3889 if let SelectCore::Select { columns, .. } = &s.body.select {
3890 match &columns[0] {
3891 ResultColumn::Expr {
3892 expr:
3893 Expr::FunctionCall {
3894 over: Some(over), ..
3895 },
3896 ..
3897 } => {
3898 let frame = over.frame.as_ref().expect("frame spec");
3899 assert!(matches!(frame.start, FrameBound::CurrentRow));
3900 assert!(matches!(frame.end, Some(FrameBound::UnboundedFollowing)));
3901 }
3902 other => unreachable!("expected window function, got {other:?}"),
3903 }
3904 } else {
3905 unreachable!("expected Select core");
3906 }
3907 } else {
3908 unreachable!("expected Select");
3909 }
3910 }
3911
3912 #[test]
3913 fn test_filter_clause_aggregate() {
3914 let stmt = parse_one("SELECT count(*) FILTER (WHERE x > 0) FROM t");
3915 if let Statement::Select(s) = stmt {
3916 if let SelectCore::Select { columns, .. } = &s.body.select {
3917 match &columns[0] {
3918 ResultColumn::Expr {
3919 expr: Expr::FunctionCall { filter, .. },
3920 ..
3921 } => {
3922 assert!(
3923 filter.is_some(),
3924 "FILTER clause must be present on aggregate"
3925 );
3926 }
3927 other => unreachable!("expected function call with filter, got {other:?}"),
3928 }
3929 } else {
3930 unreachable!("expected Select core");
3931 }
3932 } else {
3933 unreachable!("expected Select");
3934 }
3935 }
3936
3937 #[test]
3938 fn test_filter_clause_window() {
3939 let stmt = parse_one("SELECT sum(x) FILTER (WHERE x > 0) OVER (ORDER BY y) FROM t");
3940 if let Statement::Select(s) = stmt {
3941 if let SelectCore::Select { columns, .. } = &s.body.select {
3942 match &columns[0] {
3943 ResultColumn::Expr {
3944 expr:
3945 Expr::FunctionCall {
3946 filter,
3947 over: Some(_),
3948 ..
3949 },
3950 ..
3951 } => {
3952 assert!(
3953 filter.is_some(),
3954 "FILTER clause must be present on window function"
3955 );
3956 }
3957 other => unreachable!("expected window function with filter, got {other:?}"),
3958 }
3959 } else {
3960 unreachable!("expected Select core");
3961 }
3962 } else {
3963 unreachable!("expected Select");
3964 }
3965 }
3966
3967 #[test]
3968 fn test_subquery_in_from() {
3969 let stmt = parse_one("SELECT sub.x FROM (SELECT 1 AS x) AS sub");
3970 if let Statement::Select(s) = stmt {
3971 if let SelectCore::Select { from, .. } = &s.body.select {
3972 let from = from.as_ref().expect("FROM clause");
3973 match &from.source {
3974 TableOrSubquery::Subquery { alias, .. } => {
3975 assert_eq!(alias.as_deref(), Some("sub"));
3976 }
3977 other => unreachable!("expected subquery source, got {other:?}"),
3978 }
3979 } else {
3980 unreachable!("expected Select core");
3981 }
3982 } else {
3983 unreachable!("expected Select");
3984 }
3985 }
3986
3987 #[test]
3988 fn test_multiple_joins_chain() {
3989 let stmt = parse_one(
3990 "SELECT * FROM a INNER JOIN b ON a.id = b.a_id \
3991 LEFT JOIN c ON b.id = c.b_id \
3992 CROSS JOIN d",
3993 );
3994 if let Statement::Select(s) = stmt {
3995 if let SelectCore::Select { from, .. } = &s.body.select {
3996 let from = from.as_ref().expect("FROM clause");
3997 assert_eq!(from.joins.len(), 3);
3998 assert_eq!(from.joins[0].join_type.kind, JoinKind::Inner);
3999 assert_eq!(from.joins[1].join_type.kind, JoinKind::Left);
4000 assert_eq!(from.joins[2].join_type.kind, JoinKind::Cross);
4001 } else {
4002 unreachable!("expected Select core");
4003 }
4004 } else {
4005 unreachable!("expected Select");
4006 }
4007 }
4008
4009 #[test]
4010 fn test_natural_left_join() {
4011 let stmt = parse_one("SELECT * FROM a NATURAL LEFT JOIN b");
4012 if let Statement::Select(s) = stmt {
4013 if let SelectCore::Select { from, .. } = &s.body.select {
4014 let from = from.as_ref().expect("FROM clause");
4015 let jt = &from.joins[0].join_type;
4016 assert!(jt.natural, "must be NATURAL");
4017 assert_eq!(jt.kind, JoinKind::Left);
4018 } else {
4019 unreachable!("expected Select core");
4020 }
4021 } else {
4022 unreachable!("expected Select");
4023 }
4024 }
4025
4026 #[test]
4027 fn test_select_nulls_first_default_asc() {
4028 let stmt = parse_one("SELECT a FROM t ORDER BY a ASC NULLS FIRST");
4030 if let Statement::Select(s) = stmt {
4031 assert_eq!(s.order_by.len(), 1);
4032 assert_eq!(s.order_by[0].direction, Some(SortDirection::Asc));
4033 assert_eq!(s.order_by[0].nulls, Some(NullsOrder::First));
4034 } else {
4035 unreachable!("expected Select");
4036 }
4037 }
4038
4039 #[test]
4040 fn test_select_nulls_last_desc() {
4041 let stmt = parse_one("SELECT a FROM t ORDER BY a DESC NULLS LAST");
4043 if let Statement::Select(s) = stmt {
4044 assert_eq!(s.order_by.len(), 1);
4045 assert_eq!(s.order_by[0].direction, Some(SortDirection::Desc));
4046 assert_eq!(s.order_by[0].nulls, Some(NullsOrder::Last));
4047 } else {
4048 unreachable!("expected Select");
4049 }
4050 }
4051
4052 #[test]
4057 fn test_roundtrip_select_filter_clause() {
4058 assert_roundtrip("SELECT count(*) FILTER (WHERE x > 0) FROM t");
4059 }
4060
4061 #[test]
4062 fn test_roundtrip_select_window_frame_groups() {
4063 assert_roundtrip(
4064 "SELECT sum(x) OVER (ORDER BY y GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM t",
4065 );
4066 }
4067
4068 #[test]
4069 fn test_roundtrip_select_window_frame_exclude() {
4070 assert_roundtrip(
4071 "SELECT sum(x) OVER (ORDER BY y ROWS BETWEEN UNBOUNDED PRECEDING AND \
4072 UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW) FROM t",
4073 );
4074 assert_roundtrip(
4075 "SELECT sum(x) OVER (ORDER BY y ROWS BETWEEN UNBOUNDED PRECEDING AND \
4076 UNBOUNDED FOLLOWING EXCLUDE TIES) FROM t",
4077 );
4078 assert_roundtrip("SELECT sum(x) OVER (ORDER BY y GROUPS CURRENT ROW EXCLUDE GROUP) FROM t");
4079 }
4080
4081 #[test]
4082 fn test_roundtrip_select_nulls_order() {
4083 assert_roundtrip("SELECT a FROM t ORDER BY a ASC NULLS FIRST");
4084 assert_roundtrip("SELECT a FROM t ORDER BY a DESC NULLS LAST");
4085 }
4086
4087 #[test]
4088 fn test_roundtrip_select_values() {
4089 assert_roundtrip("VALUES (1, 2), (3, 4)");
4090 }
4091
4092 #[test]
4093 fn test_roundtrip_select_compound_order_limit() {
4094 assert_roundtrip("SELECT a FROM t1 UNION ALL SELECT b FROM t2 ORDER BY 1 LIMIT 10");
4095 }
4096
4097 #[test]
4098 fn test_roundtrip_select_cte_not_materialized() {
4099 assert_roundtrip("WITH cte AS NOT MATERIALIZED (SELECT 1) SELECT * FROM cte");
4100 }
4101
4102 #[test]
4103 fn test_roundtrip_select_natural_left_join() {
4104 assert_roundtrip("SELECT * FROM a NATURAL LEFT JOIN b");
4105 }
4106
4107 #[test]
4108 fn test_roundtrip_select_indexed_by() {
4109 assert_roundtrip("SELECT * FROM t INDEXED BY idx_t WHERE x = 1");
4110 }
4111
4112 #[test]
4113 fn test_roundtrip_select_filter_window_combined() {
4114 assert_roundtrip("SELECT sum(x) FILTER (WHERE x > 0) OVER (ORDER BY y) FROM t");
4115 }
4116
4117 #[test]
4118 fn test_roundtrip_select_three_way_compound() {
4119 assert_roundtrip("SELECT 1 UNION SELECT 2 EXCEPT SELECT 3");
4120 }
4121
4122 #[test]
4123 fn test_roundtrip_select_multiple_joins() {
4124 assert_roundtrip(
4125 "SELECT * FROM a INNER JOIN b ON a.id = b.a_id LEFT JOIN c ON b.id = c.b_id",
4126 );
4127 }
4128
4129 #[test]
4134 fn test_select_star() {
4135 let stmt = parse_one("SELECT * FROM t");
4137 if let Statement::Select(s) = stmt {
4138 if let SelectCore::Select { columns, .. } = &s.body.select {
4139 assert!(matches!(columns[0], ResultColumn::Star));
4140 } else {
4141 unreachable!("expected Select core");
4142 }
4143 } else {
4144 unreachable!("expected Select");
4145 }
4146 }
4147
4148 #[test]
4149 fn test_inner_join_on() {
4150 let stmt = parse_one("SELECT * FROM a INNER JOIN b ON a.id = b.a_id");
4152 if let Statement::Select(s) = stmt {
4153 if let SelectCore::Select { from, .. } = &s.body.select {
4154 let from = from.as_ref().expect("FROM clause");
4155 assert_eq!(from.joins[0].join_type.kind, JoinKind::Inner);
4156 assert!(matches!(
4157 from.joins[0].constraint,
4158 Some(JoinConstraint::On(_))
4159 ));
4160 } else {
4161 unreachable!("expected Select core");
4162 }
4163 } else {
4164 unreachable!("expected Select");
4165 }
4166 }
4167
4168 #[test]
4169 fn test_left_outer_join() {
4170 let stmt = parse_one("SELECT * FROM a LEFT OUTER JOIN b ON a.id = b.a_id");
4172 if let Statement::Select(s) = stmt {
4173 if let SelectCore::Select { from, .. } = &s.body.select {
4174 let from = from.as_ref().expect("FROM clause");
4175 assert_eq!(from.joins[0].join_type.kind, JoinKind::Left);
4176 } else {
4177 unreachable!("expected Select core");
4178 }
4179 } else {
4180 unreachable!("expected Select");
4181 }
4182 }
4183
4184 #[test]
4185 fn test_right_outer_join() {
4186 let stmt = parse_one("SELECT * FROM a RIGHT JOIN b ON a.id = b.a_id");
4188 if let Statement::Select(s) = stmt {
4189 if let SelectCore::Select { from, .. } = &s.body.select {
4190 let from = from.as_ref().expect("FROM clause");
4191 assert_eq!(from.joins[0].join_type.kind, JoinKind::Right);
4192 } else {
4193 unreachable!("expected Select core");
4194 }
4195 } else {
4196 unreachable!("expected Select");
4197 }
4198 }
4199
4200 #[test]
4201 fn test_full_outer_join() {
4202 let stmt = parse_one("SELECT * FROM a FULL OUTER JOIN b ON a.id = b.a_id");
4204 if let Statement::Select(s) = stmt {
4205 if let SelectCore::Select { from, .. } = &s.body.select {
4206 let from = from.as_ref().expect("FROM clause");
4207 assert_eq!(from.joins[0].join_type.kind, JoinKind::Full);
4208 } else {
4209 unreachable!("expected Select core");
4210 }
4211 } else {
4212 unreachable!("expected Select");
4213 }
4214 }
4215
4216 #[test]
4217 fn test_cross_join_no_reorder() {
4218 let stmt = parse_one("SELECT * FROM a CROSS JOIN b");
4220 if let Statement::Select(s) = stmt {
4221 if let SelectCore::Select { from, .. } = &s.body.select {
4222 let from = from.as_ref().expect("FROM clause");
4223 assert_eq!(from.joins[0].join_type.kind, JoinKind::Cross);
4224 assert!(from.joins[0].constraint.is_none());
4226 } else {
4227 unreachable!("expected Select core");
4228 }
4229 } else {
4230 unreachable!("expected Select");
4231 }
4232 }
4233
4234 #[test]
4235 fn test_natural_join() {
4236 let stmt = parse_one("SELECT * FROM a NATURAL JOIN b");
4238 if let Statement::Select(s) = stmt {
4239 if let SelectCore::Select { from, .. } = &s.body.select {
4240 let from = from.as_ref().expect("FROM clause");
4241 assert!(from.joins[0].join_type.natural);
4242 } else {
4243 unreachable!("expected Select core");
4244 }
4245 } else {
4246 unreachable!("expected Select");
4247 }
4248 }
4249
4250 #[test]
4251 fn test_using_clause() {
4252 let stmt = parse_one("SELECT * FROM a JOIN b USING (id, name)");
4254 if let Statement::Select(s) = stmt {
4255 if let SelectCore::Select { from, .. } = &s.body.select {
4256 let from = from.as_ref().expect("FROM clause");
4257 match &from.joins[0].constraint {
4258 Some(JoinConstraint::Using(cols)) => {
4259 assert_eq!(cols.len(), 2);
4260 assert_eq!(cols[0], "id");
4261 assert_eq!(cols[1], "name");
4262 }
4263 other => unreachable!("expected USING constraint, got {other:?}"),
4264 }
4265 } else {
4266 unreachable!("expected Select core");
4267 }
4268 } else {
4269 unreachable!("expected Select");
4270 }
4271 }
4272
4273 #[test]
4274 fn test_cte_basic() {
4275 let stmt = parse_one("WITH cte AS (SELECT 1 AS x) SELECT * FROM cte");
4277 if let Statement::Select(s) = stmt {
4278 let with = s.with.as_ref().expect("WITH clause");
4279 assert!(!with.recursive);
4280 assert_eq!(with.ctes.len(), 1);
4281 assert_eq!(with.ctes[0].name, "cte");
4282 } else {
4283 unreachable!("expected Select");
4284 }
4285 }
4286
4287 #[test]
4288 fn test_cte_recursive_union_all() {
4289 let stmt = parse_one(
4291 "WITH RECURSIVE cnt(x) AS (\
4292 SELECT 1 UNION ALL SELECT x+1 FROM cnt WHERE x<10\
4293 ) SELECT x FROM cnt",
4294 );
4295 if let Statement::Select(s) = stmt {
4296 let with = s.with.as_ref().expect("WITH clause");
4297 assert!(with.recursive);
4298 assert_eq!(with.ctes[0].name, "cnt");
4299 let cte_body = &with.ctes[0].query;
4301 assert_eq!(cte_body.body.compounds.len(), 1);
4302 assert_eq!(cte_body.body.compounds[0].0, CompoundOp::UnionAll);
4303 } else {
4304 unreachable!("expected Select");
4305 }
4306 }
4307
4308 #[test]
4309 fn test_cte_recursive_union_cycle_detection() {
4310 let stmt = parse_one(
4312 "WITH RECURSIVE paths(a, b) AS (\
4313 SELECT src, dst FROM edges \
4314 UNION \
4315 SELECT p.a, e.dst FROM paths p JOIN edges e ON p.b = e.src\
4316 ) SELECT * FROM paths",
4317 );
4318 if let Statement::Select(s) = stmt {
4319 let with = s.with.as_ref().expect("WITH clause");
4320 assert!(with.recursive);
4321 let cte_body = &with.ctes[0].query;
4323 assert_eq!(cte_body.body.compounds.len(), 1);
4324 assert_eq!(cte_body.body.compounds[0].0, CompoundOp::Union);
4325 } else {
4326 unreachable!("expected Select");
4327 }
4328 }
4329
4330 #[test]
4331 fn test_cte_materialized_hint() {
4332 let stmt = parse_one("WITH cte AS MATERIALIZED (SELECT 1) SELECT * FROM cte");
4334 if let Statement::Select(s) = stmt {
4335 let with = s.with.as_ref().expect("WITH clause");
4336 assert_eq!(
4337 with.ctes[0].materialized,
4338 Some(CteMaterialized::Materialized)
4339 );
4340 } else {
4341 unreachable!("expected Select");
4342 }
4343 }
4344
4345 #[test]
4346 fn test_cte_not_materialized_hint() {
4347 let stmt = parse_one("WITH cte AS NOT MATERIALIZED (SELECT 1) SELECT * FROM cte");
4349 if let Statement::Select(s) = stmt {
4350 let with = s.with.as_ref().expect("WITH clause");
4351 assert_eq!(
4352 with.ctes[0].materialized,
4353 Some(CteMaterialized::NotMaterialized)
4354 );
4355 } else {
4356 unreachable!("expected Select");
4357 }
4358 }
4359
4360 #[test]
4361 fn test_window_partition_by() {
4362 let stmt = parse_one("SELECT sum(x) OVER (PARTITION BY dept) FROM emp");
4364 if let Statement::Select(s) = stmt {
4365 if let SelectCore::Select { columns, .. } = &s.body.select {
4366 match &columns[0] {
4367 ResultColumn::Expr {
4368 expr:
4369 Expr::FunctionCall {
4370 over: Some(over), ..
4371 },
4372 ..
4373 } => {
4374 assert_eq!(over.partition_by.len(), 1);
4375 }
4376 other => unreachable!("expected window function, got {other:?}"),
4377 }
4378 } else {
4379 unreachable!("expected Select core");
4380 }
4381 } else {
4382 unreachable!("expected Select");
4383 }
4384 }
4385
4386 #[test]
4387 fn test_window_order_by() {
4388 let stmt = parse_one("SELECT row_number() OVER (ORDER BY salary DESC) FROM emp");
4390 if let Statement::Select(s) = stmt {
4391 if let SelectCore::Select { columns, .. } = &s.body.select {
4392 match &columns[0] {
4393 ResultColumn::Expr {
4394 expr:
4395 Expr::FunctionCall {
4396 over: Some(over), ..
4397 },
4398 ..
4399 } => {
4400 assert_eq!(over.order_by.len(), 1);
4401 assert_eq!(over.order_by[0].direction, Some(SortDirection::Desc));
4402 }
4403 other => unreachable!("expected window function, got {other:?}"),
4404 }
4405 } else {
4406 unreachable!("expected Select core");
4407 }
4408 } else {
4409 unreachable!("expected Select");
4410 }
4411 }
4412
4413 #[test]
4414 fn test_window_frame_rows() {
4415 let stmt = parse_one(
4417 "SELECT sum(x) OVER (ORDER BY y ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) FROM t",
4418 );
4419 if let Statement::Select(s) = stmt {
4420 if let SelectCore::Select { columns, .. } = &s.body.select {
4421 match &columns[0] {
4422 ResultColumn::Expr {
4423 expr:
4424 Expr::FunctionCall {
4425 over: Some(over), ..
4426 },
4427 ..
4428 } => {
4429 let frame = over.frame.as_ref().expect("frame spec");
4430 assert_eq!(frame.frame_type, FrameType::Rows);
4431 assert!(matches!(frame.start, FrameBound::Preceding(_)));
4432 assert!(matches!(frame.end, Some(FrameBound::CurrentRow)));
4433 }
4434 other => unreachable!("expected window function, got {other:?}"),
4435 }
4436 } else {
4437 unreachable!("expected Select core");
4438 }
4439 } else {
4440 unreachable!("expected Select");
4441 }
4442 }
4443
4444 #[test]
4445 fn test_window_exclude_current_row() {
4446 let stmt = parse_one(
4448 "SELECT sum(x) OVER (ORDER BY y ROWS BETWEEN UNBOUNDED PRECEDING AND \
4449 UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW) FROM t",
4450 );
4451 if let Statement::Select(s) = stmt {
4452 if let SelectCore::Select { columns, .. } = &s.body.select {
4453 match &columns[0] {
4454 ResultColumn::Expr {
4455 expr:
4456 Expr::FunctionCall {
4457 over: Some(over), ..
4458 },
4459 ..
4460 } => {
4461 let frame = over.frame.as_ref().expect("frame spec");
4462 assert_eq!(frame.exclude, Some(FrameExclude::CurrentRow));
4463 }
4464 other => unreachable!("expected window function, got {other:?}"),
4465 }
4466 } else {
4467 unreachable!("expected Select core");
4468 }
4469 } else {
4470 unreachable!("expected Select");
4471 }
4472 }
4473
4474 #[test]
4475 fn test_window_exclude_ties() {
4476 let stmt = parse_one(
4478 "SELECT sum(x) OVER (ORDER BY y ROWS BETWEEN UNBOUNDED PRECEDING AND \
4479 UNBOUNDED FOLLOWING EXCLUDE TIES) FROM t",
4480 );
4481 if let Statement::Select(s) = stmt {
4482 if let SelectCore::Select { columns, .. } = &s.body.select {
4483 match &columns[0] {
4484 ResultColumn::Expr {
4485 expr:
4486 Expr::FunctionCall {
4487 over: Some(over), ..
4488 },
4489 ..
4490 } => {
4491 let frame = over.frame.as_ref().expect("frame spec");
4492 assert_eq!(frame.exclude, Some(FrameExclude::Ties));
4493 }
4494 other => unreachable!("expected window function, got {other:?}"),
4495 }
4496 } else {
4497 unreachable!("expected Select core");
4498 }
4499 } else {
4500 unreachable!("expected Select");
4501 }
4502 }
4503
4504 #[test]
4505 fn test_nulls_first_asc() {
4506 let stmt = parse_one("SELECT a FROM t ORDER BY a ASC NULLS FIRST");
4508 if let Statement::Select(s) = stmt {
4509 assert_eq!(s.order_by.len(), 1);
4510 assert_eq!(s.order_by[0].direction, Some(SortDirection::Asc));
4511 assert_eq!(s.order_by[0].nulls, Some(NullsOrder::First));
4512 } else {
4513 unreachable!("expected Select");
4514 }
4515 }
4516
4517 #[test]
4518 fn test_nulls_last_asc() {
4519 let stmt = parse_one("SELECT a FROM t ORDER BY a ASC NULLS LAST");
4521 if let Statement::Select(s) = stmt {
4522 assert_eq!(s.order_by.len(), 1);
4523 assert_eq!(s.order_by[0].direction, Some(SortDirection::Asc));
4524 assert_eq!(s.order_by[0].nulls, Some(NullsOrder::Last));
4525 } else {
4526 unreachable!("expected Select");
4527 }
4528 }
4529
4530 #[test]
4531 fn test_distinct_deduplicates() {
4532 let stmt = parse_one("SELECT DISTINCT a, b FROM t");
4534 if let Statement::Select(s) = stmt {
4535 if let SelectCore::Select { distinct, .. } = &s.body.select {
4536 assert_eq!(*distinct, Distinctness::Distinct);
4537 } else {
4538 unreachable!("expected Select core");
4539 }
4540 } else {
4541 unreachable!("expected Select");
4542 }
4543 }
4544
4545 #[test]
4546 fn test_limit_offset() {
4547 let stmt = parse_one("SELECT a FROM t LIMIT 10 OFFSET 20");
4549 if let Statement::Select(s) = stmt {
4550 let limit = s.limit.expect("LIMIT clause");
4551 assert!(matches!(
4552 limit.limit,
4553 Expr::Literal(Literal::Integer(10), _)
4554 ));
4555 assert!(matches!(
4556 limit.offset,
4557 Some(Expr::Literal(Literal::Integer(20), _))
4558 ));
4559 } else {
4560 unreachable!("expected Select");
4561 }
4562 }
4563
4564 #[test]
4565 fn test_limit_comma_syntax() {
4566 let stmt = parse_one("SELECT a FROM t LIMIT 5, 10");
4568 if let Statement::Select(s) = stmt {
4569 let limit = s.limit.expect("LIMIT clause");
4570 assert!(matches!(
4572 limit.limit,
4573 Expr::Literal(Literal::Integer(10), _)
4574 ));
4575 assert!(matches!(
4576 limit.offset,
4577 Some(Expr::Literal(Literal::Integer(5), _))
4578 ));
4579 } else {
4580 unreachable!("expected Select");
4581 }
4582 }
4583
4584 #[test]
4585 fn test_negative_limit_unlimited() {
4586 let stmt = parse_one("SELECT a FROM t LIMIT -1");
4588 if let Statement::Select(s) = stmt {
4589 let limit = s.limit.expect("LIMIT clause");
4590 match &limit.limit {
4593 Expr::UnaryOp {
4594 op: fsqlite_ast::UnaryOp::Negate,
4595 ..
4596 } => {}
4597 Expr::Literal(Literal::Integer(n), _) if *n < 0 => {}
4598 other => unreachable!("expected negative limit expression, got {other:?}"),
4599 }
4600 } else {
4601 unreachable!("expected Select");
4602 }
4603 }
4604
4605 #[test]
4606 fn test_negative_offset_zero() {
4607 let stmt = parse_one("SELECT a FROM t LIMIT 10 OFFSET -5");
4609 if let Statement::Select(s) = stmt {
4610 let limit = s.limit.expect("LIMIT clause");
4611 assert!(limit.offset.is_some());
4612 match limit.offset.as_ref().unwrap() {
4613 Expr::UnaryOp {
4614 op: fsqlite_ast::UnaryOp::Negate,
4615 ..
4616 } => {}
4617 Expr::Literal(Literal::Integer(n), _) if *n < 0 => {}
4618 other => unreachable!("expected negative offset expression, got {other:?}"),
4619 }
4620 } else {
4621 unreachable!("expected Select");
4622 }
4623 }
4624
4625 #[test]
4626 fn test_current_date_constant() {
4627 let stmt = parse_one("SELECT CURRENT_DATE");
4629 if let Statement::Select(s) = stmt {
4630 if let SelectCore::Select { columns, .. } = &s.body.select {
4631 match &columns[0] {
4632 ResultColumn::Expr {
4633 expr: Expr::Literal(Literal::CurrentDate, _),
4634 ..
4635 } => {}
4636 other => unreachable!("expected CURRENT_DATE literal, got {other:?}"),
4637 }
4638 } else {
4639 unreachable!("expected Select core");
4640 }
4641 } else {
4642 unreachable!("expected Select");
4643 }
4644 }
4645
4646 #[test]
4647 fn test_current_time_constant() {
4648 let stmt = parse_one("SELECT CURRENT_TIME");
4650 if let Statement::Select(s) = stmt {
4651 if let SelectCore::Select { columns, .. } = &s.body.select {
4652 match &columns[0] {
4653 ResultColumn::Expr {
4654 expr: Expr::Literal(Literal::CurrentTime, _),
4655 ..
4656 } => {}
4657 other => unreachable!("expected CURRENT_TIME literal, got {other:?}"),
4658 }
4659 } else {
4660 unreachable!("expected Select core");
4661 }
4662 } else {
4663 unreachable!("expected Select");
4664 }
4665 }
4666
4667 #[test]
4668 fn test_current_timestamp_constant() {
4669 let stmt = parse_one("SELECT CURRENT_TIMESTAMP");
4671 if let Statement::Select(s) = stmt {
4672 if let SelectCore::Select { columns, .. } = &s.body.select {
4673 match &columns[0] {
4674 ResultColumn::Expr {
4675 expr: Expr::Literal(Literal::CurrentTimestamp, _),
4676 ..
4677 } => {}
4678 other => unreachable!("expected CURRENT_TIMESTAMP literal, got {other:?}"),
4679 }
4680 } else {
4681 unreachable!("expected Select core");
4682 }
4683 } else {
4684 unreachable!("expected Select");
4685 }
4686 }
4687
4688 #[test]
4689 fn test_date_constants_evaluated_once_per_statement() {
4690 let stmt = parse_one("SELECT CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP FROM t");
4693 if let Statement::Select(s) = stmt {
4694 if let SelectCore::Select { columns, .. } = &s.body.select {
4695 assert_eq!(columns.len(), 3);
4696 assert!(matches!(
4697 &columns[0],
4698 ResultColumn::Expr {
4699 expr: Expr::Literal(Literal::CurrentDate, _),
4700 ..
4701 }
4702 ));
4703 assert!(matches!(
4704 &columns[1],
4705 ResultColumn::Expr {
4706 expr: Expr::Literal(Literal::CurrentTime, _),
4707 ..
4708 }
4709 ));
4710 assert!(matches!(
4711 &columns[2],
4712 ResultColumn::Expr {
4713 expr: Expr::Literal(Literal::CurrentTimestamp, _),
4714 ..
4715 }
4716 ));
4717 } else {
4718 unreachable!("expected Select core");
4719 }
4720 } else {
4721 unreachable!("expected Select");
4722 }
4723 }
4724
4725 #[test]
4726 fn test_indexed_by_hint() {
4727 let stmt = parse_one("SELECT * FROM t INDEXED BY idx_t");
4729 if let Statement::Select(s) = stmt {
4730 if let SelectCore::Select { from, .. } = &s.body.select {
4731 let from = from.as_ref().expect("FROM clause");
4732 match &from.source {
4733 TableOrSubquery::Table {
4734 index_hint: Some(IndexHint::IndexedBy(name)),
4735 ..
4736 } => assert_eq!(name, "idx_t"),
4737 other => unreachable!("expected indexed table source, got {other:?}"),
4738 }
4739 } else {
4740 unreachable!("expected Select core");
4741 }
4742 } else {
4743 unreachable!("expected Select");
4744 }
4745 }
4746
4747 #[test]
4748 fn test_not_indexed_hint() {
4749 let stmt = parse_one("SELECT * FROM t NOT INDEXED");
4751 if let Statement::Select(s) = stmt {
4752 if let SelectCore::Select { from, .. } = &s.body.select {
4753 let from = from.as_ref().expect("FROM clause");
4754 match &from.source {
4755 TableOrSubquery::Table {
4756 index_hint: Some(IndexHint::NotIndexed),
4757 ..
4758 } => {}
4759 other => unreachable!("expected not-indexed table source, got {other:?}"),
4760 }
4761 } else {
4762 unreachable!("expected Select core");
4763 }
4764 } else {
4765 unreachable!("expected Select");
4766 }
4767 }
4768
4769 #[test]
4770 fn test_table_valued_function_in_from() {
4771 let stmt = parse_one("SELECT * FROM generate_series(1, 100) AS gs");
4773 if let Statement::Select(s) = stmt {
4774 if let SelectCore::Select { from, .. } = &s.body.select {
4775 let from = from.as_ref().expect("FROM clause");
4776 match &from.source {
4777 TableOrSubquery::TableFunction { name, args, alias } => {
4778 assert_eq!(name, "generate_series");
4779 assert_eq!(args.len(), 2);
4780 assert_eq!(alias.as_deref(), Some("gs"));
4781 }
4782 other => unreachable!("expected table-valued function source, got {other:?}"),
4783 }
4784 } else {
4785 unreachable!("expected Select core");
4786 }
4787 } else {
4788 unreachable!("expected Select");
4789 }
4790 }
4791
4792 #[test]
4797 fn test_insert_values_single() {
4798 let stmt = parse_one("INSERT INTO t (a, b, c) VALUES (1, 'hello', 3.14)");
4799 if let Statement::Insert(i) = stmt {
4800 assert_eq!(i.columns, vec!["a", "b", "c"]);
4801 if let InsertSource::Values(rows) = &i.source {
4802 assert_eq!(rows.len(), 1);
4803 assert_eq!(rows[0].len(), 3);
4804 } else {
4805 unreachable!("expected Values source");
4806 }
4807 } else {
4808 unreachable!("expected Insert");
4809 }
4810 }
4811
4812 #[test]
4813 fn test_insert_values_multi() {
4814 let stmt = parse_one("INSERT INTO t (x, y) VALUES (1, 2), (3, 4), (5, 6)");
4815 if let Statement::Insert(i) = stmt {
4816 if let InsertSource::Values(rows) = &i.source {
4817 assert_eq!(rows.len(), 3);
4818 for row in rows {
4819 assert_eq!(row.len(), 2);
4820 }
4821 } else {
4822 unreachable!("expected Values source");
4823 }
4824 } else {
4825 unreachable!("expected Insert");
4826 }
4827 }
4828
4829 #[test]
4830 fn test_insert_from_select() {
4831 let stmt = parse_one("INSERT INTO t2 (a, b) SELECT x, y FROM t1 WHERE x > 0");
4832 if let Statement::Insert(i) = stmt {
4833 assert!(matches!(i.source, InsertSource::Select(_)));
4834 assert_eq!(i.columns, vec!["a", "b"]);
4835 } else {
4836 unreachable!("expected Insert");
4837 }
4838 }
4839
4840 #[test]
4841 fn test_insert_from_select_without_from_clause() {
4842 let stmt = parse_one("INSERT INTO t (a) SELECT 1");
4843 if let Statement::Insert(i) = stmt {
4844 if let InsertSource::Select(select) = &i.source {
4845 if let SelectCore::Select { from, columns, .. } = &select.body.select {
4846 assert!(from.is_none(), "SELECT 1 should parse without FROM");
4847 assert_eq!(columns.len(), 1);
4848 } else {
4849 unreachable!("expected Select core");
4850 }
4851 } else {
4852 unreachable!("expected Select source");
4853 }
4854 } else {
4855 unreachable!("expected Insert");
4856 }
4857 }
4858
4859 #[test]
4860 fn test_insert_from_select_subquery_source() {
4861 let stmt = parse_one("INSERT INTO t (a) SELECT sub.x FROM (SELECT 1 AS x) AS sub");
4862 if let Statement::Insert(i) = stmt {
4863 if let InsertSource::Select(select) = &i.source {
4864 if let SelectCore::Select { from, .. } = &select.body.select {
4865 let from = from.as_ref().expect("FROM clause");
4866 match &from.source {
4867 TableOrSubquery::Subquery { alias, .. } => {
4868 assert_eq!(alias.as_deref(), Some("sub"));
4869 }
4870 other => unreachable!("expected subquery source, got {other:?}"),
4871 }
4872 } else {
4873 unreachable!("expected Select core");
4874 }
4875 } else {
4876 unreachable!("expected Select source");
4877 }
4878 } else {
4879 unreachable!("expected Insert");
4880 }
4881 }
4882
4883 #[test]
4884 fn test_insert_from_select_table_function_source() {
4885 let stmt = parse_one("INSERT INTO t (a) SELECT gs.value FROM generate_series(1, 3) AS gs");
4886 if let Statement::Insert(i) = stmt {
4887 if let InsertSource::Select(select) = &i.source {
4888 if let SelectCore::Select { from, .. } = &select.body.select {
4889 let from = from.as_ref().expect("FROM clause");
4890 match &from.source {
4891 TableOrSubquery::TableFunction { name, args, alias } => {
4892 assert_eq!(name, "generate_series");
4893 assert_eq!(args.len(), 2);
4894 assert_eq!(alias.as_deref(), Some("gs"));
4895 }
4896 other => unreachable!("expected table function source, got {other:?}"),
4897 }
4898 } else {
4899 unreachable!("expected Select core");
4900 }
4901 } else {
4902 unreachable!("expected Select source");
4903 }
4904 } else {
4905 unreachable!("expected Insert");
4906 }
4907 }
4908
4909 #[test]
4910 fn test_insert_default_values() {
4911 let stmt = parse_one("INSERT INTO t DEFAULT VALUES");
4912 if let Statement::Insert(i) = stmt {
4913 assert!(matches!(i.source, InsertSource::DefaultValues));
4914 assert!(i.columns.is_empty());
4915 } else {
4916 unreachable!("expected Insert");
4917 }
4918 }
4919
4920 #[test]
4921 fn test_insert_or_abort() {
4922 let stmt = parse_one("INSERT OR ABORT INTO t (a) VALUES (1)");
4923 if let Statement::Insert(i) = stmt {
4924 assert_eq!(i.or_conflict, Some(ConflictAction::Abort));
4925 } else {
4926 unreachable!("expected Insert");
4927 }
4928 }
4929
4930 #[test]
4931 fn test_insert_or_rollback() {
4932 let stmt = parse_one("INSERT OR ROLLBACK INTO t (a) VALUES (1)");
4933 if let Statement::Insert(i) = stmt {
4934 assert_eq!(i.or_conflict, Some(ConflictAction::Rollback));
4935 } else {
4936 unreachable!("expected Insert");
4937 }
4938 }
4939
4940 #[test]
4941 fn test_insert_or_fail() {
4942 let stmt = parse_one("INSERT OR FAIL INTO t (a) VALUES (1)");
4943 if let Statement::Insert(i) = stmt {
4944 assert_eq!(i.or_conflict, Some(ConflictAction::Fail));
4945 } else {
4946 unreachable!("expected Insert");
4947 }
4948 }
4949
4950 #[test]
4951 fn test_insert_or_ignore() {
4952 let stmt = parse_one("INSERT OR IGNORE INTO t (a) VALUES (1)");
4953 if let Statement::Insert(i) = stmt {
4954 assert_eq!(i.or_conflict, Some(ConflictAction::Ignore));
4955 } else {
4956 unreachable!("expected Insert");
4957 }
4958 }
4959
4960 #[test]
4961 fn test_insert_or_replace() {
4962 let stmt = parse_one("INSERT OR REPLACE INTO t (a) VALUES (1)");
4964 if let Statement::Insert(i) = stmt {
4965 assert_eq!(i.or_conflict, Some(ConflictAction::Replace));
4966 } else {
4967 unreachable!("expected Insert");
4968 }
4969 }
4970
4971 #[test]
4972 fn test_upsert_do_update() {
4973 let stmt = parse_one(
4974 "INSERT INTO t (a, b) VALUES (1, 2) ON CONFLICT (a) DO UPDATE SET b = excluded.b",
4975 );
4976 if let Statement::Insert(i) = stmt {
4977 assert_eq!(i.upsert.len(), 1);
4978 assert!(i.upsert[0].target.is_some());
4979 match &i.upsert[0].action {
4980 UpsertAction::Update {
4981 assignments,
4982 where_clause,
4983 } => {
4984 assert_eq!(assignments.len(), 1);
4985 assert!(where_clause.is_none());
4986 }
4987 UpsertAction::Nothing => unreachable!("expected Update action"),
4988 }
4989 } else {
4990 unreachable!("expected Insert");
4991 }
4992 }
4993
4994 #[test]
4995 fn test_upsert_do_nothing() {
4996 let stmt = parse_one("INSERT INTO t (a) VALUES (1) ON CONFLICT (a) DO NOTHING");
4997 if let Statement::Insert(i) = stmt {
4998 assert_eq!(i.upsert.len(), 1);
4999 assert!(matches!(i.upsert[0].action, UpsertAction::Nothing));
5000 } else {
5001 unreachable!("expected Insert");
5002 }
5003 }
5004
5005 #[test]
5006 fn test_upsert_excluded_pseudo_table() {
5007 let stmt = parse_one(
5008 "INSERT INTO t (a, b) VALUES (1, 2) \
5009 ON CONFLICT (a) DO UPDATE SET b = excluded.b, a = excluded.a + 1",
5010 );
5011 if let Statement::Insert(i) = stmt {
5012 assert_eq!(i.upsert.len(), 1);
5013 if let UpsertAction::Update { assignments, .. } = &i.upsert[0].action {
5014 assert_eq!(assignments.len(), 2);
5015 match &assignments[0].value {
5017 Expr::Column(col, _) => {
5018 assert_eq!(col.table.as_deref(), Some("excluded"));
5019 assert_eq!(col.column.as_ref(), "b");
5020 }
5021 other => unreachable!("expected Column ref to excluded.b, got {other:?}"),
5022 }
5023 } else {
5024 unreachable!("expected Update action");
5025 }
5026 } else {
5027 unreachable!("expected Insert");
5028 }
5029 }
5030
5031 #[test]
5032 fn test_upsert_multiple_on_conflict() {
5033 let stmt = parse_one(
5034 "INSERT INTO t (a, b) VALUES (1, 2) \
5035 ON CONFLICT (a) DO NOTHING \
5036 ON CONFLICT (b) DO UPDATE SET a = excluded.a",
5037 );
5038 if let Statement::Insert(i) = stmt {
5039 assert_eq!(i.upsert.len(), 2);
5040 assert!(matches!(i.upsert[0].action, UpsertAction::Nothing));
5041 assert!(matches!(i.upsert[1].action, UpsertAction::Update { .. }));
5042 } else {
5043 unreachable!("expected Insert");
5044 }
5045 }
5046
5047 #[test]
5048 fn test_upsert_where_on_conflict_target() {
5049 let stmt = parse_one(
5050 "INSERT INTO t (a, b) VALUES (1, 2) \
5051 ON CONFLICT (a) WHERE a > 0 DO UPDATE SET b = excluded.b WHERE b < 100",
5052 );
5053 if let Statement::Insert(i) = stmt {
5054 assert_eq!(i.upsert.len(), 1);
5055 let target = i.upsert[0].target.as_ref().expect("conflict target");
5056 assert!(target.where_clause.is_some(), "target WHERE missing");
5057 if let UpsertAction::Update { where_clause, .. } = &i.upsert[0].action {
5058 assert!(where_clause.is_some(), "action WHERE missing");
5059 } else {
5060 unreachable!("expected Update action");
5061 }
5062 } else {
5063 unreachable!("expected Insert");
5064 }
5065 }
5066
5067 #[test]
5068 fn test_returning_insert() {
5069 let stmt = parse_one("INSERT INTO t (a, b) VALUES (1, 2) RETURNING a, b, rowid");
5070 if let Statement::Insert(i) = stmt {
5071 assert_eq!(i.returning.len(), 3);
5072 } else {
5073 unreachable!("expected Insert");
5074 }
5075 }
5076
5077 #[test]
5078 fn test_returning_insert_select_with_semicolon() {
5079 let stmt = parse_one("INSERT INTO t2 SELECT * FROM t RETURNING *;");
5080 if let Statement::Insert(i) = stmt {
5081 assert!(matches!(i.source, InsertSource::Select(_)));
5082 assert_eq!(i.returning.len(), 1);
5083 assert!(matches!(i.returning[0], ResultColumn::Star));
5084 } else {
5085 unreachable!("expected Insert");
5086 }
5087 }
5088
5089 #[test]
5090 fn test_returning_reflects_before_triggers() {
5091 let stmt = parse_one("INSERT INTO t (a) VALUES (1) RETURNING a AS modified_a");
5095 if let Statement::Insert(i) = stmt {
5096 assert_eq!(i.returning.len(), 1);
5097 match &i.returning[0] {
5098 ResultColumn::Expr { alias, .. } => {
5099 assert_eq!(alias.as_deref(), Some("modified_a"));
5100 }
5101 other => unreachable!("expected Expr result column, got {other:?}"),
5102 }
5103 } else {
5104 unreachable!("expected Insert");
5105 }
5106 }
5107
5108 #[test]
5109 fn test_returning_ignores_after_triggers() {
5110 let stmt = parse_one("INSERT OR REPLACE INTO t (a) VALUES (1) RETURNING *");
5114 if let Statement::Insert(i) = stmt {
5115 assert_eq!(i.or_conflict, Some(ConflictAction::Replace));
5116 assert_eq!(i.returning.len(), 1);
5117 assert!(matches!(i.returning[0], ResultColumn::Star));
5118 } else {
5119 unreachable!("expected Insert");
5120 }
5121 }
5122
5123 #[test]
5124 fn test_returning_after_before_trigger_modify() {
5125 let stmt = parse_one("INSERT INTO t (a, b) VALUES (1, 2) RETURNING a, b, a + b AS total");
5129 if let Statement::Insert(i) = stmt {
5130 assert_eq!(i.returning.len(), 3);
5131 match &i.returning[2] {
5132 ResultColumn::Expr {
5133 alias: Some(alias), ..
5134 } => assert_eq!(alias, "total"),
5135 other => unreachable!("expected aliased expression, got {other:?}"),
5136 }
5137 } else {
5138 unreachable!("expected Insert");
5139 }
5140 }
5141
5142 #[test]
5143 fn test_returning_before_trigger_raise_abort() {
5144 let stmt = parse_one("INSERT INTO t (a) VALUES (1), (2), (3) RETURNING a");
5148 if let Statement::Insert(i) = stmt {
5149 if let InsertSource::Values(rows) = &i.source {
5150 assert_eq!(rows.len(), 3);
5151 } else {
5152 unreachable!("expected Values source");
5153 }
5154 assert_eq!(i.returning.len(), 1);
5155 } else {
5156 unreachable!("expected Insert");
5157 }
5158 }
5159
5160 #[test]
5161 fn test_returning_instead_of_view() {
5162 let stmt = parse_one("INSERT INTO v (a, b) VALUES (1, 2) RETURNING *");
5165 if let Statement::Insert(i) = stmt {
5166 assert_eq!(i.table.name, "v");
5167 assert!(!i.returning.is_empty());
5168 } else {
5169 unreachable!("expected Insert");
5170 }
5171 }
5172
5173 #[test]
5174 fn test_returning_autoincrement_with_trigger() {
5175 let stmt = parse_one("INSERT INTO t (name) VALUES ('test') RETURNING rowid, name");
5178 if let Statement::Insert(i) = stmt {
5179 assert_eq!(i.returning.len(), 2);
5180 } else {
5181 unreachable!("expected Insert");
5182 }
5183 }
5184
5185 #[test]
5186 fn test_update_set_where() {
5187 let stmt = parse_one("UPDATE t SET a = 1, b = 'hello' WHERE id = 42");
5188 if let Statement::Update(u) = stmt {
5189 assert_eq!(u.assignments.len(), 2);
5190 assert!(u.where_clause.is_some());
5191 assert!(u.from.is_none());
5192 } else {
5193 unreachable!("expected Update");
5194 }
5195 }
5196
5197 #[test]
5198 fn test_update_from_join() {
5199 let stmt = parse_one("UPDATE t1 SET a = t2.x FROM t2 WHERE t1.id = t2.id");
5200 if let Statement::Update(u) = stmt {
5201 assert_eq!(u.assignments.len(), 1);
5202 assert!(u.from.is_some());
5203 assert!(u.where_clause.is_some());
5204 } else {
5205 unreachable!("expected Update");
5206 }
5207 }
5208
5209 #[test]
5210 fn test_update_from_multi_match() {
5211 let stmt = parse_one(
5214 "UPDATE t1 SET val = src.val FROM src \
5215 INNER JOIN mapping ON mapping.src_id = src.id \
5216 WHERE t1.id = mapping.dst_id",
5217 );
5218 if let Statement::Update(u) = stmt {
5219 assert!(u.from.is_some());
5220 let from = u.from.as_ref().unwrap();
5221 assert!(!from.joins.is_empty(), "expected JOIN in FROM clause");
5222 } else {
5223 unreachable!("expected Update");
5224 }
5225 }
5226
5227 #[test]
5228 fn test_update_order_by_limit() {
5229 let stmt = parse_one("UPDATE t SET a = a + 1 ORDER BY b DESC LIMIT 10");
5230 if let Statement::Update(u) = stmt {
5231 assert_eq!(u.order_by.len(), 1);
5232 assert_eq!(u.order_by[0].direction, Some(SortDirection::Desc));
5233 assert!(u.limit.is_some());
5234 } else {
5235 unreachable!("expected Update");
5236 }
5237 }
5238
5239 #[test]
5240 fn test_update_returning() {
5241 let stmt = parse_one("UPDATE t SET a = 1 WHERE id = 5 RETURNING id, a AS new_a");
5242 if let Statement::Update(u) = stmt {
5243 assert_eq!(u.returning.len(), 2);
5244 match &u.returning[1] {
5245 ResultColumn::Expr {
5246 alias: Some(alias), ..
5247 } => assert_eq!(alias, "new_a"),
5248 other => unreachable!("expected aliased result column, got {other:?}"),
5249 }
5250 } else {
5251 unreachable!("expected Update");
5252 }
5253 }
5254
5255 #[test]
5256 fn test_update_or_ignore() {
5257 let stmt = parse_one("UPDATE OR IGNORE t SET a = 1 WHERE id = 5");
5258 if let Statement::Update(u) = stmt {
5259 assert_eq!(u.or_conflict, Some(ConflictAction::Ignore));
5260 assert!(u.where_clause.is_some());
5261 } else {
5262 unreachable!("expected Update");
5263 }
5264 }
5265
5266 #[test]
5267 fn test_delete_where() {
5268 let stmt = parse_one("DELETE FROM t WHERE id = 42 AND active = 0");
5269 if let Statement::Delete(d) = stmt {
5270 assert!(d.where_clause.is_some());
5271 assert!(d.returning.is_empty());
5272 } else {
5273 unreachable!("expected Delete");
5274 }
5275 }
5276
5277 #[test]
5278 fn test_delete_order_by_limit() {
5279 let stmt = parse_one("DELETE FROM t ORDER BY created_at ASC LIMIT 100");
5280 if let Statement::Delete(d) = stmt {
5281 assert_eq!(d.order_by.len(), 1);
5282 assert_eq!(d.order_by[0].direction, Some(SortDirection::Asc));
5283 let limit = d.limit.as_ref().expect("LIMIT clause");
5284 assert!(matches!(
5285 limit.limit,
5286 Expr::Literal(Literal::Integer(100), _)
5287 ));
5288 } else {
5289 unreachable!("expected Delete");
5290 }
5291 }
5292
5293 #[test]
5294 fn test_delete_returning() {
5295 let stmt = parse_one("DELETE FROM t WHERE id = 1 RETURNING *");
5296 if let Statement::Delete(d) = stmt {
5297 assert!(d.where_clause.is_some());
5298 assert_eq!(d.returning.len(), 1);
5299 assert!(matches!(d.returning[0], ResultColumn::Star));
5300 } else {
5301 unreachable!("expected Delete");
5302 }
5303 }
5304
5305 #[test]
5306 fn test_delete_bulk_optimization() {
5307 let stmt = parse_one("DELETE FROM t");
5310 if let Statement::Delete(d) = stmt {
5311 assert!(d.where_clause.is_none());
5312 assert!(d.order_by.is_empty());
5313 assert!(d.limit.is_none());
5314 assert!(d.returning.is_empty());
5315 } else {
5316 unreachable!("expected Delete");
5317 }
5318 }
5319
5320 #[test]
5321 fn test_delete_bulk_no_where_fast() {
5322 let stmt = parse_one("DELETE FROM main.t");
5325 if let Statement::Delete(d) = stmt {
5326 assert_eq!(d.table.name.schema.as_deref(), Some("main"));
5327 assert_eq!(d.table.name.name, "t");
5328 assert!(d.where_clause.is_none());
5329 } else {
5330 unreachable!("expected Delete");
5331 }
5332 }
5333
5334 #[test]
5335 fn test_delete_bulk_blocked_by_trigger() {
5336 let stmt = parse_one("DELETE FROM orders");
5339 if let Statement::Delete(d) = stmt {
5340 assert!(d.where_clause.is_none());
5341 assert!(d.returning.is_empty());
5342 } else {
5343 unreachable!("expected Delete");
5344 }
5345 }
5346
5347 #[test]
5348 fn test_delete_bulk_blocked_by_fk() {
5349 let stmt = parse_one("DELETE FROM parent_table");
5352 if let Statement::Delete(d) = stmt {
5353 assert!(d.where_clause.is_none());
5354 } else {
5355 unreachable!("expected Delete");
5356 }
5357 }
5358
5359 #[test]
5360 fn test_delete_bulk_changes_count() {
5361 let stmt = parse_one("DELETE FROM t");
5365 if let Statement::Delete(d) = stmt {
5366 assert!(d.where_clause.is_none());
5367 } else {
5368 unreachable!("expected Delete");
5369 }
5370 }
5371
5372 #[test]
5373 fn test_delete_bulk_autoincrement_preserved() {
5374 let stmt = parse_one("DELETE FROM t");
5378 if let Statement::Delete(d) = stmt {
5379 assert!(d.where_clause.is_none());
5380 assert!(d.limit.is_none());
5381 } else {
5382 unreachable!("expected Delete");
5383 }
5384 }
5385
5386 #[test]
5387 fn test_delete_bulk_where_1_not_optimized() {
5388 let stmt = parse_one("DELETE FROM t WHERE 1");
5391 if let Statement::Delete(d) = stmt {
5392 assert!(
5393 d.where_clause.is_some(),
5394 "WHERE 1 must produce a where_clause"
5395 );
5396 assert!(matches!(
5397 d.where_clause.as_ref().unwrap(),
5398 Expr::Literal(Literal::Integer(1), _)
5399 ));
5400 } else {
5401 unreachable!("expected Delete");
5402 }
5403 }
5404
5405 #[test]
5410 fn test_create_table_basic() {
5411 let stmt = parse_one("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)");
5412 if let Statement::CreateTable(ct) = stmt {
5413 assert_eq!(ct.name.name, "users");
5414 assert!(!ct.if_not_exists);
5415 assert!(!ct.temporary);
5416 assert!(!ct.without_rowid);
5417 assert!(!ct.strict);
5418 if let CreateTableBody::Columns { columns, .. } = &ct.body {
5419 assert_eq!(columns.len(), 3);
5420 assert_eq!(columns[0].name, "id");
5421 assert_eq!(columns[1].name, "name");
5422 assert_eq!(columns[2].name, "age");
5423 } else {
5424 unreachable!("expected Columns body");
5425 }
5426 } else {
5427 unreachable!("expected CreateTable");
5428 }
5429 }
5430
5431 #[test]
5432 fn test_create_table_if_not_exists() {
5433 let stmt = parse_one("CREATE TABLE IF NOT EXISTS t (id INTEGER)");
5434 if let Statement::CreateTable(ct) = stmt {
5435 assert!(ct.if_not_exists);
5436 } else {
5437 unreachable!("expected CreateTable");
5438 }
5439 }
5440
5441 #[test]
5442 fn test_create_temp_table() {
5443 let stmt = parse_one("CREATE TEMP TABLE session_data (key TEXT, val BLOB)");
5444 if let Statement::CreateTable(ct) = stmt {
5445 assert!(ct.temporary);
5446 } else {
5447 unreachable!("expected CreateTable");
5448 }
5449 }
5450
5451 #[test]
5452 fn test_create_table_as_select() {
5453 let stmt = parse_one("CREATE TABLE t2 AS SELECT id, name FROM t1 WHERE active = 1");
5454 if let Statement::CreateTable(ct) = stmt {
5455 assert!(matches!(ct.body, CreateTableBody::AsSelect(_)));
5456 } else {
5457 unreachable!("expected CreateTable");
5458 }
5459 }
5460
5461 #[test]
5462 fn test_column_primary_key() {
5463 let stmt = parse_one("CREATE TABLE t (id INTEGER PRIMARY KEY ASC)");
5464 if let Statement::CreateTable(ct) = stmt {
5465 if let CreateTableBody::Columns { columns, .. } = &ct.body {
5466 let pk = columns[0]
5467 .constraints
5468 .iter()
5469 .find(|c| matches!(c.kind, ColumnConstraintKind::PrimaryKey { .. }));
5470 assert!(pk.is_some(), "PK constraint missing");
5471 if let ColumnConstraintKind::PrimaryKey { direction, .. } = &pk.unwrap().kind {
5472 assert_eq!(*direction, Some(SortDirection::Asc));
5473 }
5474 } else {
5475 unreachable!("expected Columns body");
5476 }
5477 } else {
5478 unreachable!("expected CreateTable");
5479 }
5480 }
5481
5482 #[test]
5483 fn test_column_primary_key_autoincrement() {
5484 let stmt = parse_one("CREATE TABLE t (id INTEGER PRIMARY KEY AUTOINCREMENT)");
5485 if let Statement::CreateTable(ct) = stmt {
5486 if let CreateTableBody::Columns { columns, .. } = &ct.body {
5487 let pk = columns[0]
5488 .constraints
5489 .iter()
5490 .find(|c| matches!(c.kind, ColumnConstraintKind::PrimaryKey { .. }));
5491 if let ColumnConstraintKind::PrimaryKey { autoincrement, .. } = &pk.unwrap().kind {
5492 assert!(autoincrement, "AUTOINCREMENT flag not set");
5493 }
5494 } else {
5495 unreachable!("expected Columns body");
5496 }
5497 } else {
5498 unreachable!("expected CreateTable");
5499 }
5500 }
5501
5502 #[test]
5503 fn test_autoincrement_uses_sqlite_sequence() {
5504 let stmt = parse_one("CREATE TABLE t (id INTEGER PRIMARY KEY AUTOINCREMENT, val TEXT)");
5507 if let Statement::CreateTable(ct) = stmt {
5508 if let CreateTableBody::Columns { columns, .. } = &ct.body {
5509 assert_eq!(columns.len(), 2);
5510 let pk = columns[0].constraints.iter().find(|c| {
5511 matches!(
5512 c.kind,
5513 ColumnConstraintKind::PrimaryKey {
5514 autoincrement: true,
5515 ..
5516 }
5517 )
5518 });
5519 assert!(pk.is_some(), "AUTOINCREMENT constraint missing");
5520 } else {
5521 unreachable!("expected Columns body");
5522 }
5523 } else {
5524 unreachable!("expected CreateTable");
5525 }
5526 }
5527
5528 #[test]
5529 fn test_column_not_null() {
5530 let stmt = parse_one("CREATE TABLE t (name TEXT NOT NULL)");
5531 if let Statement::CreateTable(ct) = stmt {
5532 if let CreateTableBody::Columns { columns, .. } = &ct.body {
5533 let nn = columns[0]
5534 .constraints
5535 .iter()
5536 .find(|c| matches!(c.kind, ColumnConstraintKind::NotNull { .. }));
5537 assert!(nn.is_some(), "NOT NULL constraint missing");
5538 } else {
5539 unreachable!("expected Columns body");
5540 }
5541 } else {
5542 unreachable!("expected CreateTable");
5543 }
5544 }
5545
5546 #[test]
5547 fn test_column_unique() {
5548 let stmt = parse_one("CREATE TABLE t (email TEXT UNIQUE)");
5549 if let Statement::CreateTable(ct) = stmt {
5550 if let CreateTableBody::Columns { columns, .. } = &ct.body {
5551 let uq = columns[0]
5552 .constraints
5553 .iter()
5554 .find(|c| matches!(c.kind, ColumnConstraintKind::Unique { .. }));
5555 assert!(uq.is_some(), "UNIQUE constraint missing");
5556 } else {
5557 unreachable!("expected Columns body");
5558 }
5559 } else {
5560 unreachable!("expected CreateTable");
5561 }
5562 }
5563
5564 #[test]
5565 fn test_column_check() {
5566 let stmt = parse_one("CREATE TABLE t (age INTEGER CHECK(age >= 0 AND age < 200))");
5567 if let Statement::CreateTable(ct) = stmt {
5568 if let CreateTableBody::Columns { columns, .. } = &ct.body {
5569 let chk = columns[0]
5570 .constraints
5571 .iter()
5572 .find(|c| matches!(c.kind, ColumnConstraintKind::Check(_)));
5573 assert!(chk.is_some(), "CHECK constraint missing");
5574 } else {
5575 unreachable!("expected Columns body");
5576 }
5577 } else {
5578 unreachable!("expected CreateTable");
5579 }
5580 }
5581
5582 #[test]
5583 fn test_column_default_literal() {
5584 let stmt = parse_one("CREATE TABLE t (status TEXT DEFAULT 'active')");
5585 if let Statement::CreateTable(ct) = stmt {
5586 if let CreateTableBody::Columns { columns, .. } = &ct.body {
5587 let def = columns[0]
5588 .constraints
5589 .iter()
5590 .find(|c| matches!(c.kind, ColumnConstraintKind::Default(_)));
5591 assert!(def.is_some(), "DEFAULT constraint missing");
5592 } else {
5593 unreachable!("expected Columns body");
5594 }
5595 } else {
5596 unreachable!("expected CreateTable");
5597 }
5598 }
5599
5600 #[test]
5601 fn test_column_default_expr() {
5602 let stmt = parse_one("CREATE TABLE t (created_at TEXT DEFAULT (datetime('now')))");
5603 if let Statement::CreateTable(ct) = stmt {
5604 if let CreateTableBody::Columns { columns, .. } = &ct.body {
5605 let def = columns[0].constraints.iter().find(|c| {
5606 matches!(
5607 c.kind,
5608 ColumnConstraintKind::Default(DefaultValue::ParenExpr(_))
5609 )
5610 });
5611 assert!(def.is_some(), "DEFAULT (expr) missing");
5612 } else {
5613 unreachable!("expected Columns body");
5614 }
5615 } else {
5616 unreachable!("expected CreateTable");
5617 }
5618 }
5619
5620 #[test]
5621 fn test_column_collate() {
5622 let stmt = parse_one("CREATE TABLE t (name TEXT COLLATE NOCASE)");
5623 if let Statement::CreateTable(ct) = stmt {
5624 if let CreateTableBody::Columns { columns, .. } = &ct.body {
5625 let coll = columns[0]
5626 .constraints
5627 .iter()
5628 .find(|c| matches!(c.kind, ColumnConstraintKind::Collate(_)));
5629 assert!(coll.is_some(), "COLLATE constraint missing");
5630 if let ColumnConstraintKind::Collate(name) = &coll.unwrap().kind {
5631 assert_eq!(name, "NOCASE");
5632 }
5633 } else {
5634 unreachable!("expected Columns body");
5635 }
5636 } else {
5637 unreachable!("expected CreateTable");
5638 }
5639 }
5640
5641 #[test]
5642 fn test_table_constraint_composite_pk() {
5643 let stmt = parse_one("CREATE TABLE t (a INTEGER, b INTEGER, PRIMARY KEY (a, b))");
5644 if let Statement::CreateTable(ct) = stmt {
5645 if let CreateTableBody::Columns { constraints, .. } = &ct.body {
5646 let pk = constraints
5647 .iter()
5648 .find(|c| matches!(c.kind, TableConstraintKind::PrimaryKey { .. }));
5649 assert!(pk.is_some(), "composite PK missing");
5650 if let TableConstraintKind::PrimaryKey { columns, .. } = &pk.unwrap().kind {
5651 assert_eq!(columns.len(), 2);
5652 }
5653 } else {
5654 unreachable!("expected Columns body");
5655 }
5656 } else {
5657 unreachable!("expected CreateTable");
5658 }
5659 }
5660
5661 #[test]
5662 fn test_table_constraint_composite_unique() {
5663 let stmt = parse_one("CREATE TABLE t (a TEXT, b TEXT, UNIQUE (a, b))");
5664 if let Statement::CreateTable(ct) = stmt {
5665 if let CreateTableBody::Columns { constraints, .. } = &ct.body {
5666 let uq = constraints
5667 .iter()
5668 .find(|c| matches!(c.kind, TableConstraintKind::Unique { .. }));
5669 assert!(uq.is_some(), "composite UNIQUE missing");
5670 if let TableConstraintKind::Unique { columns, .. } = &uq.unwrap().kind {
5671 assert_eq!(columns.len(), 2);
5672 }
5673 } else {
5674 unreachable!("expected Columns body");
5675 }
5676 } else {
5677 unreachable!("expected CreateTable");
5678 }
5679 }
5680
5681 #[test]
5682 fn test_table_constraint_check() {
5683 let stmt = parse_one(
5684 "CREATE TABLE t (start_date TEXT, end_date TEXT, CHECK (start_date < end_date))",
5685 );
5686 if let Statement::CreateTable(ct) = stmt {
5687 if let CreateTableBody::Columns { constraints, .. } = &ct.body {
5688 let chk = constraints
5689 .iter()
5690 .find(|c| matches!(c.kind, TableConstraintKind::Check(_)));
5691 assert!(chk.is_some(), "table CHECK constraint missing");
5692 } else {
5693 unreachable!("expected Columns body");
5694 }
5695 } else {
5696 unreachable!("expected CreateTable");
5697 }
5698 }
5699
5700 #[test]
5701 fn test_foreign_key_on_delete_cascade() {
5702 let stmt = parse_one(
5703 "CREATE TABLE child (id INTEGER, parent_id INTEGER \
5704 REFERENCES parent(id) ON DELETE CASCADE)",
5705 );
5706 if let Statement::CreateTable(ct) = stmt {
5707 if let CreateTableBody::Columns { columns, .. } = &ct.body {
5708 let fk = columns[1]
5709 .constraints
5710 .iter()
5711 .find(|c| matches!(c.kind, ColumnConstraintKind::ForeignKey(_)));
5712 assert!(fk.is_some(), "FK constraint missing");
5713 if let ColumnConstraintKind::ForeignKey(clause) = &fk.unwrap().kind {
5714 assert_eq!(clause.table, "parent");
5715 let del = clause
5716 .actions
5717 .iter()
5718 .find(|a| a.trigger == ForeignKeyTrigger::OnDelete);
5719 assert!(del.is_some());
5720 assert_eq!(del.unwrap().action, ForeignKeyActionType::Cascade);
5721 }
5722 } else {
5723 unreachable!("expected Columns body");
5724 }
5725 } else {
5726 unreachable!("expected CreateTable");
5727 }
5728 }
5729
5730 #[test]
5731 fn test_foreign_key_on_delete_set_null() {
5732 let stmt = parse_one(
5733 "CREATE TABLE child (id INTEGER, parent_id INTEGER \
5734 REFERENCES parent(id) ON DELETE SET NULL)",
5735 );
5736 if let Statement::CreateTable(ct) = stmt {
5737 if let CreateTableBody::Columns { columns, .. } = &ct.body {
5738 if let ColumnConstraintKind::ForeignKey(clause) = &columns[1]
5739 .constraints
5740 .iter()
5741 .find(|c| matches!(c.kind, ColumnConstraintKind::ForeignKey(_)))
5742 .unwrap()
5743 .kind
5744 {
5745 let del = clause
5746 .actions
5747 .iter()
5748 .find(|a| a.trigger == ForeignKeyTrigger::OnDelete);
5749 assert_eq!(del.unwrap().action, ForeignKeyActionType::SetNull);
5750 }
5751 } else {
5752 unreachable!("expected Columns body");
5753 }
5754 } else {
5755 unreachable!("expected CreateTable");
5756 }
5757 }
5758
5759 #[test]
5760 fn test_foreign_key_on_update_cascade() {
5761 let stmt = parse_one(
5762 "CREATE TABLE child (id INTEGER, parent_id INTEGER \
5763 REFERENCES parent(id) ON UPDATE CASCADE)",
5764 );
5765 if let Statement::CreateTable(ct) = stmt {
5766 if let CreateTableBody::Columns { columns, .. } = &ct.body {
5767 if let ColumnConstraintKind::ForeignKey(clause) = &columns[1]
5768 .constraints
5769 .iter()
5770 .find(|c| matches!(c.kind, ColumnConstraintKind::ForeignKey(_)))
5771 .unwrap()
5772 .kind
5773 {
5774 let upd = clause
5775 .actions
5776 .iter()
5777 .find(|a| a.trigger == ForeignKeyTrigger::OnUpdate);
5778 assert!(upd.is_some());
5779 assert_eq!(upd.unwrap().action, ForeignKeyActionType::Cascade);
5780 }
5781 } else {
5782 unreachable!("expected Columns body");
5783 }
5784 } else {
5785 unreachable!("expected CreateTable");
5786 }
5787 }
5788
5789 #[test]
5790 fn test_foreign_key_restrict() {
5791 let stmt = parse_one(
5792 "CREATE TABLE child (id INTEGER, parent_id INTEGER \
5793 REFERENCES parent(id) ON DELETE RESTRICT)",
5794 );
5795 if let Statement::CreateTable(ct) = stmt {
5796 if let CreateTableBody::Columns { columns, .. } = &ct.body {
5797 if let ColumnConstraintKind::ForeignKey(clause) = &columns[1]
5798 .constraints
5799 .iter()
5800 .find(|c| matches!(c.kind, ColumnConstraintKind::ForeignKey(_)))
5801 .unwrap()
5802 .kind
5803 {
5804 let del = clause
5805 .actions
5806 .iter()
5807 .find(|a| a.trigger == ForeignKeyTrigger::OnDelete);
5808 assert_eq!(del.unwrap().action, ForeignKeyActionType::Restrict);
5809 }
5810 } else {
5811 unreachable!("expected Columns body");
5812 }
5813 } else {
5814 unreachable!("expected CreateTable");
5815 }
5816 }
5817
5818 #[test]
5819 fn test_foreign_key_deferred() {
5820 let stmt = parse_one(
5821 "CREATE TABLE child (id INTEGER, parent_id INTEGER \
5822 REFERENCES parent(id) DEFERRABLE INITIALLY DEFERRED)",
5823 );
5824 if let Statement::CreateTable(ct) = stmt {
5825 if let CreateTableBody::Columns { columns, .. } = &ct.body {
5826 if let ColumnConstraintKind::ForeignKey(clause) = &columns[1]
5827 .constraints
5828 .iter()
5829 .find(|c| matches!(c.kind, ColumnConstraintKind::ForeignKey(_)))
5830 .unwrap()
5831 .kind
5832 {
5833 let def = clause.deferrable.as_ref().expect("DEFERRABLE missing");
5834 assert!(!def.not, "should be DEFERRABLE, not NOT DEFERRABLE");
5835 assert_eq!(def.initially, Some(DeferrableInitially::Deferred));
5836 }
5837 } else {
5838 unreachable!("expected Columns body");
5839 }
5840 } else {
5841 unreachable!("expected CreateTable");
5842 }
5843 }
5844
5845 #[test]
5846 fn test_foreign_key_pragma_required() {
5847 let stmt = parse_one(
5850 "CREATE TABLE child (id INTEGER, parent_id INTEGER \
5851 REFERENCES parent(id) ON DELETE CASCADE ON UPDATE SET NULL)",
5852 );
5853 if let Statement::CreateTable(ct) = stmt {
5854 if let CreateTableBody::Columns { columns, .. } = &ct.body {
5855 if let ColumnConstraintKind::ForeignKey(clause) = &columns[1]
5856 .constraints
5857 .iter()
5858 .find(|c| matches!(c.kind, ColumnConstraintKind::ForeignKey(_)))
5859 .unwrap()
5860 .kind
5861 {
5862 assert_eq!(clause.actions.len(), 2);
5863 }
5864 } else {
5865 unreachable!("expected Columns body");
5866 }
5867 } else {
5868 unreachable!("expected CreateTable");
5869 }
5870 }
5871
5872 #[test]
5873 fn test_conflict_clause_on_not_null() {
5874 let stmt = parse_one("CREATE TABLE t (name TEXT NOT NULL ON CONFLICT IGNORE)");
5875 if let Statement::CreateTable(ct) = stmt {
5876 if let CreateTableBody::Columns { columns, .. } = &ct.body {
5877 let nn = columns[0]
5878 .constraints
5879 .iter()
5880 .find(|c| matches!(c.kind, ColumnConstraintKind::NotNull { .. }));
5881 if let ColumnConstraintKind::NotNull { conflict } = &nn.unwrap().kind {
5882 assert_eq!(*conflict, Some(ConflictAction::Ignore));
5883 }
5884 } else {
5885 unreachable!("expected Columns body");
5886 }
5887 } else {
5888 unreachable!("expected CreateTable");
5889 }
5890 }
5891
5892 #[test]
5893 fn test_without_rowid_table() {
5894 let stmt = parse_one("CREATE TABLE t (k TEXT PRIMARY KEY, v BLOB) WITHOUT ROWID");
5895 if let Statement::CreateTable(ct) = stmt {
5896 assert!(ct.without_rowid);
5897 } else {
5898 unreachable!("expected CreateTable");
5899 }
5900 }
5901
5902 #[test]
5903 fn test_without_rowid_no_autoincrement() {
5904 let stmt = parse_one(
5907 "CREATE TABLE t (id INTEGER PRIMARY KEY AUTOINCREMENT, v TEXT) WITHOUT ROWID",
5908 );
5909 if let Statement::CreateTable(ct) = stmt {
5910 assert!(ct.without_rowid);
5911 if let CreateTableBody::Columns { columns, .. } = &ct.body {
5912 let pk = columns[0].constraints.iter().find(|c| {
5913 matches!(
5914 c.kind,
5915 ColumnConstraintKind::PrimaryKey {
5916 autoincrement: true,
5917 ..
5918 }
5919 )
5920 });
5921 assert!(pk.is_some());
5922 } else {
5923 unreachable!("expected Columns body");
5924 }
5925 } else {
5926 unreachable!("expected CreateTable");
5927 }
5928 }
5929
5930 #[test]
5931 fn test_without_rowid_integer_pk_not_alias() {
5932 let stmt = parse_one("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT) WITHOUT ROWID");
5935 if let Statement::CreateTable(ct) = stmt {
5936 assert!(ct.without_rowid);
5937 if let CreateTableBody::Columns { columns, .. } = &ct.body {
5938 assert_eq!(columns[0].name, "id");
5939 assert!(columns[0].type_name.is_some());
5940 } else {
5941 unreachable!("expected Columns body");
5942 }
5943 } else {
5944 unreachable!("expected CreateTable");
5945 }
5946 }
5947
5948 #[test]
5949 fn test_strict_table_type_enforcement() {
5950 let stmt = parse_one("CREATE TABLE t (id INTEGER, name TEXT, score REAL) STRICT");
5953 if let Statement::CreateTable(ct) = stmt {
5954 assert!(ct.strict);
5955 assert!(!ct.without_rowid);
5956 } else {
5957 unreachable!("expected CreateTable");
5958 }
5959 }
5960
5961 #[test]
5962 fn test_strict_table_any_column() {
5963 let stmt = parse_one("CREATE TABLE t (id INTEGER, data ANY) STRICT");
5965 if let Statement::CreateTable(ct) = stmt {
5966 assert!(ct.strict);
5967 if let CreateTableBody::Columns { columns, .. } = &ct.body {
5968 let tn = columns[1].type_name.as_ref().expect("type name");
5969 assert_eq!(tn.name, "ANY");
5970 } else {
5971 unreachable!("expected Columns body");
5972 }
5973 } else {
5974 unreachable!("expected CreateTable");
5975 }
5976 }
5977
5978 #[test]
5979 fn test_strict_allowed_types() {
5980 let stmt =
5982 parse_one("CREATE TABLE t (a INT, b INTEGER, c REAL, d TEXT, e BLOB, f ANY) STRICT");
5983 if let Statement::CreateTable(ct) = stmt {
5984 assert!(ct.strict);
5985 if let CreateTableBody::Columns { columns, .. } = &ct.body {
5986 assert_eq!(columns.len(), 6);
5987 let types: Vec<&str> = columns
5988 .iter()
5989 .map(|c| c.type_name.as_ref().unwrap().name.as_str())
5990 .collect();
5991 assert_eq!(types, vec!["INT", "INTEGER", "REAL", "TEXT", "BLOB", "ANY"]);
5992 } else {
5993 unreachable!("expected Columns body");
5994 }
5995 } else {
5996 unreachable!("expected CreateTable");
5997 }
5998 }
5999
6000 #[test]
6001 fn test_generated_col_virtual() {
6002 let stmt = parse_one(
6003 "CREATE TABLE t (a INTEGER, b INTEGER, c INTEGER GENERATED ALWAYS AS (a + b) VIRTUAL)",
6004 );
6005 if let Statement::CreateTable(ct) = stmt {
6006 if let CreateTableBody::Columns { columns, .. } = &ct.body {
6007 let generated = columns[2]
6008 .constraints
6009 .iter()
6010 .find(|c| matches!(c.kind, ColumnConstraintKind::Generated { .. }));
6011 assert!(generated.is_some(), "Generated constraint missing");
6012 if let ColumnConstraintKind::Generated { storage, .. } = &generated.unwrap().kind {
6013 assert_eq!(*storage, Some(GeneratedStorage::Virtual));
6014 }
6015 } else {
6016 unreachable!("expected Columns body");
6017 }
6018 } else {
6019 unreachable!("expected CreateTable");
6020 }
6021 }
6022
6023 #[test]
6024 fn test_generated_col_stored() {
6025 let stmt = parse_one(
6026 "CREATE TABLE t (a INTEGER, b INTEGER, c INTEGER GENERATED ALWAYS AS (a * b) STORED)",
6027 );
6028 if let Statement::CreateTable(ct) = stmt {
6029 if let CreateTableBody::Columns { columns, .. } = &ct.body {
6030 let generated = columns[2]
6031 .constraints
6032 .iter()
6033 .find(|c| matches!(c.kind, ColumnConstraintKind::Generated { .. }));
6034 if let ColumnConstraintKind::Generated { storage, .. } = &generated.unwrap().kind {
6035 assert_eq!(*storage, Some(GeneratedStorage::Stored));
6036 }
6037 } else {
6038 unreachable!("expected Columns body");
6039 }
6040 } else {
6041 unreachable!("expected CreateTable");
6042 }
6043 }
6044
6045 #[test]
6046 fn test_generated_col_ordering() {
6047 let stmt = parse_one(
6050 "CREATE TABLE t (\
6051 a INTEGER, \
6052 b INTEGER GENERATED ALWAYS AS (a + 1) STORED, \
6053 c INTEGER GENERATED ALWAYS AS (b * 2) VIRTUAL)",
6054 );
6055 if let Statement::CreateTable(ct) = stmt {
6056 if let CreateTableBody::Columns { columns, .. } = &ct.body {
6057 assert_eq!(columns.len(), 3);
6058 let gen_b = columns[1]
6060 .constraints
6061 .iter()
6062 .any(|c| matches!(c.kind, ColumnConstraintKind::Generated { .. }));
6063 let gen_c = columns[2]
6064 .constraints
6065 .iter()
6066 .any(|c| matches!(c.kind, ColumnConstraintKind::Generated { .. }));
6067 assert!(gen_b, "column b should be generated");
6068 assert!(gen_c, "column c should be generated");
6069 } else {
6070 unreachable!("expected Columns body");
6071 }
6072 } else {
6073 unreachable!("expected CreateTable");
6074 }
6075 }
6076
6077 #[test]
6078 fn test_generated_col_stored_indexable() {
6079 let stmts = parse_ok(
6082 "CREATE TABLE t (a INTEGER, b INTEGER GENERATED ALWAYS AS (a * 2) STORED); \
6083 CREATE INDEX idx_b ON t (b)",
6084 );
6085 assert_eq!(stmts.len(), 2);
6086 assert!(matches!(stmts[0], Statement::CreateTable(_)));
6087 assert!(matches!(stmts[1], Statement::CreateIndex(_)));
6088 }
6089
6090 #[test]
6091 fn test_type_affinity_int() {
6092 let stmt = parse_one("CREATE TABLE t (a INTEGER, b BIGINT, c SMALLINT, d MEDIUMINT)");
6095 if let Statement::CreateTable(ct) = stmt {
6096 if let CreateTableBody::Columns { columns, .. } = &ct.body {
6097 assert_eq!(columns.len(), 4);
6098 for col in columns {
6099 let tn = col.type_name.as_ref().unwrap();
6100 assert!(tn.name.contains("INT"), "{} should contain INT", tn.name);
6101 }
6102 } else {
6103 unreachable!("expected Columns body");
6104 }
6105 } else {
6106 unreachable!("expected CreateTable");
6107 }
6108 }
6109
6110 #[test]
6111 fn test_type_affinity_text() {
6112 let stmt = parse_one("CREATE TABLE t (a TEXT, b VARCHAR, c CLOB, d CHARACTER)");
6113 if let Statement::CreateTable(ct) = stmt {
6114 if let CreateTableBody::Columns { columns, .. } = &ct.body {
6115 assert_eq!(columns.len(), 4);
6116 for col in columns {
6117 assert!(col.type_name.is_some());
6118 }
6119 } else {
6120 unreachable!("expected Columns body");
6121 }
6122 } else {
6123 unreachable!("expected CreateTable");
6124 }
6125 }
6126
6127 #[test]
6128 fn test_type_affinity_blob() {
6129 let stmt = parse_one("CREATE TABLE t (a BLOB, b)");
6130 if let Statement::CreateTable(ct) = stmt {
6131 if let CreateTableBody::Columns { columns, .. } = &ct.body {
6132 assert_eq!(columns.len(), 2);
6133 assert_eq!(columns[0].type_name.as_ref().unwrap().name, "BLOB");
6134 assert!(columns[1].type_name.is_none());
6136 } else {
6137 unreachable!("expected Columns body");
6138 }
6139 } else {
6140 unreachable!("expected CreateTable");
6141 }
6142 }
6143
6144 #[test]
6145 fn test_type_affinity_real() {
6146 let stmt = parse_one("CREATE TABLE t (a REAL, b DOUBLE, c FLOAT)");
6147 if let Statement::CreateTable(ct) = stmt {
6148 if let CreateTableBody::Columns { columns, .. } = &ct.body {
6149 assert_eq!(columns.len(), 3);
6150 for col in columns {
6151 assert!(col.type_name.is_some());
6152 }
6153 } else {
6154 unreachable!("expected Columns body");
6155 }
6156 } else {
6157 unreachable!("expected CreateTable");
6158 }
6159 }
6160
6161 #[test]
6162 fn test_type_affinity_numeric() {
6163 let stmt = parse_one("CREATE TABLE t (a NUMERIC, b DECIMAL, c BOOLEAN)");
6164 if let Statement::CreateTable(ct) = stmt {
6165 if let CreateTableBody::Columns { columns, .. } = &ct.body {
6166 assert_eq!(columns.len(), 3);
6167 for col in columns {
6168 assert!(col.type_name.is_some());
6169 }
6170 } else {
6171 unreachable!("expected Columns body");
6172 }
6173 } else {
6174 unreachable!("expected CreateTable");
6175 }
6176 }
6177
6178 #[test]
6179 fn test_create_unique_index() {
6180 let stmt = parse_one("CREATE UNIQUE INDEX idx_email ON users (email)");
6181 if let Statement::CreateIndex(ci) = stmt {
6182 assert!(ci.unique);
6183 assert_eq!(ci.name.name, "idx_email");
6184 assert_eq!(ci.table, "users");
6185 assert_eq!(ci.columns.len(), 1);
6186 assert!(ci.where_clause.is_none());
6187 } else {
6188 unreachable!("expected CreateIndex");
6189 }
6190 }
6191
6192 #[test]
6193 fn test_partial_index() {
6194 let stmt = parse_one("CREATE INDEX idx_active ON users (name) WHERE active = 1");
6195 if let Statement::CreateIndex(ci) = stmt {
6196 assert!(!ci.unique);
6197 assert_eq!(ci.name.name, "idx_active");
6198 assert!(ci.where_clause.is_some(), "partial index WHERE missing");
6199 } else {
6200 unreachable!("expected CreateIndex");
6201 }
6202 }
6203
6204 #[test]
6205 fn test_partial_index_planner_usage() {
6206 let stmt =
6209 parse_one("CREATE INDEX idx_recent ON orders (created_at) WHERE status != 'archived'");
6210 if let Statement::CreateIndex(ci) = stmt {
6211 assert!(ci.where_clause.is_some());
6212 assert_eq!(ci.columns.len(), 1);
6213 } else {
6214 unreachable!("expected CreateIndex");
6215 }
6216 }
6217
6218 #[test]
6219 fn test_expression_index() {
6220 let stmt = parse_one("CREATE INDEX idx_lower_name ON users (lower(name))");
6221 if let Statement::CreateIndex(ci) = stmt {
6222 assert_eq!(ci.columns.len(), 1);
6223 assert!(
6225 matches!(ci.columns[0].expr, Expr::FunctionCall { .. }),
6226 "expected function call expression in index"
6227 );
6228 } else {
6229 unreachable!("expected CreateIndex");
6230 }
6231 }
6232
6233 #[test]
6234 fn test_expression_index_planner_match() {
6235 let stmt = parse_one("CREATE INDEX idx_calc ON t (a + b * 2)");
6238 if let Statement::CreateIndex(ci) = stmt {
6239 assert_eq!(ci.columns.len(), 1);
6240 assert!(
6241 matches!(ci.columns[0].expr, Expr::BinaryOp { .. }),
6242 "expected binary op in expression index"
6243 );
6244 } else {
6245 unreachable!("expected CreateIndex");
6246 }
6247 }
6248
6249 #[test]
6250 fn test_index_collate_asc_desc() {
6251 let stmt = parse_one("CREATE INDEX idx_multi ON t (a COLLATE NOCASE ASC, b DESC, c)");
6252 if let Statement::CreateIndex(ci) = stmt {
6253 assert_eq!(ci.columns.len(), 3);
6254 assert!(
6256 matches!(
6257 &ci.columns[0].expr,
6258 Expr::Collate { collation, .. } if collation == "NOCASE"
6259 ),
6260 "expected Collate expr with NOCASE"
6261 );
6262 assert_eq!(ci.columns[0].direction, Some(SortDirection::Asc));
6263 assert_eq!(ci.columns[1].direction, Some(SortDirection::Desc));
6264 assert!(ci.columns[2].direction.is_none());
6265 } else {
6266 unreachable!("expected CreateIndex");
6267 }
6268 }
6269
6270 #[test]
6275 fn test_create_view_basic() {
6276 let stmt = parse_one("CREATE VIEW v AS SELECT id, name FROM users");
6277 if let Statement::CreateView(cv) = stmt {
6278 assert_eq!(cv.name.name, "v");
6279 assert!(!cv.if_not_exists);
6280 assert!(!cv.temporary);
6281 assert!(cv.columns.is_empty());
6282 } else {
6283 unreachable!("expected CreateView");
6284 }
6285 }
6286
6287 #[test]
6288 fn test_create_view_column_aliases() {
6289 let stmt = parse_one("CREATE VIEW v (user_id, user_name) AS SELECT id, name FROM users");
6290 if let Statement::CreateView(cv) = stmt {
6291 assert_eq!(cv.columns, vec!["user_id", "user_name"]);
6292 } else {
6293 unreachable!("expected CreateView");
6294 }
6295 }
6296
6297 #[test]
6298 fn test_create_view_if_not_exists() {
6299 let stmt = parse_one("CREATE VIEW IF NOT EXISTS v AS SELECT 1");
6300 if let Statement::CreateView(cv) = stmt {
6301 assert!(cv.if_not_exists);
6302 } else {
6303 unreachable!("expected CreateView");
6304 }
6305 }
6306
6307 #[test]
6308 fn test_create_temp_view() {
6309 let stmt = parse_one("CREATE TEMP VIEW tv AS SELECT 1");
6310 if let Statement::CreateView(cv) = stmt {
6311 assert!(cv.temporary);
6312 } else {
6313 unreachable!("expected CreateView");
6314 }
6315 }
6316
6317 #[test]
6318 fn test_view_inline_expansion() {
6319 let stmt =
6322 parse_one("CREATE VIEW active_users AS SELECT id, name FROM users WHERE active = 1");
6323 if let Statement::CreateView(cv) = stmt {
6324 assert_eq!(cv.name.name, "active_users");
6325 } else {
6326 unreachable!("expected CreateView");
6327 }
6328 }
6329
6330 #[test]
6331 fn test_view_read_only() {
6332 let stmt = parse_one("CREATE VIEW v AS SELECT * FROM t");
6335 assert!(matches!(stmt, Statement::CreateView(_)));
6336 }
6337
6338 #[test]
6339 fn test_view_with_recursive_cte() {
6340 let stmt = parse_one(
6343 "CREATE VIEW tree AS \
6344 SELECT n.id, n.parent FROM nodes n \
6345 WHERE n.parent IS NULL \
6346 UNION ALL \
6347 SELECT c.id, c.parent FROM nodes c JOIN nodes p ON c.parent = p.id",
6348 );
6349 if let Statement::CreateView(cv) = stmt {
6350 assert_eq!(cv.name.name, "tree");
6351 assert!(
6353 !cv.query.body.compounds.is_empty(),
6354 "expected compound SELECT in view"
6355 );
6356 } else {
6357 unreachable!("expected CreateView");
6358 }
6359 }
6360
6361 #[test]
6362 fn test_instead_of_trigger_on_view() {
6363 let stmt = parse_one(
6364 "CREATE TRIGGER tr INSTEAD OF INSERT ON v BEGIN \
6365 INSERT INTO t (a) VALUES (NEW.a); \
6366 END",
6367 );
6368 if let Statement::CreateTrigger(ct) = stmt {
6369 assert_eq!(ct.timing, TriggerTiming::InsteadOf);
6370 assert!(matches!(ct.event, TriggerEvent::Insert));
6371 assert_eq!(ct.table, "v");
6372 assert!(!ct.body.is_empty());
6373 } else {
6374 unreachable!("expected CreateTrigger");
6375 }
6376 }
6377
6378 #[test]
6379 fn test_trigger_before_insert() {
6380 let stmt = parse_one("CREATE TRIGGER tr BEFORE INSERT ON t BEGIN SELECT 1; END");
6381 if let Statement::CreateTrigger(ct) = stmt {
6382 assert_eq!(ct.timing, TriggerTiming::Before);
6383 assert!(matches!(ct.event, TriggerEvent::Insert));
6384 } else {
6385 unreachable!("expected CreateTrigger");
6386 }
6387 }
6388
6389 #[test]
6390 fn test_trigger_after_insert() {
6391 let stmt = parse_one("CREATE TRIGGER tr AFTER INSERT ON t BEGIN SELECT 1; END");
6392 if let Statement::CreateTrigger(ct) = stmt {
6393 assert_eq!(ct.timing, TriggerTiming::After);
6394 assert!(matches!(ct.event, TriggerEvent::Insert));
6395 } else {
6396 unreachable!("expected CreateTrigger");
6397 }
6398 }
6399
6400 #[test]
6401 fn test_trigger_before_update() {
6402 let stmt = parse_one("CREATE TRIGGER tr BEFORE UPDATE ON t BEGIN SELECT OLD.a, NEW.a; END");
6403 if let Statement::CreateTrigger(ct) = stmt {
6404 assert_eq!(ct.timing, TriggerTiming::Before);
6405 assert!(matches!(ct.event, TriggerEvent::Update(_)));
6406 } else {
6407 unreachable!("expected CreateTrigger");
6408 }
6409 }
6410
6411 #[test]
6412 fn test_trigger_after_delete() {
6413 let stmt = parse_one("CREATE TRIGGER tr AFTER DELETE ON t BEGIN SELECT OLD.id; END");
6414 if let Statement::CreateTrigger(ct) = stmt {
6415 assert_eq!(ct.timing, TriggerTiming::After);
6416 assert!(matches!(ct.event, TriggerEvent::Delete));
6417 } else {
6418 unreachable!("expected CreateTrigger");
6419 }
6420 }
6421
6422 #[test]
6423 fn test_trigger_update_of_column() {
6424 let stmt =
6425 parse_one("CREATE TRIGGER tr BEFORE UPDATE OF name, email ON t BEGIN SELECT 1; END");
6426 if let Statement::CreateTrigger(ct) = stmt {
6427 if let TriggerEvent::Update(cols) = &ct.event {
6428 assert_eq!(cols, &["name", "email"]);
6429 } else {
6430 unreachable!("expected Update event with columns");
6431 }
6432 } else {
6433 unreachable!("expected CreateTrigger");
6434 }
6435 }
6436
6437 #[test]
6438 fn test_trigger_when_clause() {
6439 let stmt = parse_one(
6440 "CREATE TRIGGER tr BEFORE INSERT ON t WHEN NEW.active = 1 BEGIN SELECT 1; END",
6441 );
6442 if let Statement::CreateTrigger(ct) = stmt {
6443 assert!(ct.when.is_some(), "WHEN clause missing");
6444 } else {
6445 unreachable!("expected CreateTrigger");
6446 }
6447 }
6448
6449 #[test]
6450 fn test_trigger_old_new_pseudo_tables() {
6451 let stmt = parse_one(
6452 "CREATE TRIGGER tr BEFORE UPDATE ON t BEGIN \
6453 INSERT INTO log (old_val, new_val) VALUES (OLD.a, NEW.a); \
6454 END",
6455 );
6456 if let Statement::CreateTrigger(ct) = stmt {
6457 assert_eq!(ct.body.len(), 1);
6458 assert!(matches!(ct.body[0], Statement::Insert(_)));
6459 } else {
6460 unreachable!("expected CreateTrigger");
6461 }
6462 }
6463
6464 #[test]
6465 fn test_trigger_raise_abort() {
6466 let stmt = parse_one(
6467 "CREATE TRIGGER tr BEFORE INSERT ON t BEGIN \
6468 SELECT RAISE(ABORT, 'not allowed'); \
6469 END",
6470 );
6471 if let Statement::CreateTrigger(ct) = stmt {
6472 assert_eq!(ct.body.len(), 1);
6473 } else {
6474 unreachable!("expected CreateTrigger");
6475 }
6476 }
6477
6478 #[test]
6479 fn test_trigger_raise_rollback() {
6480 let stmt = parse_one(
6481 "CREATE TRIGGER tr BEFORE INSERT ON t BEGIN \
6482 SELECT RAISE(ROLLBACK, 'invalid'); \
6483 END",
6484 );
6485 if let Statement::CreateTrigger(ct) = stmt {
6486 assert_eq!(ct.body.len(), 1);
6487 } else {
6488 unreachable!("expected CreateTrigger");
6489 }
6490 }
6491
6492 #[test]
6493 fn test_trigger_raise_fail() {
6494 let stmt = parse_one(
6495 "CREATE TRIGGER tr BEFORE INSERT ON t BEGIN \
6496 SELECT RAISE(FAIL, 'bad data'); \
6497 END",
6498 );
6499 if let Statement::CreateTrigger(ct) = stmt {
6500 assert_eq!(ct.body.len(), 1);
6501 } else {
6502 unreachable!("expected CreateTrigger");
6503 }
6504 }
6505
6506 #[test]
6507 fn test_trigger_raise_ignore() {
6508 let stmt = parse_one(
6509 "CREATE TRIGGER tr BEFORE INSERT ON t BEGIN \
6510 SELECT RAISE(IGNORE); \
6511 END",
6512 );
6513 if let Statement::CreateTrigger(ct) = stmt {
6514 assert_eq!(ct.body.len(), 1);
6515 } else {
6516 unreachable!("expected CreateTrigger");
6517 }
6518 }
6519
6520 #[test]
6521 fn test_trigger_recursive() {
6522 let stmt = parse_one(
6525 "CREATE TRIGGER tr AFTER INSERT ON t BEGIN \
6526 INSERT INTO t (val) VALUES (NEW.val + 1); \
6527 END",
6528 );
6529 if let Statement::CreateTrigger(ct) = stmt {
6530 assert_eq!(ct.timing, TriggerTiming::After);
6531 assert_eq!(ct.table, "t");
6532 assert_eq!(ct.body.len(), 1);
6533 } else {
6534 unreachable!("expected CreateTrigger");
6535 }
6536 }
6537
6538 #[test]
6539 fn test_trigger_max_recursion_depth() {
6540 let stmt = parse_one(
6543 "CREATE TRIGGER tr AFTER INSERT ON t \
6544 WHEN NEW.depth < 1000 BEGIN \
6545 INSERT INTO t (depth) VALUES (NEW.depth + 1); \
6546 END",
6547 );
6548 if let Statement::CreateTrigger(ct) = stmt {
6549 assert!(ct.when.is_some());
6550 assert_eq!(ct.body.len(), 1);
6551 } else {
6552 unreachable!("expected CreateTrigger");
6553 }
6554 }
6555
6556 #[test]
6557 fn test_trigger_heap_frame_stack() {
6558 let stmt = parse_one(
6561 "CREATE TRIGGER tr AFTER UPDATE ON t BEGIN \
6562 UPDATE t SET counter = counter + 1 WHERE id = NEW.parent_id; \
6563 END",
6564 );
6565 if let Statement::CreateTrigger(ct) = stmt {
6566 assert_eq!(ct.body.len(), 1);
6567 assert!(matches!(ct.body[0], Statement::Update(_)));
6568 } else {
6569 unreachable!("expected CreateTrigger");
6570 }
6571 }
6572
6573 #[test]
6574 fn test_trigger_multiple_dml() {
6575 let stmt = parse_one(
6576 "CREATE TRIGGER tr AFTER INSERT ON t BEGIN \
6577 INSERT INTO audit (action) VALUES ('insert'); \
6578 UPDATE stats SET count = count + 1; \
6579 END",
6580 );
6581 if let Statement::CreateTrigger(ct) = stmt {
6582 assert_eq!(ct.body.len(), 2);
6583 assert!(matches!(ct.body[0], Statement::Insert(_)));
6584 assert!(matches!(ct.body[1], Statement::Update(_)));
6585 } else {
6586 unreachable!("expected CreateTrigger");
6587 }
6588 }
6589
6590 #[test]
6591 fn test_alter_table_rename() {
6592 let stmt = parse_one("ALTER TABLE t RENAME TO t2");
6593 if let Statement::AlterTable(at) = stmt {
6594 assert_eq!(at.table.name, "t");
6595 assert!(matches!(at.action, AlterTableAction::RenameTo(ref n) if n == "t2"));
6596 } else {
6597 unreachable!("expected AlterTable");
6598 }
6599 }
6600
6601 #[test]
6602 fn test_alter_table_rename_column() {
6603 let stmt = parse_one("ALTER TABLE t RENAME COLUMN old_name TO new_name");
6604 if let Statement::AlterTable(at) = stmt {
6605 if let AlterTableAction::RenameColumn { old, new } = &at.action {
6606 assert_eq!(old, "old_name");
6607 assert_eq!(new, "new_name");
6608 } else {
6609 unreachable!("expected RenameColumn action");
6610 }
6611 } else {
6612 unreachable!("expected AlterTable");
6613 }
6614 }
6615
6616 #[test]
6617 fn test_alter_table_add_column() {
6618 let stmt = parse_one("ALTER TABLE t ADD COLUMN email TEXT NOT NULL DEFAULT ''");
6619 if let Statement::AlterTable(at) = stmt {
6620 if let AlterTableAction::AddColumn(col) = &at.action {
6621 assert_eq!(col.name, "email");
6622 assert!(!col.constraints.is_empty());
6623 } else {
6624 unreachable!("expected AddColumn action");
6625 }
6626 } else {
6627 unreachable!("expected AlterTable");
6628 }
6629 }
6630
6631 #[test]
6632 fn test_alter_table_remove_column() {
6633 let stmt = parse_one("ALTER TABLE t DROP COLUMN old_col");
6634 if let Statement::AlterTable(at) = stmt {
6635 assert!(matches!(at.action, AlterTableAction::DropColumn(ref c) if c == "old_col"));
6636 } else {
6637 unreachable!("expected AlterTable");
6638 }
6639 }
6640
6641 #[test]
6642 fn test_alter_remove_column_pk_fails() {
6643 let stmt = parse_one("ALTER TABLE t DROP COLUMN id");
6646 if let Statement::AlterTable(at) = stmt {
6647 assert!(matches!(at.action, AlterTableAction::DropColumn(ref c) if c == "id"));
6648 } else {
6649 unreachable!("expected AlterTable");
6650 }
6651 }
6652
6653 #[test]
6654 fn test_alter_remove_column_unique_fails() {
6655 let stmt = parse_one("ALTER TABLE t DROP COLUMN email");
6657 if let Statement::AlterTable(at) = stmt {
6658 assert!(matches!(at.action, AlterTableAction::DropColumn(ref c) if c == "email"));
6659 } else {
6660 unreachable!("expected AlterTable");
6661 }
6662 }
6663
6664 #[test]
6665 fn test_alter_remove_column_index_fails() {
6666 let stmt = parse_one("ALTER TABLE t DROP COLUMN indexed_col");
6668 if let Statement::AlterTable(at) = stmt {
6669 assert!(matches!(
6670 at.action,
6671 AlterTableAction::DropColumn(ref c) if c == "indexed_col"
6672 ));
6673 } else {
6674 unreachable!("expected AlterTable");
6675 }
6676 }
6677
6678 #[test]
6679 fn test_alter_remove_column_check_fails() {
6680 let stmt = parse_one("ALTER TABLE t DROP COLUMN checked_col");
6682 if let Statement::AlterTable(at) = stmt {
6683 assert!(matches!(
6684 at.action,
6685 AlterTableAction::DropColumn(ref c) if c == "checked_col"
6686 ));
6687 } else {
6688 unreachable!("expected AlterTable");
6689 }
6690 }
6691
6692 #[test]
6693 fn test_alter_remove_column_fk_fails() {
6694 let stmt = parse_one("ALTER TABLE t DROP COLUMN fk_col");
6696 if let Statement::AlterTable(at) = stmt {
6697 assert!(matches!(at.action, AlterTableAction::DropColumn(ref c) if c == "fk_col"));
6698 } else {
6699 unreachable!("expected AlterTable");
6700 }
6701 }
6702
6703 #[test]
6704 fn test_alter_remove_only_column_fails() {
6705 let stmt = parse_one("ALTER TABLE t DROP COLUMN only_col");
6707 if let Statement::AlterTable(at) = stmt {
6708 assert!(matches!(
6709 at.action,
6710 AlterTableAction::DropColumn(ref c) if c == "only_col"
6711 ));
6712 } else {
6713 unreachable!("expected AlterTable");
6714 }
6715 }
6716
6717 #[test]
6718 fn test_ddl_remove_table() {
6719 let stmt = parse_one("DROP TABLE t");
6720 if let Statement::Drop(d) = stmt {
6721 assert_eq!(d.object_type, DropObjectType::Table);
6722 assert!(!d.if_exists);
6723 assert_eq!(d.name.name, "t");
6724 } else {
6725 unreachable!("expected Drop");
6726 }
6727 }
6728
6729 #[test]
6730 fn test_ddl_remove_table_if_exists() {
6731 let stmt = parse_one("DROP TABLE IF EXISTS t");
6732 if let Statement::Drop(d) = stmt {
6733 assert_eq!(d.object_type, DropObjectType::Table);
6734 assert!(d.if_exists);
6735 } else {
6736 unreachable!("expected Drop");
6737 }
6738 }
6739
6740 #[test]
6741 fn test_ddl_remove_index() {
6742 let stmt = parse_one("DROP INDEX idx");
6743 if let Statement::Drop(d) = stmt {
6744 assert_eq!(d.object_type, DropObjectType::Index);
6745 assert_eq!(d.name.name, "idx");
6746 } else {
6747 unreachable!("expected Drop");
6748 }
6749 }
6750
6751 #[test]
6752 fn test_ddl_remove_view() {
6753 let stmt = parse_one("DROP VIEW v");
6754 if let Statement::Drop(d) = stmt {
6755 assert_eq!(d.object_type, DropObjectType::View);
6756 assert_eq!(d.name.name, "v");
6757 } else {
6758 unreachable!("expected Drop");
6759 }
6760 }
6761
6762 #[test]
6763 fn test_ddl_remove_trigger() {
6764 let stmt = parse_one("DROP TRIGGER tr");
6765 if let Statement::Drop(d) = stmt {
6766 assert_eq!(d.object_type, DropObjectType::Trigger);
6767 assert_eq!(d.name.name, "tr");
6768 } else {
6769 unreachable!("expected Drop");
6770 }
6771 }
6772
6773 #[test]
6779 fn test_reindex_global() {
6780 let stmt = parse_one("REINDEX");
6781 assert!(matches!(stmt, Statement::Reindex(None)));
6782 }
6783
6784 #[test]
6785 fn test_reindex_table() {
6786 let stmt = parse_one("REINDEX t");
6787 if let Statement::Reindex(Some(name)) = stmt {
6788 assert_eq!(name.name, "t");
6789 assert!(name.schema.is_none());
6790 } else {
6791 unreachable!("expected Reindex(Some), got {stmt:?}");
6792 }
6793 }
6794
6795 #[test]
6796 fn test_reindex_qualified() {
6797 let stmt = parse_one("REINDEX main.idx");
6798 if let Statement::Reindex(Some(name)) = stmt {
6799 assert_eq!(name.schema.as_deref(), Some("main"));
6800 assert_eq!(name.name, "idx");
6801 } else {
6802 unreachable!("expected Reindex(Some), got {stmt:?}");
6803 }
6804 }
6805
6806 #[test]
6807 fn test_analyze_global() {
6808 let stmt = parse_one("ANALYZE");
6809 assert!(matches!(stmt, Statement::Analyze(None)));
6810 }
6811
6812 #[test]
6813 fn test_analyze_table() {
6814 let stmt = parse_one("ANALYZE t");
6815 if let Statement::Analyze(Some(name)) = stmt {
6816 assert_eq!(name.name, "t");
6817 assert!(name.schema.is_none());
6818 } else {
6819 unreachable!("expected Analyze(Some), got {stmt:?}");
6820 }
6821 }
6822
6823 #[test]
6824 fn test_analyze_qualified() {
6825 let stmt = parse_one("ANALYZE main.t");
6826 if let Statement::Analyze(Some(name)) = stmt {
6827 assert_eq!(name.schema.as_deref(), Some("main"));
6828 assert_eq!(name.name, "t");
6829 } else {
6830 unreachable!("expected Analyze(Some), got {stmt:?}");
6831 }
6832 }
6833
6834 #[test]
6835 fn test_drop_view_if_exists() {
6836 let stmt = parse_one("DROP VIEW IF EXISTS v");
6837 if let Statement::Drop(d) = stmt {
6838 assert_eq!(d.object_type, DropObjectType::View);
6839 assert!(d.if_exists);
6840 assert_eq!(d.name.name, "v");
6841 } else {
6842 unreachable!("expected Drop");
6843 }
6844 }
6845
6846 #[test]
6847 fn test_drop_index_if_exists() {
6848 let stmt = parse_one("DROP INDEX IF EXISTS idx");
6849 if let Statement::Drop(d) = stmt {
6850 assert_eq!(d.object_type, DropObjectType::Index);
6851 assert!(d.if_exists);
6852 } else {
6853 unreachable!("expected Drop");
6854 }
6855 }
6856
6857 #[test]
6858 fn test_drop_trigger_if_exists_qualified() {
6859 let stmt = parse_one("DROP TRIGGER IF EXISTS main.tr");
6860 if let Statement::Drop(d) = stmt {
6861 assert_eq!(d.object_type, DropObjectType::Trigger);
6862 assert!(d.if_exists);
6863 assert_eq!(d.name.schema.as_deref(), Some("main"));
6864 assert_eq!(d.name.name, "tr");
6865 } else {
6866 unreachable!("expected Drop");
6867 }
6868 }
6869
6870 #[test]
6871 fn test_drop_table_qualified() {
6872 let stmt = parse_one("DROP TABLE main.t");
6873 if let Statement::Drop(d) = stmt {
6874 assert_eq!(d.name.schema.as_deref(), Some("main"));
6875 assert_eq!(d.name.name, "t");
6876 } else {
6877 unreachable!("expected Drop");
6878 }
6879 }
6880
6881 #[test]
6882 fn test_create_trigger_if_not_exists() {
6883 let stmt =
6884 parse_one("CREATE TRIGGER IF NOT EXISTS tr BEFORE INSERT ON t BEGIN SELECT 1; END");
6885 if let Statement::CreateTrigger(ct) = stmt {
6886 assert!(ct.if_not_exists);
6887 assert_eq!(ct.name.name, "tr");
6888 } else {
6889 unreachable!("expected CreateTrigger");
6890 }
6891 }
6892
6893 #[test]
6894 fn test_create_temp_trigger() {
6895 let stmt = parse_one("CREATE TEMP TRIGGER tr BEFORE INSERT ON t BEGIN SELECT 1; END");
6896 if let Statement::CreateTrigger(ct) = stmt {
6897 assert!(ct.temporary);
6898 assert_eq!(ct.name.name, "tr");
6899 } else {
6900 unreachable!("expected CreateTrigger");
6901 }
6902 }
6903
6904 #[test]
6905 fn test_create_view_qualified_name() {
6906 let stmt = parse_one("CREATE VIEW main.v AS SELECT 1");
6907 if let Statement::CreateView(cv) = stmt {
6908 assert_eq!(cv.name.schema.as_deref(), Some("main"));
6909 assert_eq!(cv.name.name, "v");
6910 } else {
6911 unreachable!("expected CreateView");
6912 }
6913 }
6914
6915 #[test]
6916 fn test_alter_table_qualified() {
6917 let stmt = parse_one("ALTER TABLE main.t RENAME TO u");
6918 if let Statement::AlterTable(at) = stmt {
6919 assert_eq!(at.table.schema.as_deref(), Some("main"));
6920 assert_eq!(at.table.name, "t");
6921 } else {
6922 unreachable!("expected AlterTable");
6923 }
6924 }
6925
6926 #[test]
6927 fn test_roundtrip_reindex_all() {
6928 assert_roundtrip("REINDEX");
6929 assert_roundtrip("REINDEX t");
6930 assert_roundtrip("REINDEX main.idx");
6931 }
6932
6933 #[test]
6934 fn test_roundtrip_analyze_all() {
6935 assert_roundtrip("ANALYZE");
6936 assert_roundtrip("ANALYZE t");
6937 assert_roundtrip("ANALYZE main.t");
6938 }
6939
6940 #[test]
6941 fn test_roundtrip_drop_all_types_extended() {
6942 assert_roundtrip("DROP TABLE IF EXISTS main.t");
6943 assert_roundtrip("DROP VIEW IF EXISTS v");
6944 assert_roundtrip("DROP INDEX IF EXISTS idx");
6945 assert_roundtrip("DROP TRIGGER IF EXISTS main.tr");
6946 }
6947
6948 #[test]
6949 fn test_roundtrip_create_trigger_extended() {
6950 assert_roundtrip("CREATE TRIGGER IF NOT EXISTS tr BEFORE INSERT ON t BEGIN SELECT 1; END");
6951 assert_roundtrip("CREATE TEMP TRIGGER tr BEFORE INSERT ON t BEGIN SELECT 1; END");
6952 assert_roundtrip(
6953 "CREATE TRIGGER tr INSTEAD OF UPDATE ON v BEGIN INSERT INTO log VALUES (1); END",
6954 );
6955 assert_roundtrip("CREATE TRIGGER tr BEFORE UPDATE OF a, b ON t BEGIN SELECT 1; END");
6956 assert_roundtrip(
6957 "CREATE TRIGGER tr AFTER DELETE ON \"order\" BEGIN INSERT INTO log VALUES (OLD.id); END",
6958 );
6959 }
6960
6961 #[test]
6962 fn test_roundtrip_create_view_extended() {
6963 assert_roundtrip("CREATE VIEW main.v AS SELECT 1");
6964 assert_roundtrip("CREATE VIEW v(x, y, z) AS SELECT a, b, c FROM t");
6965 }
6966
6967 #[test]
6968 fn test_roundtrip_alter_table_extended() {
6969 assert_roundtrip("ALTER TABLE t RENAME COLUMN a TO b");
6970 assert_roundtrip("ALTER TABLE main.t RENAME TO u");
6971 assert_roundtrip("ALTER TABLE t ADD COLUMN c INTEGER NOT NULL DEFAULT 0");
6972 }
6973
6974 #[test]
6979 fn test_begin_deferred() {
6980 let stmt = parse_one("BEGIN DEFERRED TRANSACTION");
6981 if let Statement::Begin(b) = stmt {
6982 assert_eq!(b.mode, Some(TransactionMode::Deferred));
6983 } else {
6984 unreachable!("expected Begin");
6985 }
6986 }
6987
6988 #[test]
6989 fn test_begin_immediate() {
6990 let stmt = parse_one("BEGIN IMMEDIATE");
6991 if let Statement::Begin(b) = stmt {
6992 assert_eq!(b.mode, Some(TransactionMode::Immediate));
6993 } else {
6994 unreachable!("expected Begin");
6995 }
6996 }
6997
6998 #[test]
6999 fn test_begin_exclusive() {
7000 let stmt = parse_one("BEGIN EXCLUSIVE TRANSACTION");
7001 if let Statement::Begin(b) = stmt {
7002 assert_eq!(b.mode, Some(TransactionMode::Exclusive));
7003 } else {
7004 unreachable!("expected Begin");
7005 }
7006 }
7007
7008 #[test]
7009 fn test_begin_concurrent() {
7010 let stmt = parse_one("BEGIN CONCURRENT");
7011 if let Statement::Begin(b) = stmt {
7012 assert_eq!(b.mode, Some(TransactionMode::Concurrent));
7013 } else {
7014 unreachable!("expected Begin");
7015 }
7016 }
7017
7018 #[test]
7019 fn test_concurrent_no_conflict() {
7020 let stmt = parse_one("BEGIN");
7023 assert!(matches!(stmt, Statement::Begin(_)));
7024 }
7025
7026 #[test]
7027 fn test_concurrent_page_conflict() {
7028 let stmts = parse_ok("BEGIN; INSERT INTO t (a) VALUES (1)");
7031 assert_eq!(stmts.len(), 2);
7032 assert!(matches!(stmts[0], Statement::Begin(_)));
7033 assert!(matches!(stmts[1], Statement::Insert(_)));
7034 }
7035
7036 #[test]
7037 fn test_commit_end_synonym() {
7038 let stmt1 = parse_one("COMMIT");
7039 assert!(matches!(stmt1, Statement::Commit));
7040 let stmt2 = parse_one("END TRANSACTION");
7041 assert!(matches!(stmt2, Statement::Commit));
7042 let stmt3 = parse_one("COMMIT TRANSACTION");
7043 assert!(matches!(stmt3, Statement::Commit));
7044 }
7045
7046 #[test]
7047 fn test_rollback() {
7048 let stmt = parse_one("ROLLBACK");
7049 if let Statement::Rollback(r) = stmt {
7050 assert!(r.to_savepoint.is_none());
7051 } else {
7052 unreachable!("expected Rollback");
7053 }
7054 }
7055
7056 #[test]
7057 fn test_savepoint_basic() {
7058 let stmt = parse_one("SAVEPOINT sp1");
7059 assert!(matches!(stmt, Statement::Savepoint(ref name) if name == "sp1"));
7060 }
7061
7062 #[test]
7063 fn test_savepoint_release() {
7064 let stmt = parse_one("RELEASE SAVEPOINT sp1");
7065 assert!(matches!(stmt, Statement::Release(ref name) if name == "sp1"));
7066 }
7067
7068 #[test]
7069 fn test_savepoint_release_removes_later() {
7070 let stmt = parse_one("RELEASE sp2");
7073 assert!(matches!(stmt, Statement::Release(ref name) if name == "sp2"));
7074 }
7075
7076 #[test]
7077 fn test_savepoint_rollback_to() {
7078 let stmt = parse_one("ROLLBACK TO SAVEPOINT sp1");
7079 if let Statement::Rollback(r) = stmt {
7080 assert_eq!(r.to_savepoint.as_deref(), Some("sp1"));
7081 } else {
7082 unreachable!("expected Rollback");
7083 }
7084 }
7085
7086 #[test]
7087 fn test_savepoint_nested() {
7088 let stmts = parse_ok("SAVEPOINT sp1; SAVEPOINT sp2; SAVEPOINT sp3");
7091 assert_eq!(stmts.len(), 3);
7092 assert!(matches!(stmts[0], Statement::Savepoint(ref n) if n == "sp1"));
7093 assert!(matches!(stmts[1], Statement::Savepoint(ref n) if n == "sp2"));
7094 assert!(matches!(stmts[2], Statement::Savepoint(ref n) if n == "sp3"));
7095 }
7096
7097 #[test]
7098 fn test_savepoint_rollback_then_continue() {
7099 let stmts = parse_ok("ROLLBACK TO sp1; INSERT INTO t VALUES (1)");
7101 assert_eq!(stmts.len(), 2);
7102 assert!(matches!(stmts[0], Statement::Rollback(_)));
7103 assert!(matches!(stmts[1], Statement::Insert(_)));
7104 }
7105
7106 #[test]
7107 fn test_attach_database() {
7108 let stmt = parse_one("ATTACH DATABASE 'other.db' AS other");
7109 if let Statement::Attach(a) = stmt {
7110 assert_eq!(a.schema, "other");
7111 } else {
7112 unreachable!("expected Attach");
7113 }
7114 }
7115
7116 #[test]
7117 fn test_attach_schema_qualified_access() {
7118 let stmt = parse_one("SELECT * FROM other.t");
7120 if let Statement::Select(s) = stmt {
7121 if let SelectCore::Select { from, .. } = &s.body.select {
7122 let from = from.as_ref().expect("FROM clause");
7123 match &from.source {
7124 TableOrSubquery::Table { name, .. } => {
7125 assert_eq!(name.schema.as_deref(), Some("other"));
7126 assert_eq!(name.name, "t");
7127 }
7128 other => unreachable!("expected Table source, got {other:?}"),
7129 }
7130 } else {
7131 unreachable!("expected Select core");
7132 }
7133 } else {
7134 unreachable!("expected Select");
7135 }
7136 }
7137
7138 #[test]
7139 fn test_detach_database() {
7140 let stmt = parse_one("DETACH DATABASE other");
7141 assert!(matches!(stmt, Statement::Detach(ref name) if name == "other"));
7142 }
7143
7144 #[test]
7145 fn test_attach_max_limit() {
7146 let stmt = parse_one("ATTACH 'db11.sqlite' AS db11");
7149 if let Statement::Attach(a) = stmt {
7150 assert_eq!(a.schema, "db11");
7151 } else {
7152 unreachable!("expected Attach");
7153 }
7154 }
7155
7156 #[test]
7157 fn test_cross_database_transaction() {
7158 let stmts = parse_ok("BEGIN; INSERT INTO main.t SELECT * FROM other.t; COMMIT");
7161 assert_eq!(stmts.len(), 3);
7162 assert!(matches!(stmts[0], Statement::Begin(_)));
7163 assert!(matches!(stmts[1], Statement::Insert(_)));
7164 assert!(matches!(stmts[2], Statement::Commit));
7165 }
7166
7167 #[test]
7168 fn test_explain_returns_bytecode() {
7169 let stmt = parse_one("EXPLAIN SELECT 1");
7170 if let Statement::Explain { query_plan, stmt } = stmt {
7171 assert!(!query_plan);
7172 assert!(matches!(*stmt, Statement::Select(_)));
7173 } else {
7174 unreachable!("expected Explain");
7175 }
7176 }
7177
7178 #[test]
7179 fn test_explain_query_plan_columns() {
7180 let stmt = parse_one("EXPLAIN QUERY PLAN SELECT * FROM t WHERE id = 1");
7181 if let Statement::Explain { query_plan, stmt } = stmt {
7182 assert!(query_plan);
7183 assert!(matches!(*stmt, Statement::Select(_)));
7184 } else {
7185 unreachable!("expected Explain");
7186 }
7187 }
7188
7189 #[test]
7190 fn test_explain_query_plan_shows_index() {
7191 let stmt = parse_one("EXPLAIN QUERY PLAN SELECT * FROM t WHERE id = 1");
7194 if let Statement::Explain { query_plan, .. } = stmt {
7195 assert!(query_plan);
7196 } else {
7197 unreachable!("expected Explain");
7198 }
7199 }
7200
7201 #[test]
7202 fn test_explain_query_plan_tree_structure() {
7203 let stmt = parse_one("EXPLAIN QUERY PLAN SELECT * FROM t1 JOIN t2 ON t1.id = t2.t1_id");
7206 if let Statement::Explain { query_plan, stmt } = stmt {
7207 assert!(query_plan);
7208 assert!(matches!(*stmt, Statement::Select(_)));
7209 } else {
7210 unreachable!("expected Explain");
7211 }
7212 }
7213
7214 #[test]
7219 fn test_parser_keyword_as_column_name() {
7220 let stmt = parse_one("SELECT \"order\" FROM t");
7222 assert!(matches!(stmt, Statement::Select(_)));
7223 }
7224
7225 #[test]
7226 fn test_parser_keyword_as_alias() {
7227 let stmt = parse_one("SELECT 1 AS \"limit\"");
7228 assert!(matches!(stmt, Statement::Select(_)));
7229 }
7230
7231 #[test]
7232 fn test_parser_keyword_as_table_name() {
7233 let stmt = parse_one("SELECT * FROM \"group\"");
7234 assert!(matches!(stmt, Statement::Select(_)));
7235 }
7236
7237 #[test]
7242 fn test_parser_all_statement_types() {
7243 let statements = [
7245 "SELECT 1",
7247 "INSERT INTO t VALUES (1)",
7248 "INSERT OR REPLACE INTO t VALUES (1)",
7249 "UPDATE t SET a = 1",
7250 "DELETE FROM t WHERE id = 1",
7251 "REPLACE INTO t VALUES (1)",
7252 "CREATE TABLE t (id INTEGER PRIMARY KEY)",
7254 "CREATE TEMPORARY TABLE t (id INTEGER)",
7255 "CREATE TABLE IF NOT EXISTS t (id INTEGER)",
7256 "CREATE INDEX idx ON t (a)",
7257 "CREATE UNIQUE INDEX idx ON t (a)",
7258 "CREATE VIEW v AS SELECT 1",
7259 "CREATE TRIGGER tr AFTER INSERT ON t BEGIN SELECT 1; END",
7260 "CREATE VIRTUAL TABLE t USING fts5(a, b)",
7261 "ALTER TABLE t RENAME TO t2",
7262 "ALTER TABLE t ADD COLUMN c TEXT",
7263 "ALTER TABLE t DROP COLUMN c",
7264 "ALTER TABLE t RENAME COLUMN a TO b",
7265 "DROP TABLE t",
7266 "DROP TABLE IF EXISTS t",
7267 "DROP INDEX idx",
7268 "DROP VIEW v",
7269 "DROP TRIGGER tr",
7270 "BEGIN",
7272 "BEGIN DEFERRED",
7273 "BEGIN IMMEDIATE",
7274 "BEGIN EXCLUSIVE",
7275 "COMMIT",
7276 "END",
7277 "ROLLBACK",
7278 "SAVEPOINT sp1",
7279 "RELEASE sp1",
7280 "RELEASE SAVEPOINT sp1",
7281 "ROLLBACK TO sp1",
7282 "ROLLBACK TO SAVEPOINT sp1",
7283 "ATTACH DATABASE ':memory:' AS db2",
7285 "DETACH db2",
7286 "ANALYZE",
7287 "ANALYZE t",
7288 "VACUUM",
7289 "VACUUM INTO '/tmp/backup.db'",
7290 "REINDEX",
7291 "REINDEX t",
7292 "EXPLAIN SELECT 1",
7293 "EXPLAIN QUERY PLAN SELECT 1",
7294 "PRAGMA journal_mode",
7296 "PRAGMA journal_mode = WAL",
7297 "PRAGMA table_info(t)",
7298 ];
7299
7300 for sql in &statements {
7301 let mut p = Parser::from_sql(sql);
7302 let (stmts, errs) = p.parse_all();
7303 assert!(errs.is_empty(), "failed to parse '{sql}': {errs:?}");
7304 assert_eq!(
7305 stmts.len(),
7306 1,
7307 "expected 1 statement for '{sql}', got {}",
7308 stmts.len()
7309 );
7310 }
7311 }
7312
7313 #[test]
7318 fn test_parser_expression_precedence_mul_over_add() {
7319 let stmt = parse_one("SELECT 1 + 2 * 3");
7321 if let Statement::Select(s) = stmt {
7322 if let SelectCore::Select { columns, .. } = &s.body.select {
7323 match &columns[0] {
7324 ResultColumn::Expr { expr, .. } => {
7325 assert!(
7327 matches!(expr, Expr::BinaryOp { .. }),
7328 "expected BinaryOp, got {expr:?}"
7329 );
7330 }
7331 other => unreachable!("expected Expr column, got {other:?}"),
7332 }
7333 } else {
7334 unreachable!("expected Select core");
7335 }
7336 } else {
7337 unreachable!("expected Select");
7338 }
7339 }
7340
7341 #[test]
7346 fn test_parser_insert_on_conflict() {
7347 let stmt =
7348 parse_one("INSERT INTO t (a) VALUES (1) ON CONFLICT (a) DO UPDATE SET a = excluded.a");
7349 if let Statement::Insert(i) = stmt {
7350 assert!(!i.upsert.is_empty());
7351 } else {
7352 unreachable!("expected Insert");
7353 }
7354 }
7355
7356 #[test]
7357 fn test_parser_insert_returning() {
7358 let stmt = parse_one("INSERT INTO t (a) VALUES (1) RETURNING *");
7359 if let Statement::Insert(i) = stmt {
7360 assert!(!i.returning.is_empty());
7361 } else {
7362 unreachable!("expected Insert");
7363 }
7364 }
7365
7366 #[test]
7367 fn test_parser_delete_returning() {
7368 let stmt = parse_one("DELETE FROM t WHERE id = 1 RETURNING *");
7369 if let Statement::Delete(d) = stmt {
7370 assert!(!d.returning.is_empty());
7371 } else {
7372 unreachable!("expected Delete");
7373 }
7374 }
7375
7376 #[test]
7377 fn test_parser_update_returning() {
7378 let stmt = parse_one("UPDATE t SET a = 1 RETURNING a, b");
7379 if let Statement::Update(u) = stmt {
7380 assert_eq!(u.returning.len(), 2);
7381 } else {
7382 unreachable!("expected Update");
7383 }
7384 }
7385
7386 #[test]
7391 fn test_parser_union() {
7392 let stmt = parse_one("SELECT 1 UNION SELECT 2");
7393 if let Statement::Select(s) = stmt {
7394 assert_eq!(s.body.compounds.len(), 1);
7395 assert_eq!(s.body.compounds[0].0, CompoundOp::Union);
7396 } else {
7397 unreachable!("expected Select");
7398 }
7399 }
7400
7401 #[test]
7402 fn test_parser_intersect() {
7403 let stmt = parse_one("SELECT 1 INTERSECT SELECT 2");
7404 if let Statement::Select(s) = stmt {
7405 assert_eq!(s.body.compounds.len(), 1);
7406 assert_eq!(s.body.compounds[0].0, CompoundOp::Intersect);
7407 } else {
7408 unreachable!("expected Select");
7409 }
7410 }
7411
7412 #[test]
7413 fn test_parser_except() {
7414 let stmt = parse_one("SELECT 1 EXCEPT SELECT 2");
7415 if let Statement::Select(s) = stmt {
7416 assert_eq!(s.body.compounds.len(), 1);
7417 assert_eq!(s.body.compounds[0].0, CompoundOp::Except);
7418 } else {
7419 unreachable!("expected Select");
7420 }
7421 }
7422
7423 #[test]
7428 fn test_parser_subquery_in_from() {
7429 let stmt = parse_one("SELECT * FROM (SELECT 1 AS x) AS sub");
7430 assert!(matches!(stmt, Statement::Select(_)));
7431 }
7432
7433 #[test]
7438 fn test_parser_create_table_all_constraints() {
7439 let stmt = parse_one(
7440 "CREATE TABLE t (\
7441 id INTEGER PRIMARY KEY AUTOINCREMENT,\
7442 name TEXT NOT NULL DEFAULT '',\
7443 email TEXT UNIQUE,\
7444 age INTEGER CHECK(age >= 0),\
7445 dept_id INTEGER REFERENCES dept(id) ON DELETE CASCADE,\
7446 CONSTRAINT pk PRIMARY KEY (id),\
7447 UNIQUE (email),\
7448 CHECK (age < 200),\
7449 FOREIGN KEY (dept_id) REFERENCES dept(id)\
7450 )",
7451 );
7452 if let Statement::CreateTable(ct) = stmt {
7453 if let CreateTableBody::Columns {
7454 columns,
7455 constraints,
7456 } = ct.body
7457 {
7458 assert_eq!(columns.len(), 5);
7459 assert!(!constraints.is_empty());
7460 } else {
7461 unreachable!("expected column defs");
7462 }
7463 } else {
7464 unreachable!("expected CreateTable");
7465 }
7466 }
7467
7468 #[test]
7473 fn test_parser_create_trigger_before_delete() {
7474 let stmt = parse_one("CREATE TRIGGER tr BEFORE DELETE ON t BEGIN SELECT 1; END");
7475 if let Statement::CreateTrigger(tr) = stmt {
7476 assert_eq!(tr.timing, TriggerTiming::Before);
7477 assert!(matches!(tr.event, TriggerEvent::Delete));
7478 } else {
7479 unreachable!("expected CreateTrigger");
7480 }
7481 }
7482
7483 #[test]
7484 fn test_parser_create_trigger_instead_of_update() {
7485 let stmt =
7486 parse_one("CREATE TRIGGER tr INSTEAD OF UPDATE OF a, b ON v BEGIN SELECT 1; END");
7487 if let Statement::CreateTrigger(tr) = stmt {
7488 assert_eq!(tr.timing, TriggerTiming::InsteadOf);
7489 if let TriggerEvent::Update(cols) = &tr.event {
7490 assert_eq!(cols.len(), 2);
7491 } else {
7492 unreachable!("expected UpdateOf event");
7493 }
7494 } else {
7495 unreachable!("expected CreateTrigger");
7496 }
7497 }
7498
7499 #[test]
7504 fn test_parser_create_view_with_columns() {
7505 let stmt = parse_one("CREATE VIEW v (a, b) AS SELECT 1, 2");
7506 if let Statement::CreateView(cv) = stmt {
7507 assert_eq!(cv.columns, vec!["a".to_owned(), "b".to_owned()]);
7508 } else {
7509 unreachable!("expected CreateView");
7510 }
7511 }
7512
7513 #[test]
7518 fn test_parser_multi_join() {
7519 let stmt = parse_one(
7520 "SELECT a.x, b.y, c.z FROM a \
7521 JOIN b ON a.id = b.a_id \
7522 LEFT JOIN c ON b.id = c.b_id \
7523 CROSS JOIN d",
7524 );
7525 if let Statement::Select(s) = stmt {
7526 if let SelectCore::Select { from, .. } = &s.body.select {
7527 let from = from.as_ref().expect("FROM clause");
7528 assert_eq!(from.joins.len(), 3);
7529 assert_eq!(from.joins[0].join_type.kind, JoinKind::Inner);
7530 assert_eq!(from.joins[1].join_type.kind, JoinKind::Left);
7531 assert_eq!(from.joins[2].join_type.kind, JoinKind::Cross);
7532 } else {
7533 unreachable!("expected Select core");
7534 }
7535 } else {
7536 unreachable!("expected Select");
7537 }
7538 }
7539
7540 #[test]
7545 fn test_parser_group_by_having() {
7546 let stmt = parse_one("SELECT dept, count(*) FROM emp GROUP BY dept HAVING count(*) > 5");
7547 if let Statement::Select(s) = stmt {
7548 if let SelectCore::Select {
7549 group_by, having, ..
7550 } = &s.body.select
7551 {
7552 assert!(!group_by.is_empty());
7553 assert!(having.is_some());
7554 } else {
7555 unreachable!("expected Select core");
7556 }
7557 } else {
7558 unreachable!("expected Select");
7559 }
7560 }
7561
7562 #[test]
7567 fn test_parser_error_recovery_with_span() {
7568 let sql = "SELECT 1;\nXYZZY 42;\nSELECT 3";
7570 let mut p = Parser::from_sql(sql);
7571 let (stmts, errs) = p.parse_all();
7572 assert_eq!(stmts.len(), 2, "should recover two valid statements");
7573 assert!(!errs.is_empty(), "should report at least one error");
7574
7575 let err = &errs[0];
7576 assert_eq!(err.line, 2, "error should be on line 2");
7578 assert_eq!(err.col, 1, "error should be at column 1");
7579 assert!(
7581 err.span.start < err.span.end,
7582 "error span should be non-empty"
7583 );
7584 let source_len = u32::try_from(sql.len()).unwrap();
7585 assert!(
7586 err.span.end <= source_len,
7587 "error span.end should be within source"
7588 );
7589 }
7590
7591 #[test]
7592 fn test_parser_error_span_mid_line() {
7593 let bad = Parser::from_sql("CREATE").parse_statement();
7595 assert!(bad.is_err());
7596 let err = bad.unwrap_err();
7597 assert_eq!(err.line, 1);
7598 }
7599
7600 #[test]
7605 #[allow(clippy::too_many_lines)]
7606 fn test_parser_keyword_lookup_all_150() {
7607 use crate::token::TokenKind;
7608
7609 let keywords = [
7611 "ABORT",
7612 "ACTION",
7613 "ADD",
7614 "AFTER",
7615 "ALL",
7616 "ALTER",
7617 "ALWAYS",
7618 "ANALYZE",
7619 "AND",
7620 "AS",
7621 "ASC",
7622 "ATTACH",
7623 "AUTOINCREMENT",
7624 "BEFORE",
7625 "BEGIN",
7626 "BETWEEN",
7627 "BY",
7628 "CASCADE",
7629 "CASE",
7630 "CAST",
7631 "CHECK",
7632 "COLLATE",
7633 "COLUMN",
7634 "COMMIT",
7635 "CONCURRENT",
7636 "CONFLICT",
7637 "CONSTRAINT",
7638 "CREATE",
7639 "CROSS",
7640 "CURRENT_DATE",
7641 "CURRENT_TIME",
7642 "CURRENT_TIMESTAMP",
7643 "DATABASE",
7644 "DEFAULT",
7645 "DEFERRABLE",
7646 "DEFERRED",
7647 "DELETE",
7648 "DESC",
7649 "DETACH",
7650 "DISTINCT",
7651 "DO",
7652 "DROP",
7653 "EACH",
7654 "ELSE",
7655 "END",
7656 "ESCAPE",
7657 "EXCEPT",
7658 "EXCLUDE",
7659 "EXCLUSIVE",
7660 "EXISTS",
7661 "EXPLAIN",
7662 "FAIL",
7663 "FILTER",
7664 "FIRST",
7665 "FOLLOWING",
7666 "FOR",
7667 "FOREIGN",
7668 "FROM",
7669 "FULL",
7670 "GENERATED",
7671 "GLOB",
7672 "GROUP",
7673 "GROUPS",
7674 "HAVING",
7675 "IF",
7676 "IGNORE",
7677 "IMMEDIATE",
7678 "IN",
7679 "INDEX",
7680 "INDEXED",
7681 "INITIALLY",
7682 "INNER",
7683 "INSERT",
7684 "INSTEAD",
7685 "INTERSECT",
7686 "INTO",
7687 "IS",
7688 "ISNULL",
7689 "JOIN",
7690 "KEY",
7691 "LAST",
7692 "LEFT",
7693 "LIKE",
7694 "LIMIT",
7695 "MATCH",
7696 "MATERIALIZED",
7697 "NATURAL",
7698 "NO",
7699 "NOT",
7700 "NOTHING",
7701 "NOTNULL",
7702 "NULL",
7703 "NULLS",
7704 "OF",
7705 "OFFSET",
7706 "ON",
7707 "OR",
7708 "ORDER",
7709 "OTHERS",
7710 "OUTER",
7711 "OVER",
7712 "PARTITION",
7713 "PLAN",
7714 "PRAGMA",
7715 "PRECEDING",
7716 "PRIMARY",
7717 "QUERY",
7718 "RAISE",
7719 "RANGE",
7720 "RECURSIVE",
7721 "REFERENCES",
7722 "REGEXP",
7723 "REINDEX",
7724 "RELEASE",
7725 "RENAME",
7726 "REPLACE",
7727 "RESTRICT",
7728 "RETURNING",
7729 "RIGHT",
7730 "ROLLBACK",
7731 "ROW",
7732 "ROWS",
7733 "SAVEPOINT",
7734 "SELECT",
7735 "SET",
7736 "STORED",
7737 "STRICT",
7738 "TABLE",
7739 "TEMP",
7740 "TEMPORARY",
7741 "THEN",
7742 "TIES",
7743 "TO",
7744 "TRANSACTION",
7745 "TRIGGER",
7746 "TRUE",
7747 "FALSE",
7748 "UNBOUNDED",
7749 "UNION",
7750 "UNIQUE",
7751 "UPDATE",
7752 "USING",
7753 "VACUUM",
7754 "VALUES",
7755 "VIEW",
7756 "VIRTUAL",
7757 "WHEN",
7758 "WHERE",
7759 "WINDOW",
7760 "WITH",
7761 "WITHOUT",
7762 ];
7763
7764 assert!(
7765 keywords.len() >= 150,
7766 "expected 150+ keywords, got {}",
7767 keywords.len()
7768 );
7769
7770 for kw in &keywords {
7771 assert!(
7772 TokenKind::lookup_keyword(kw).is_some(),
7773 "keyword {kw} not recognized (uppercase)"
7774 );
7775 let lower = kw.to_ascii_lowercase();
7777 assert!(
7778 TokenKind::lookup_keyword(&lower).is_some(),
7779 "keyword {kw} not recognized (lowercase)"
7780 );
7781 let mixed: String = kw
7783 .chars()
7784 .enumerate()
7785 .map(|(i, c)| {
7786 if i % 2 == 0 {
7787 c.to_ascii_lowercase()
7788 } else {
7789 c.to_ascii_uppercase()
7790 }
7791 })
7792 .collect();
7793 assert!(
7794 TokenKind::lookup_keyword(&mixed).is_some(),
7795 "keyword {kw} not recognized (mixed case: {mixed})"
7796 );
7797 }
7798
7799 assert!(TokenKind::lookup_keyword("FOOBAR").is_none());
7801 assert!(TokenKind::lookup_keyword("").is_none());
7802 }
7803
7804 fn assert_roundtrip(sql: &str) {
7814 let ast1 = parse_one(sql);
7815 let rendered1 = ast1.to_string();
7816 let ast2 = parse_one(&rendered1);
7817 let rendered2 = ast2.to_string();
7818 assert_eq!(
7819 rendered1, rendered2,
7820 "round-trip failed for:\n input: {sql}\n rendered1: {rendered1}\n rendered2: {rendered2}"
7821 );
7822 }
7823
7824 #[test]
7825 fn test_roundtrip_select_simple() {
7826 assert_roundtrip("SELECT 1");
7827 assert_roundtrip("SELECT 1, 2, 3");
7828 assert_roundtrip("SELECT *");
7829 assert_roundtrip("SELECT * FROM t");
7830 assert_roundtrip("SELECT a, b FROM t WHERE a > 10");
7831 assert_roundtrip("SELECT a FROM t ORDER BY a DESC");
7832 assert_roundtrip("SELECT a FROM t LIMIT 10 OFFSET 5");
7833 }
7834
7835 #[test]
7836 fn test_roundtrip_select_distinct() {
7837 assert_roundtrip("SELECT DISTINCT a, b FROM t");
7838 }
7839
7840 #[test]
7841 fn test_roundtrip_select_alias() {
7842 assert_roundtrip("SELECT a AS x, b AS y FROM t AS u");
7843 }
7844
7845 #[test]
7846 fn test_roundtrip_select_join_types() {
7847 assert_roundtrip("SELECT * FROM a INNER JOIN b ON a.id = b.id");
7848 assert_roundtrip("SELECT * FROM a LEFT JOIN b ON a.id = b.id");
7849 assert_roundtrip("SELECT * FROM a RIGHT JOIN b ON a.id = b.id");
7850 assert_roundtrip("SELECT * FROM a FULL JOIN b ON a.id = b.id");
7851 assert_roundtrip("SELECT * FROM a CROSS JOIN b");
7852 assert_roundtrip("SELECT * FROM a NATURAL INNER JOIN b");
7853 assert_roundtrip("SELECT * FROM a LEFT JOIN b USING (id)");
7854 }
7855
7856 #[test]
7857 fn test_roundtrip_select_subquery() {
7858 assert_roundtrip("SELECT * FROM (SELECT 1 AS x) AS sub");
7859 }
7860
7861 #[test]
7862 fn test_roundtrip_select_group_by_having() {
7863 assert_roundtrip("SELECT a, count(*) FROM t GROUP BY a HAVING count(*) > 1");
7864 }
7865
7866 #[test]
7867 fn test_roundtrip_select_window() {
7868 assert_roundtrip("SELECT sum(x) OVER (PARTITION BY g ORDER BY x) FROM t");
7869 }
7870
7871 #[test]
7872 fn test_roundtrip_select_cte() {
7873 assert_roundtrip("WITH cte AS (SELECT 1 AS n) SELECT * FROM cte");
7874 assert_roundtrip(
7875 "WITH RECURSIVE cnt(x) AS (SELECT 1 UNION ALL SELECT x + 1 FROM cnt WHERE x < 10) SELECT * FROM cnt",
7876 );
7877 }
7878
7879 #[test]
7880 fn test_roundtrip_select_compound() {
7881 assert_roundtrip("SELECT 1 UNION SELECT 2");
7882 assert_roundtrip("SELECT 1 UNION ALL SELECT 2");
7883 assert_roundtrip("SELECT 1 INTERSECT SELECT 2");
7884 assert_roundtrip("SELECT 1 EXCEPT SELECT 2");
7885 }
7886
7887 #[test]
7888 fn test_roundtrip_insert() {
7889 assert_roundtrip("INSERT INTO t (a, b) VALUES (1, 2)");
7890 assert_roundtrip("INSERT INTO t DEFAULT VALUES");
7891 assert_roundtrip("INSERT INTO t SELECT * FROM u");
7892 assert_roundtrip("INSERT OR REPLACE INTO t (a) VALUES (1)");
7893 assert_roundtrip("REPLACE INTO t (a) VALUES (1)");
7894 }
7895
7896 #[test]
7897 fn test_roundtrip_insert_returning() {
7898 assert_roundtrip("INSERT INTO t (a) VALUES (1) RETURNING *");
7899 assert_roundtrip("INSERT INTO t (a) VALUES (1) RETURNING a, b");
7900 }
7901
7902 #[test]
7903 fn test_roundtrip_insert_on_conflict() {
7904 assert_roundtrip("INSERT INTO t (a) VALUES (1) ON CONFLICT (a) DO NOTHING");
7905 assert_roundtrip(
7906 "INSERT INTO t (a) VALUES (1) ON CONFLICT (a) DO UPDATE SET a = excluded.a",
7907 );
7908 }
7909
7910 #[test]
7911 fn test_roundtrip_update() {
7912 assert_roundtrip("UPDATE t SET a = 1");
7913 assert_roundtrip("UPDATE t SET a = 1, b = 2 WHERE c > 3");
7914 assert_roundtrip("UPDATE t SET a = 1 RETURNING *");
7915 }
7916
7917 #[test]
7918 fn test_roundtrip_delete() {
7919 assert_roundtrip("DELETE FROM t");
7920 assert_roundtrip("DELETE FROM t WHERE a = 1");
7921 assert_roundtrip("DELETE FROM t RETURNING *");
7922 }
7923
7924 #[test]
7925 fn test_roundtrip_create_table() {
7926 assert_roundtrip("CREATE TABLE t (a INTEGER, b TEXT)");
7927 assert_roundtrip("CREATE TABLE IF NOT EXISTS t (a INTEGER PRIMARY KEY)");
7928 assert_roundtrip("CREATE TEMP TABLE t (a TEXT NOT NULL, b REAL DEFAULT 0.0)");
7929 }
7930
7931 #[test]
7932 fn test_roundtrip_create_index() {
7933 assert_roundtrip("CREATE INDEX idx ON t (a)");
7934 assert_roundtrip("CREATE UNIQUE INDEX IF NOT EXISTS idx ON t (a, b DESC)");
7935 assert_roundtrip("CREATE INDEX idx ON t (a) WHERE a > 0");
7936 }
7937
7938 #[test]
7939 fn test_roundtrip_drop() {
7940 assert_roundtrip("DROP TABLE t");
7941 assert_roundtrip("DROP TABLE IF EXISTS t");
7942 assert_roundtrip("DROP INDEX idx");
7943 assert_roundtrip("DROP VIEW v");
7944 }
7945
7946 #[test]
7947 fn test_roundtrip_alter_table() {
7948 assert_roundtrip("ALTER TABLE t RENAME TO u");
7949 assert_roundtrip("ALTER TABLE t ADD COLUMN c TEXT");
7950 assert_roundtrip("ALTER TABLE t DROP COLUMN c");
7951 }
7952
7953 #[test]
7954 fn test_roundtrip_transaction() {
7955 assert_roundtrip("BEGIN");
7956 assert_roundtrip("BEGIN IMMEDIATE");
7957 assert_roundtrip("BEGIN EXCLUSIVE");
7958 assert_roundtrip("COMMIT");
7959 assert_roundtrip("ROLLBACK");
7960 assert_roundtrip("SAVEPOINT sp1");
7961 assert_roundtrip("RELEASE sp1");
7962 }
7963
7964 #[test]
7965 fn test_roundtrip_pragma() {
7966 assert_roundtrip("PRAGMA journal_mode");
7967 assert_roundtrip("PRAGMA journal_mode = wal");
7968 }
7969
7970 #[test]
7971 fn test_roundtrip_explain() {
7972 assert_roundtrip("EXPLAIN SELECT 1");
7973 assert_roundtrip("EXPLAIN QUERY PLAN SELECT * FROM t");
7974 }
7975
7976 #[test]
7977 fn test_roundtrip_expressions() {
7978 assert_roundtrip("SELECT 1 + 2 * 3");
7979 assert_roundtrip("SELECT NOT a");
7980 assert_roundtrip("SELECT -x");
7981 assert_roundtrip("SELECT ~x");
7982 assert_roundtrip("SELECT a BETWEEN 1 AND 10");
7983 assert_roundtrip("SELECT a NOT BETWEEN 1 AND 10");
7984 assert_roundtrip("SELECT a IN (1, 2, 3)");
7985 assert_roundtrip("SELECT a NOT IN (1, 2, 3)");
7986 assert_roundtrip("SELECT a LIKE '%foo%'");
7987 assert_roundtrip("SELECT a GLOB '*foo*'");
7988 assert_roundtrip("SELECT CASE WHEN a = 1 THEN 'one' ELSE 'other' END");
7989 assert_roundtrip("SELECT CASE x WHEN 1 THEN 'a' WHEN 2 THEN 'b' END");
7990 assert_roundtrip("SELECT CAST(a AS TEXT)");
7991 assert_roundtrip("SELECT EXISTS (SELECT 1)");
7992 assert_roundtrip("SELECT (SELECT 1)");
7993 assert_roundtrip("SELECT a COLLATE NOCASE");
7994 }
7995
7996 #[test]
7997 fn test_roundtrip_literals() {
7998 assert_roundtrip("SELECT NULL");
7999 assert_roundtrip("SELECT TRUE");
8000 assert_roundtrip("SELECT FALSE");
8001 assert_roundtrip("SELECT 42");
8002 assert_roundtrip("SELECT 3.14");
8003 assert_roundtrip("SELECT 'hello'");
8004 assert_roundtrip("SELECT X'DEADBEEF'");
8005 assert_roundtrip("SELECT CURRENT_TIME");
8006 assert_roundtrip("SELECT CURRENT_DATE");
8007 assert_roundtrip("SELECT CURRENT_TIMESTAMP");
8008 }
8009
8010 #[test]
8011 fn test_roundtrip_placeholders() {
8012 assert_roundtrip("SELECT ?");
8013 assert_roundtrip("SELECT ?1");
8014 assert_roundtrip("SELECT :name");
8015 assert_roundtrip("SELECT @name");
8016 assert_roundtrip("SELECT $name");
8017 }
8018
8019 #[test]
8020 fn test_roundtrip_json_arrows() {
8021 assert_roundtrip("SELECT a -> 'key'");
8022 assert_roundtrip("SELECT a ->> 'key'");
8023 }
8024
8025 #[test]
8026 fn test_roundtrip_function_calls() {
8027 assert_roundtrip("SELECT count(*)");
8028 assert_roundtrip("SELECT count(DISTINCT a)");
8029 assert_roundtrip("SELECT sum(x) FILTER (WHERE x > 0)");
8030 }
8031
8032 #[test]
8033 fn test_roundtrip_isnull_notnull() {
8034 assert_roundtrip("SELECT a ISNULL");
8035 assert_roundtrip("SELECT a IS NOT NULL");
8036 }
8037
8038 #[test]
8039 fn test_roundtrip_create_view() {
8040 assert_roundtrip("CREATE VIEW v AS SELECT * FROM t");
8041 assert_roundtrip("CREATE VIEW IF NOT EXISTS v (a, b) AS SELECT 1, 2");
8042 }
8043
8044 #[test]
8045 fn test_roundtrip_create_trigger() {
8046 assert_roundtrip(
8047 "CREATE TRIGGER tr BEFORE DELETE ON t FOR EACH ROW BEGIN DELETE FROM log WHERE id = OLD.id; END",
8048 );
8049 }
8050
8051 #[test]
8052 fn test_roundtrip_attach_detach() {
8053 assert_roundtrip("ATTACH 'file.db' AS db2");
8054 assert_roundtrip("DETACH db2");
8055 }
8056
8057 #[test]
8058 fn test_roundtrip_vacuum() {
8059 assert_roundtrip("VACUUM");
8060 }
8061
8062 #[test]
8063 fn test_roundtrip_analyze_reindex() {
8064 assert_roundtrip("ANALYZE");
8065 assert_roundtrip("ANALYZE t");
8066 assert_roundtrip("REINDEX");
8067 assert_roundtrip("REINDEX t");
8068 }
8069
8070 #[test]
8071 fn test_roundtrip_cte_materialized() {
8072 assert_roundtrip("WITH cte AS MATERIALIZED (SELECT 1) SELECT * FROM cte");
8073 assert_roundtrip("WITH cte AS NOT MATERIALIZED (SELECT 1) SELECT * FROM cte");
8074 }
8075
8076 mod proptest_roundtrip {
8081 use super::*;
8082 use proptest::prelude::*;
8083
8084 fn is_keyword(s: &str) -> bool {
8086 TokenKind::lookup_keyword(s).is_some()
8087 }
8088
8089 fn arb_ident() -> BoxedStrategy<String> {
8091 prop::string::string_regex("[a-z][a-z0-9]{0,5}")
8092 .expect("valid regex")
8093 .prop_filter("must not be keyword", |s| !is_keyword(s))
8094 .boxed()
8095 }
8096
8097 fn arb_literal() -> BoxedStrategy<String> {
8099 prop_oneof![
8100 any::<i32>().prop_map(|n| n.to_string()),
8101 (1i32..1000).prop_map(|n| format!("{n}.{}", n % 100)),
8102 arb_ident().prop_map(|s| format!("'{s}'")),
8103 Just("NULL".to_string()),
8104 Just("TRUE".to_string()),
8105 Just("FALSE".to_string()),
8106 ]
8107 .boxed()
8108 }
8109
8110 fn arb_expr(depth: u32) -> BoxedStrategy<String> {
8112 if depth == 0 {
8113 prop_oneof![
8114 arb_literal(),
8115 arb_ident(),
8116 (arb_ident(), arb_ident()).prop_map(|(t, c)| format!("{t}.{c}")),
8117 ]
8118 .boxed()
8119 } else {
8120 let leaf = arb_expr(0);
8121 prop_oneof![
8122 4 => leaf,
8123 2 => (arb_expr(depth - 1), prop_oneof![
8125 Just("+"), Just("-"), Just("*"), Just("/"),
8126 Just("="), Just("!="), Just("<"), Just("<="),
8127 Just(">"), Just(">="), Just("AND"), Just("OR"),
8128 Just("||"),
8129 ], arb_expr(depth - 1))
8130 .prop_map(|(l, op, r)| format!("({l} {op} {r})")),
8131 1 => arb_expr(depth - 1).prop_map(|e| format!("(-{e})")),
8133 1 => arb_expr(depth - 1).prop_map(|e| format!("(NOT {e})")),
8134 1 => arb_expr(depth - 1).prop_map(|e| format!("{e} IS NULL")),
8136 1 => arb_expr(depth - 1).prop_map(|e| format!("{e} IS NOT NULL")),
8137 1 => arb_expr(depth - 1).prop_map(|e| format!("{e} ISNULL")),
8139 1 => arb_expr(depth - 1).prop_map(|e| format!("{e} NOTNULL")),
8140 1 => arb_expr(depth - 1).prop_map(|e| format!("{e} COLLATE nocase")),
8142 2 => (arb_expr(depth - 1), prop_oneof![
8147 Just("+"), Just("*"), Just("="), Just("<"),
8148 Just("AND"), Just("OR"), Just("||"), Just("IS"),
8149 Just("IS NOT"),
8150 ], arb_expr(depth - 1))
8151 .prop_map(|(l, op, r)| format!("{l} {op} {r}")),
8152 1 => (arb_expr(depth - 1), arb_expr(0), arb_expr(0))
8154 .prop_map(|(e, lo, hi)| format!("{e} BETWEEN {lo} AND {hi}")),
8155 1 => (arb_expr(depth - 1), proptest::collection::vec(arb_expr(0), 1..4))
8157 .prop_map(|(e, items)| format!("{e} IN ({})", items.join(", "))),
8158 1 => (arb_expr(depth - 1), arb_ident())
8160 .prop_map(|(e, p)| format!("{e} LIKE '{p}'")),
8161 1 => arb_expr(depth - 1).prop_map(|e| format!("CAST({e} AS TEXT)")),
8163 1 => (arb_expr(depth - 1), arb_expr(0), arb_expr(0))
8165 .prop_map(|(c, t, el)| format!("CASE WHEN {c} THEN {t} ELSE {el} END")),
8166 1 => (arb_ident(), proptest::collection::vec(arb_expr(0), 0..3))
8168 .prop_map(|(name, args)| format!("{name}({})", args.join(", "))),
8169 1 => arb_expr(0).prop_map(|e| format!("(SELECT {e})")),
8171 ]
8172 .boxed()
8173 }
8174 }
8175
8176 fn arb_select() -> BoxedStrategy<String> {
8178 use std::fmt::Write as _;
8179
8180 let cols =
8181 proptest::collection::vec(arb_expr(1), 1..4).prop_map(|cols| cols.join(", "));
8182 let table = arb_ident();
8183 let where_clause = prop::option::of(arb_expr(1));
8184 let order_by = prop::option::of(arb_ident());
8185 let limit = prop::option::of(1u32..100);
8186
8187 (cols, table, where_clause, order_by, limit)
8188 .prop_map(|(cols, tbl, wh, ord, lim)| {
8189 let mut sql = format!("SELECT {cols} FROM {tbl}");
8190 if let Some(w) = wh {
8191 write!(sql, " WHERE {w}").expect("writing to String should not fail");
8192 }
8193 if let Some(o) = ord {
8194 write!(sql, " ORDER BY {o}").expect("writing to String should not fail");
8195 }
8196 if let Some(l) = lim {
8197 write!(sql, " LIMIT {l}").expect("writing to String should not fail");
8198 }
8199 sql
8200 })
8201 .boxed()
8202 }
8203
8204 fn arb_insert() -> BoxedStrategy<String> {
8206 let ncols = 1usize..4;
8207 ncols
8208 .prop_flat_map(|n| {
8209 let tbl = arb_ident();
8210 let cols = proptest::collection::vec(arb_ident(), n..=n);
8211 let vals = proptest::collection::vec(arb_literal(), n..=n);
8212 (tbl, cols, vals).prop_map(|(t, cs, vs): (String, Vec<String>, Vec<String>)| {
8213 format!(
8214 "INSERT INTO {t} ({}) VALUES ({})",
8215 cs.join(", "),
8216 vs.join(", ")
8217 )
8218 })
8219 })
8220 .boxed()
8221 }
8222
8223 fn arb_statement() -> BoxedStrategy<String> {
8225 prop_oneof![
8226 6 => arb_select(),
8227 3 => arb_insert(),
8228 1 => arb_expr(2).prop_map(|e| format!("SELECT {e}")),
8229 1 => (arb_ident(), arb_expr(1))
8230 .prop_map(|(t, w)| format!("DELETE FROM {t} WHERE {w}")),
8231 1 => (arb_ident(), arb_ident(), arb_literal(), arb_expr(1))
8232 .prop_map(|(t, c, v, w)| format!("UPDATE {t} SET {c} = {v} WHERE {w}")),
8233 ]
8234 .boxed()
8235 }
8236
8237 fn try_parse_one(sql: &str) -> Option<Statement> {
8239 let mut p = Parser::from_sql(sql);
8240 let (stmts, errs) = p.parse_all();
8241 if errs.is_empty() && stmts.len() == 1 {
8242 Some(stmts.into_iter().next().unwrap())
8243 } else {
8244 None
8245 }
8246 }
8247
8248 proptest::proptest! {
8249 #![proptest_config(proptest::prelude::ProptestConfig::with_cases(1000))]
8250
8251 #[test]
8252 fn test_parser_roundtrip_proptest(sql in arb_statement()) {
8253 let Some(ast1) = try_parse_one(&sql) else {
8255 return Ok(()); };
8257
8258 let rendered1 = ast1.to_string();
8260
8261 let Some(ast2) = try_parse_one(&rendered1) else {
8263 let msg = format!("re-parse failed for rendered SQL: {rendered1:?}");
8264 prop_assert!(false, "{}", msg);
8265 unreachable!()
8266 };
8267
8268 let rendered2 = ast2.to_string();
8270 let msg = format!(
8271 "round-trip not idempotent:\n original: {sql}\n rendered1: {rendered1}\n rendered2: {rendered2}"
8272 );
8273 prop_assert_eq!(rendered1, rendered2, "{}", msg);
8274 }
8275 }
8276 }
8277
8278 mod proptest_properties {
8283 use super::*;
8284 use proptest::prelude::*;
8285
8286 fn arb_ident() -> BoxedStrategy<String> {
8288 prop::string::string_regex("[a-z][a-z0-9]{0,5}")
8289 .expect("valid regex")
8290 .prop_filter("must not be keyword", |s| {
8291 TokenKind::lookup_keyword(s).is_none()
8292 })
8293 .boxed()
8294 }
8295
8296 fn arb_literal() -> BoxedStrategy<String> {
8297 prop_oneof![
8298 any::<i32>().prop_map(|n| n.to_string()),
8299 (1i32..1000).prop_map(|n| format!("{n}.{}", n % 100)),
8300 arb_ident().prop_map(|s| format!("'{s}'")),
8301 Just("NULL".to_string()),
8302 Just("TRUE".to_string()),
8303 Just("FALSE".to_string()),
8304 ]
8305 .boxed()
8306 }
8307
8308 fn arb_expr(depth: u32) -> BoxedStrategy<String> {
8309 if depth == 0 {
8310 prop_oneof![arb_literal(), arb_ident(),].boxed()
8311 } else {
8312 let leaf = arb_expr(0);
8313 prop_oneof![
8314 4 => leaf,
8315 2 => (arb_expr(depth - 1), prop_oneof![
8316 Just("+"), Just("-"), Just("*"), Just("/"),
8317 Just("="), Just("!="), Just("<"), Just("<="),
8318 Just(">"), Just(">="), Just("AND"), Just("OR"),
8319 ], arb_expr(depth - 1))
8320 .prop_map(|(l, op, r)| format!("({l} {op} {r})")),
8321 1 => arb_expr(depth - 1).prop_map(|e| format!("(-{e})")),
8322 1 => arb_expr(depth - 1).prop_map(|e| format!("(NOT {e})")),
8323 ]
8324 .boxed()
8325 }
8326 }
8327
8328 fn arb_select() -> BoxedStrategy<String> {
8329 use std::fmt::Write as _;
8330 let cols =
8331 proptest::collection::vec(arb_expr(1), 1..4).prop_map(|cols| cols.join(", "));
8332 let table = arb_ident();
8333 let where_clause = prop::option::of(arb_expr(1));
8334 (cols, table, where_clause)
8335 .prop_map(|(cols, tbl, wh)| {
8336 let mut sql = format!("SELECT {cols} FROM {tbl}");
8337 if let Some(w) = wh {
8338 write!(sql, " WHERE {w}").expect("writing to String should not fail");
8339 }
8340 sql
8341 })
8342 .boxed()
8343 }
8344
8345 fn arb_statement() -> BoxedStrategy<String> {
8346 prop_oneof![
8347 6 => arb_select(),
8348 3 => {
8349 let ncols = 1usize..4;
8350 ncols
8351 .prop_flat_map(|n| {
8352 let tbl = arb_ident();
8353 let cols = proptest::collection::vec(arb_ident(), n..=n);
8354 let vals = proptest::collection::vec(arb_literal(), n..=n);
8355 (tbl, cols, vals).prop_map(
8356 |(t, cs, vs): (String, Vec<String>, Vec<String>)| {
8357 format!(
8358 "INSERT INTO {t} ({}) VALUES ({})",
8359 cs.join(", "),
8360 vs.join(", ")
8361 )
8362 },
8363 )
8364 })
8365 .boxed()
8366 },
8367 1 => arb_expr(2).prop_map(|e| format!("SELECT {e}")),
8368 1 => (arb_ident(), arb_expr(1))
8369 .prop_map(|(t, w)| format!("DELETE FROM {t} WHERE {w}")),
8370 1 => (arb_ident(), arb_ident(), arb_literal(), arb_expr(1))
8371 .prop_map(|(t, c, v, w)| format!("UPDATE {t} SET {c} = {v} WHERE {w}")),
8372 ]
8373 .boxed()
8374 }
8375
8376 proptest::proptest! {
8378 #![proptest_config(proptest::prelude::ProptestConfig::with_cases(500))]
8379
8380 #[test]
8381 fn test_parser_determinism(sql in arb_statement()) {
8382 let mut p1 = Parser::from_sql(&sql);
8383 let (stmts1, errs1) = p1.parse_all();
8384
8385 let mut p2 = Parser::from_sql(&sql);
8386 let (stmts2, errs2) = p2.parse_all();
8387
8388 let msg_stmt = format!("different statement counts for: {sql}");
8390 prop_assert_eq!(stmts1.len(), stmts2.len(), "{}", msg_stmt);
8391 let msg_err = format!("different error counts for: {sql}");
8392 prop_assert_eq!(errs1.len(), errs2.len(), "{}", msg_err);
8393
8394 if errs1.is_empty() && !stmts1.is_empty() {
8396 for (s1, s2) in stmts1.iter().zip(stmts2.iter()) {
8397 let r1 = s1.to_string();
8398 let r2 = s2.to_string();
8399 let msg_det = format!("non-deterministic parse output for: {sql}");
8400 prop_assert_eq!(r1, r2, "{}", msg_det);
8401 }
8402 }
8403 }
8404 }
8405
8406 proptest::proptest! {
8408 #![proptest_config(proptest::prelude::ProptestConfig::with_cases(2000))]
8409
8410 #[test]
8411 fn test_parser_fuzz_no_panic(input in prop::collection::vec(any::<u8>(), 0..256)) {
8412 let sql = String::from_utf8_lossy(&input);
8413 let mut p = Parser::from_sql(&sql);
8415 let _ = p.parse_all();
8416 }
8417 }
8418
8419 proptest::proptest! {
8421 #![proptest_config(proptest::prelude::ProptestConfig::with_cases(1000))]
8422
8423 #[test]
8424 fn test_parser_fuzz_near_valid(
8425 prefix in prop_oneof![
8426 Just("SELECT "),
8427 Just("INSERT INTO "),
8428 Just("DELETE FROM "),
8429 Just("UPDATE "),
8430 Just("CREATE TABLE "),
8431 Just("DROP TABLE "),
8432 Just("BEGIN "),
8433 Just("PRAGMA "),
8434 ],
8435 suffix in prop::string::string_regex("[a-zA-Z0-9_ ,.*=<>!()'\";+\\-/]{0,100}")
8436 .expect("valid regex")
8437 ) {
8438 let sql = format!("{prefix}{suffix}");
8439 let mut p = Parser::from_sql(&sql);
8440 let _ = p.parse_all();
8441 }
8442 }
8443
8444 proptest::proptest! {
8446 #![proptest_config(proptest::prelude::ProptestConfig::with_cases(200))]
8447
8448 #[test]
8449 fn test_parser_unicode_identifiers(
8450 name in prop::string::string_regex("[\\p{L}][\\p{L}\\p{N}_]{0,10}")
8451 .expect("valid regex")
8452 .prop_filter("must not be keyword", |s| {
8453 TokenKind::lookup_keyword(s).is_none()
8454 })
8455 ) {
8456 let sql = format!("SELECT \"{name}\" FROM \"{name}\"");
8458 let mut p = Parser::from_sql(&sql);
8459 let (stmts, errs) = p.parse_all();
8460 prop_assert!(
8461 errs.is_empty(),
8462 "Unicode identifier should parse: {sql}, errors: {errs:?}"
8463 );
8464 prop_assert_eq!(stmts.len(), 1);
8465 }
8466 }
8467
8468 proptest::proptest! {
8470 #![proptest_config(proptest::prelude::ProptestConfig::with_cases(300))]
8471
8472 #[test]
8473 fn test_parser_rejects_incomplete_statements(
8474 kind in prop_oneof![
8475 Just("SELECT"),
8476 Just("SELECT FROM"),
8477 Just("INSERT INTO"),
8478 Just("DELETE"),
8479 Just("UPDATE SET"),
8480 Just("CREATE"),
8481 Just("CREATE TABLE"),
8482 Just("DROP"),
8483 ],
8484 trailing in prop::option::of(
8485 prop::string::string_regex("[;, ]{0,3}").expect("valid regex")
8486 )
8487 ) {
8488 let sql = match trailing {
8489 Some(t) => format!("{kind}{t}"),
8490 None => kind.to_string(),
8491 };
8492 let mut p = Parser::from_sql(&sql);
8493 let (stmts, errs) = p.parse_all();
8494 prop_assert!(
8498 !errs.is_empty() || stmts.is_empty(),
8499 "Expected rejection of incomplete SQL: {sql}, got {stmts:?}"
8500 );
8501 }
8502 }
8503
8504 proptest::proptest! {
8507 #![proptest_config(proptest::prelude::ProptestConfig::with_cases(200))]
8508
8509 #[test]
8510 fn test_parser_multi_statement_count(
8511 stmts in proptest::collection::vec(arb_statement(), 1..4)
8512 ) {
8513 let sql = stmts.join("; ");
8514 let mut p = Parser::from_sql(&sql);
8515 let (parsed, errors) = p.parse_all();
8516 if errors.is_empty() {
8518 prop_assert!(
8519 parsed.len() >= stmts.len(),
8520 "Expected at least {} statements from: {sql}, got {}",
8521 stmts.len(),
8522 parsed.len()
8523 );
8524 }
8525 }
8526 }
8527 }
8528
8529 #[test]
8530 fn test_parse_statements_with_scratch_reuses_token_and_error_capacity() {
8531 let mut scratch = StatementParseScratch::default();
8532 let err = parse_statements_with_scratch("SELECT FROM", &mut scratch)
8533 .expect_err("malformed SQL should surface a parse error");
8534 assert!(
8535 err.message.contains("expected"),
8536 "malformed parse should preserve its diagnostic detail",
8537 );
8538 let warmed_token_capacity = scratch.token_capacity();
8539 let warmed_error_capacity = scratch.error_capacity();
8540 assert!(
8541 warmed_token_capacity > 0,
8542 "parse scratch should warm token storage"
8543 );
8544 assert!(
8545 warmed_error_capacity > 0,
8546 "parse scratch should warm error storage"
8547 );
8548
8549 let statements = parse_statements_with_scratch("SELECT 1;", &mut scratch)
8550 .expect("follow-up parse should succeed");
8551 assert_eq!(statements.len(), 1);
8552 assert_eq!(
8553 scratch.token_capacity(),
8554 warmed_token_capacity,
8555 "successful parse should reuse token scratch capacity",
8556 );
8557 assert_eq!(
8558 scratch.error_capacity(),
8559 warmed_error_capacity,
8560 "successful parse should preserve error scratch capacity for the next recovery path",
8561 );
8562 }
8563
8564 #[test]
8565 fn test_parse_statements_with_scratch_reuses_identifier_interns_across_parses() {
8566 let mut scratch = StatementParseScratch::default();
8567 let mut sql = String::from("SELECT ");
8568 for i in 0..32 {
8569 if i > 0 {
8570 sql.push_str(", ");
8571 }
8572 sql.push_str(&format!("unique_identifier_{i} AS unique_alias_{i}"));
8573 }
8574 sql.push(';');
8575
8576 let statements = parse_statements_with_scratch(&sql, &mut scratch)
8577 .expect("identifier-heavy statement should parse");
8578 assert_eq!(statements.len(), 1);
8579 let interner_len = scratch.identifier_interner_len();
8580 assert!(
8581 interner_len > 0,
8582 "scratch should retain identifier interns for the next parse",
8583 );
8584
8585 let statements = parse_statements_with_scratch(&sql, &mut scratch)
8586 .expect("repeat parse should also succeed");
8587 assert_eq!(statements.len(), 1);
8588 assert_eq!(
8589 scratch.identifier_interner_len(),
8590 interner_len,
8591 "repeated parse should reuse the retained interner set instead of growing it",
8592 );
8593
8594 scratch.reset();
8595 assert!(
8596 scratch.identifier_interner_is_empty(),
8597 "explicit scratch reset should also keep the interner logically empty",
8598 );
8599 }
8600
8601 #[test]
8602 fn test_parse_statements_with_scratch_drops_oversized_identifier_interner() {
8603 let mut scratch = StatementParseScratch::default();
8604 let mut sql = String::from("SELECT ");
8605 for i in 0..300 {
8606 if i > 0 {
8607 sql.push_str(", ");
8608 }
8609 sql.push_str(&format!(
8610 "very_long_unique_identifier_{i:03} AS alias_{i:03}"
8611 ));
8612 }
8613 sql.push(';');
8614
8615 let statements = parse_statements_with_scratch(&sql, &mut scratch)
8616 .expect("oversized identifier-heavy statement should parse");
8617 assert_eq!(statements.len(), 1);
8618 assert!(
8619 scratch.identifier_interner_is_empty(),
8620 "oversized identifier interners should be dropped instead of retained indefinitely",
8621 );
8622 }
8623
8624 #[test]
8628 fn create_table_quoted_reserved_word_key() {
8629 parse_ok(r#"CREATE TABLE "meta" ("key" TEXT, "val" TEXT);"#);
8631 }
8632
8633 #[test]
8634 fn create_table_unquoted_key_column() {
8635 parse_ok("CREATE TABLE meta (key TEXT, val TEXT);");
8637 }
8638
8639 #[test]
8640 fn create_table_quoted_order_column() {
8641 parse_ok(r#"CREATE TABLE t ("order" INTEGER);"#);
8643 }
8644
8645 #[test]
8646 fn create_table_quoted_select_column() {
8647 parse_ok(r#"CREATE TABLE t ("select" TEXT);"#);
8649 }
8650
8651 #[test]
8652 fn select_with_reserved_word_column_key() {
8653 parse_ok("SELECT key FROM meta;");
8655 }
8656
8657 #[test]
8658 fn select_with_reserved_word_column_value() {
8659 parse_ok("SELECT value FROM meta;");
8661 }
8662
8663 #[test]
8664 fn select_with_reserved_word_column_order() {
8665 parse_ok(r#"SELECT "order" FROM t;"#);
8667 }
8668
8669 #[test]
8670 fn where_clause_with_reserved_word_column() {
8671 parse_ok("UPDATE meta SET val = '2.0' WHERE key = 'version';");
8673 }
8674
8675 #[test]
8676 fn update_set_reserved_word_column() {
8677 parse_ok(r#"UPDATE meta SET "key" = 'newkey' WHERE "key" = 'oldkey';"#);
8679 }
8680
8681 #[test]
8682 fn delete_where_reserved_word_column() {
8683 parse_ok("DELETE FROM meta WHERE key = 'version';");
8684 }
8685
8686 #[test]
8687 fn persistence_dump_with_reserved_word_columns() {
8688 let sql = concat!(
8691 r#"CREATE TABLE "meta" ("key" TEXT, "value" TEXT);"#,
8692 "\n",
8693 r#"INSERT INTO "meta" VALUES ('version', '1.0');"#,
8694 "\n",
8695 r#"INSERT INTO "meta" VALUES ('author', 'test');"#,
8696 );
8697 let mut p = Parser::from_sql(sql);
8698 let (stmts, errs) = p.parse_all();
8699 assert!(
8700 errs.is_empty(),
8701 "persistence dump with reserved-word columns should parse cleanly: {errs:?}"
8702 );
8703 assert_eq!(stmts.len(), 3);
8704 }
8705
8706 #[test]
8707 fn create_table_with_single_quoted_name_parses_cleanly() {
8708 let sql = "CREATE TABLE 'fts_messages_data'(id INTEGER PRIMARY KEY, block BLOB);";
8709 let mut p = Parser::from_sql(sql);
8710 let (stmts, errs) = p.parse_all();
8711 assert!(
8712 errs.is_empty(),
8713 "single-quoted sqlite_master shadow-table SQL should parse cleanly: {errs:?}"
8714 );
8715 assert_eq!(stmts.len(), 1);
8716 match &stmts[0] {
8717 Statement::CreateTable(stmt) => {
8718 assert_eq!(stmt.name.name, "fts_messages_data");
8719 }
8720 other => panic!("expected CreateTable, got {other:?}"),
8721 }
8722 }
8723
8724 #[test]
8725 fn select_qualified_column_with_alias() {
8726 let stmt = parse_one("SELECT a.name AS from_name FROM users a");
8729 if let Statement::Select(s) = stmt {
8730 if let SelectCore::Select { columns, .. } = &s.body.select {
8731 assert_eq!(columns.len(), 1);
8732 match &columns[0] {
8733 ResultColumn::Expr { expr, alias } => {
8734 assert_eq!(
8736 alias.as_deref(),
8737 Some("from_name"),
8738 "alias should be 'from_name', got {alias:?}"
8739 );
8740 if let Expr::Column(col_ref, _) = expr {
8742 assert_eq!(col_ref.table.as_deref(), Some("a"));
8743 assert_eq!(col_ref.column.as_ref(), "name");
8744 } else {
8745 panic!("expected Column expression, got {expr:?}");
8746 }
8747 }
8748 other => panic!("expected Expr variant, got {other:?}"),
8749 }
8750 } else {
8751 panic!("expected Select core");
8752 }
8753 } else {
8754 panic!("expected Select statement");
8755 }
8756 }
8757
8758 #[test]
8759 fn select_qualified_column_with_implicit_alias() {
8760 let stmt = parse_one("SELECT a.name from_name FROM users a");
8762 if let Statement::Select(s) = stmt {
8763 if let SelectCore::Select { columns, .. } = &s.body.select {
8764 assert_eq!(columns.len(), 1);
8765 match &columns[0] {
8766 ResultColumn::Expr { expr, alias } => {
8767 assert_eq!(
8769 alias.as_deref(),
8770 Some("from_name"),
8771 "implicit alias should be 'from_name', got {alias:?}"
8772 );
8773 if let Expr::Column(col_ref, _) = expr {
8775 assert_eq!(col_ref.table.as_deref(), Some("a"));
8776 assert_eq!(col_ref.column.as_ref(), "name");
8777 } else {
8778 panic!("expected Column expression, got {expr:?}");
8779 }
8780 }
8781 other => panic!("expected Expr variant, got {other:?}"),
8782 }
8783 } else {
8784 panic!("expected Select core");
8785 }
8786 } else {
8787 panic!("expected Select statement");
8788 }
8789 }
8790
8791 #[test]
8792 fn select_implicit_alias_non_reserved_keyword() {
8793 let stmt = parse_one("SELECT 1 action");
8796 if let Statement::Select(s) = stmt {
8797 if let SelectCore::Select { columns, .. } = &s.body.select {
8798 if let ResultColumn::Expr { alias, .. } = &columns[0] {
8799 assert_eq!(
8800 alias.as_deref(),
8801 Some("action"),
8802 "implicit alias 'action' (keyword) failed to parse"
8803 );
8804 } else {
8805 unreachable!("expected Expr result column");
8806 }
8807 } else {
8808 unreachable!("expected Select core");
8809 }
8810 } else {
8811 unreachable!("expected Select");
8812 }
8813 }
8814}