diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index 324c52d1d5..9aa61debe1 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -1092,6 +1092,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 3deffafb7d..82b04d3ba5 100644 --- a/src/dialect/sqlite.rs +++ b/src/dialect/sqlite.rs @@ -184,4 +184,9 @@ impl Dialect for SQLiteDialect { fn supports_over_keyword_as_column_alias(&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 4dfd3d51bc..fce061975b 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -4591,9 +4591,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 eaadea263e..7ccc0aac4f 100644 --- a/tests/sqlparser_sqlite.rs +++ b/tests/sqlparser_sqlite.rs @@ -955,6 +955,47 @@ fn test_drop_trigger() { } } +#[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"); + + // 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"), + Err(ParserError::ParserError( + "Expected: AND, found: >".to_string() + )) + ); +} + #[test] fn parse_pattern_operators_bind_at_like_precedence() { fn where_operator(sql: &str) -> BinaryOperator {