You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by lu...@apache.org on 2026/10/17 16:53:11 UTC
(datafusion-sqlparser-rs) 01/01: PostgreSQL, DuckDB, Spark, Databricks: Accept a comparison in the `BETWEEN` lower bound
This is an automated email from the ASF dual-hosted git repository.
LucaCappelletti94 pushed a commit to branch between-low-bound-comparison
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git
commit ec5975ac52b0092370689d17cf59cf80617e0e47
Author: LucaCappelletti94 <ca...@gmail.com>
AuthorDate: Sat Oct 17 18:50:36 2026 +0200
PostgreSQL, DuckDB, Spark, Databricks: Accept a comparison in the `BETWEEN` lower bound
---
src/dialect/databricks.rs | 4 ++++
src/dialect/duckdb.rs | 4 ++++
src/dialect/generic.rs | 4 ++++
src/dialect/mod.rs | 6 ++++++
src/dialect/postgresql.rs | 4 ++++
src/dialect/spark.rs | 4 ++++
src/parser/mod.rs | 17 +++++++++++++++-
tests/sqlparser_common.rs | 52 +++++++++++++++++++++++++++++++++++++++++++++++
tests/sqlparser_sqlite.rs | 8 --------
9 files changed, 94 insertions(+), 9 deletions(-)
diff --git a/src/dialect/databricks.rs b/src/dialect/databricks.rs
index ca8610dd6..a2a6a97d5 100644
--- a/src/dialect/databricks.rs
+++ b/src/dialect/databricks.rs
@@ -31,6 +31,10 @@ impl Dialect for DatabricksDialect {
matches!(ch, '`')
}
+ fn supports_comparison_in_between_low_bound(&self) -> bool {
+ true
+ }
+
/// See <https://docs.databricks.com/en/sql/language-manual/sql-ref-identifiers.html>
fn identifier_quote_style(&self, _identifier: &str) -> Option<char> {
Some('`')
diff --git a/src/dialect/duckdb.rs b/src/dialect/duckdb.rs
index ce72f578a..6567bdd29 100644
--- a/src/dialect/duckdb.rs
+++ b/src/dialect/duckdb.rs
@@ -57,6 +57,10 @@ impl Dialect for DuckDbDialect {
true
}
+ fn supports_comparison_in_between_low_bound(&self) -> bool {
+ true
+ }
+
fn supports_named_fn_args_with_eq_operator(&self) -> bool {
true
}
diff --git a/src/dialect/generic.rs b/src/dialect/generic.rs
index 694eb8f19..9f1fbd600 100644
--- a/src/dialect/generic.rs
+++ b/src/dialect/generic.rs
@@ -173,6 +173,10 @@ impl Dialect for GenericDialect {
true
}
+ fn supports_comparison_in_between_low_bound(&self) -> bool {
+ true
+ }
+
fn supports_comment_on(&self) -> bool {
true
}
diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs
index 1df53844e..09b10380c 100644
--- a/src/dialect/mod.rs
+++ b/src/dialect/mod.rs
@@ -1089,6 +1089,12 @@ pub trait Dialect: Debug + Any {
self.prec_value(Precedence::Between)
}
+ /// Returns true if the low bound of a `BETWEEN` predicate may end with one comparison,
+ /// as in `a BETWEEN b > 1 AND c`, while `IS`, `LIKE` and `BETWEEN` still end the bound.
+ fn supports_comparison_in_between_low_bound(&self) -> bool {
+ false
+ }
+
/// Returns true if this dialect requires the `TABLE` keyword after `DESCRIBE`
///
/// Defaults to false.
diff --git a/src/dialect/postgresql.rs b/src/dialect/postgresql.rs
index 65bfd46a7..c98367d07 100644
--- a/src/dialect/postgresql.rs
+++ b/src/dialect/postgresql.rs
@@ -249,6 +249,10 @@ impl Dialect for PostgreSqlDialect {
true
}
+ fn supports_comparison_in_between_low_bound(&self) -> bool {
+ true
+ }
+
/// see <https://www.postgresql.org/docs/current/sql-comment.html>
fn supports_comment_on(&self) -> bool {
true
diff --git a/src/dialect/spark.rs b/src/dialect/spark.rs
index 024aec715..7bfe42a06 100644
--- a/src/dialect/spark.rs
+++ b/src/dialect/spark.rs
@@ -36,6 +36,10 @@ impl Dialect for SparkSqlDialect {
matches!(ch, '`')
}
+ fn supports_comparison_in_between_low_bound(&self) -> bool {
+ true
+ }
+
/// See <https://spark.apache.org/docs/latest/sql-ref-identifier.html>
fn identifier_quote_style(&self, _identifier: &str) -> Option<char> {
Some('`')
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 0931d02d5..8620aad81 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -4548,7 +4548,22 @@ impl<'a> Parser<'a> {
/// Parses `BETWEEN <low> AND <high>`, assuming the `BETWEEN` keyword was already consumed.
pub fn parse_between(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParserError> {
- let low = self.parse_subexpr(self.dialect.between_low_bound_precedence())?;
+ let mut low = self.parse_subexpr(self.dialect.between_low_bound_precedence())?;
+ if self.dialect.supports_comparison_in_between_low_bound()
+ && matches!(
+ self.peek_token_ref().token,
+ Token::Eq
+ | Token::DoubleEq
+ | Token::Neq
+ | Token::Lt
+ | Token::LtEq
+ | Token::Gt
+ | Token::GtEq
+ | Token::Spaceship
+ )
+ {
+ low = self.parse_infix(low, self.dialect.prec_value(Precedence::Between))?;
+ }
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_common.rs b/tests/sqlparser_common.rs
index 1959bcd34..a5f2765fc 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -20699,3 +20699,55 @@ fn parse_cte_materialization() {
}
}
}
+
+#[test]
+fn parse_comparison_in_between_low_bound() {
+ let dialects = all_dialects_where(|d| d.supports_comparison_in_between_low_bound());
+ for (sql, expected) in [
+ ("a BETWEEN b > 1 AND c", BinaryOperator::Gt),
+ ("a BETWEEN b <> 1 AND c", BinaryOperator::NotEq),
+ ("a BETWEEN b + 1 >= 2 AND c", BinaryOperator::GtEq),
+ ] {
+ match dialects.verified_expr(sql) {
+ Expr::Between { low, .. } => assert!(
+ matches!(&*low, Expr::BinaryOp { op, .. } if *op == expected),
+ "{sql}: {low:?}"
+ ),
+ other => panic!("{sql}: expected BETWEEN, got {other:?}"),
+ }
+ }
+ for (sql, error) in [
+ (
+ "SELECT a BETWEEN b = 1 = true AND c",
+ "Expected: AND, found: =",
+ ),
+ (
+ "SELECT a BETWEEN b LIKE 'x' AND c",
+ "Expected: AND, found: LIKE",
+ ),
+ (
+ "SELECT a BETWEEN b IS NULL AND c",
+ "Expected: AND, found: IS",
+ ),
+ (
+ "SELECT a BETWEEN b BETWEEN 1 AND 2 AND c",
+ "Expected: AND, found: BETWEEN",
+ ),
+ ] {
+ assert_eq!(
+ dialects.parse_sql_statements(sql),
+ Err(ParserError::ParserError(error.to_string())),
+ "{sql}"
+ );
+ }
+
+ assert_eq!(
+ all_dialects_except(|d| {
+ d.supports_comparison_in_between_low_bound() || d.is::<SQLiteDialect>()
+ })
+ .parse_sql_statements("SELECT a BETWEEN b > 1 AND c"),
+ Err(ParserError::ParserError(
+ "Expected: AND, found: >".to_string()
+ ))
+ );
+}
diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs
index 431ba9b69..6c3098493 100644
--- a/tests/sqlparser_sqlite.rs
+++ b/tests/sqlparser_sqlite.rs
@@ -1140,14 +1140,6 @@ fn parse_between_low_bound_precedence() {
// 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 {
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org