From e1283609b985ac178d2a100188a3433b520e5f70 Mon Sep 17 00:00:00 2001 From: LucaCappelletti94 Date: Wed, 23 Sep 2026 12:37:55 +0200 Subject: [PATCH 1/2] SQLite: Parse the `BETWEEN` lower bound up to its `AND` --- src/dialect/mod.rs | 5 +++++ src/dialect/sqlite.rs | 7 ++++++- src/parser/mod.rs | 4 +--- tests/sqlparser_sqlite.rs | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index 69bbe355c8..14c7f17dc7 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -1084,6 +1084,11 @@ pub trait Dialect: Debug + Any { 0 } + /// Precedence used to stop parsing the low bound expression of a BETWEEN predicate. + fn between_low_bound_precedence(&self) -> u8 { + self.prec_value(Precedence::Between) + } + /// Returns true if this dialect requires the `TABLE` keyword after `DESCRIBE` /// /// Defaults to false. diff --git a/src/dialect/sqlite.rs b/src/dialect/sqlite.rs index f63e4cbe13..5947bf9031 100644 --- a/src/dialect/sqlite.rs +++ b/src/dialect/sqlite.rs @@ -20,7 +20,7 @@ use alloc::boxed::Box; use crate::ast::BinaryOperator; use crate::ast::{Expr, Statement}; -use crate::dialect::Dialect; +use crate::dialect::{Dialect, Precedence}; use crate::keywords::Keyword; use crate::parser::{Parser, ParserError}; @@ -146,4 +146,9 @@ impl Dialect for SQLiteDialect { fn supports_parens_around_table_factor(&self) -> bool { true } + + /// Comparison and postfix operators bind within the BETWEEN low bound in SQLite (). + fn between_low_bound_precedence(&self) -> u8 { + self.prec_value(Precedence::And) + } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index db54296fce..f08be7a537 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -4548,9 +4548,7 @@ impl<'a> Parser<'a> { /// Parses `BETWEEN AND `, assuming the `BETWEEN` keyword was already consumed. pub fn parse_between(&mut self, expr: Expr, negated: bool) -> Result { - // Stop parsing subexpressions for and on tokens with - // precedence lower than that of `BETWEEN`, such as `AND`, `IS`, etc. - let low = self.parse_subexpr(self.dialect.prec_value(Precedence::Between))?; + let low = self.parse_subexpr(self.dialect.between_low_bound_precedence())?; self.expect_keyword_is(Keyword::AND)?; let high = self.parse_subexpr(self.dialect.prec_value(Precedence::Between))?; Ok(Expr::Between { diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs index 2e60c75efa..9661d8217b 100644 --- a/tests/sqlparser_sqlite.rs +++ b/tests/sqlparser_sqlite.rs @@ -1142,6 +1142,39 @@ fn parse_parenthesized_table_factor() { ); } +#[test] +fn parse_between_low_bound_precedence() { + match sqlite().verified_expr("a BETWEEN b > 1 AND c") { + Expr::Between { low, .. } => assert!( + matches!( + *low, + Expr::BinaryOp { + op: BinaryOperator::Gt, + .. + } + ), + "{low:?}" + ), + other => panic!("expected BETWEEN, got {other:?}"), + } + sqlite().verified_expr("a BETWEEN b LIKE 'x' AND c"); + sqlite().verified_expr("a BETWEEN b IS NOT NULL AND c"); + sqlite().expr_parses_to("a BETWEEN b NOTNULL AND c", "a BETWEEN b IS NOT NULL AND c"); + sqlite().verified_expr("a BETWEEN b BETWEEN 1 AND 2 AND c"); + + // Operators after the high bound apply to the whole BETWEEN. + sqlite().verified_expr("5 BETWEEN 1 AND NULL IS NULL"); + sqlite().verified_expr("5 BETWEEN 1 AND 10 = 1"); + + assert_eq!( + TestedDialects::new(vec![Box::new(GenericDialect {})]) + .parse_sql_statements("SELECT a BETWEEN b > 1 AND c FROM t"), + Err(ParserError::ParserError( + "Expected: AND, found: >".to_string() + )) + ); +} + fn sqlite() -> TestedDialects { TestedDialects::new(vec![Box::new(SQLiteDialect {})]) } From 9f9b65e6c6c1c2dd3c0827fae38d7b7dcbbb4cd1 Mon Sep 17 00:00:00 2001 From: LucaCappelletti94 Date: Tue, 20 Oct 2026 12:51:20 +0200 Subject: [PATCH 2/2] Cover malformed parenthesized `BETWEEN` low bounds --- tests/sqlparser_sqlite.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs index 9661d8217b..1b7f746bca 100644 --- a/tests/sqlparser_sqlite.rs +++ b/tests/sqlparser_sqlite.rs @@ -1166,6 +1166,14 @@ fn parse_between_low_bound_precedence() { sqlite().verified_expr("5 BETWEEN 1 AND NULL IS NULL"); sqlite().verified_expr("5 BETWEEN 1 AND 10 = 1"); + // The inner AND belongs to the bound expression, so the missing `)` is reported at FROM. + assert_eq!( + sqlite().parse_sql_statements("SELECT a BETWEEN (1 AND 2 FROM t"), + Err(ParserError::ParserError( + "Expected: ), found: FROM".to_string() + )) + ); + assert_eq!( TestedDialects::new(vec![Box::new(GenericDialect {})]) .parse_sql_statements("SELECT a BETWEEN b > 1 AND c FROM t"),