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/18 10:17:35 UTC
(datafusion-sqlparser-rs) 01/01: Merge the `IN` expression variants into `Expr::In`
This is an automated email from the ASF dual-hosted git repository.
LucaCappelletti94 pushed a commit to branch in-expr-refactor
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git
commit 653d1668fade74d2302f395e1fd492ee76e89c7b
Author: LucaCappelletti94 <ca...@gmail.com>
AuthorDate: Sun Oct 18 12:17:23 2026 +0200
Merge the `IN` expression variants into `Expr::In`
---
src/ast/mod.rs | 84 +++++++++++++++++--------------------------
src/ast/spans.rs | 31 +++++++---------
src/parser/mod.rs | 48 +++++++++++--------------
tests/sqlparser_clickhouse.rs | 14 ++++----
tests/sqlparser_common.rs | 52 ++++++++++++++-------------
tests/sqlparser_sqlite.rs | 6 +++-
6 files changed, 105 insertions(+), 130 deletions(-)
diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index f585121ad..8a1b7e305 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -871,6 +871,30 @@ impl fmt::Display for CaseWhen {
}
}
+/// The right-hand side of an `IN` predicate, see [`Expr::In`].
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum InTarget {
+ /// `(val1, val2, ...)`
+ List(Vec<Expr>),
+ /// `(SELECT ...)`
+ Subquery(Box<Query>),
+ /// `UNNEST(array_expression)`, see
+ /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/operators#in_operators)
+ Unnest(Box<Expr>),
+}
+
+impl fmt::Display for InTarget {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ InTarget::List(list) => write!(f, "({})", display_comma_separated(list)),
+ InTarget::Subquery(subquery) => write!(f, "({subquery})"),
+ InTarget::Unnest(array_expr) => write!(f, "UNNEST({array_expr})"),
+ }
+ }
+}
+
/// An SQL expression of any type.
///
/// # Semantics / Type Checking
@@ -975,37 +999,17 @@ pub enum Expr {
/// `true` when `NOT` is present.
negated: bool,
},
- /// `[ GLOBAL ] [ NOT ] IN (val1, val2, ...)`
- InList {
+ /// `<expr> [ GLOBAL ] [ NOT ] IN <target>`
+ In {
/// Left-hand expression to test for membership.
expr: Box<Expr>,
- /// Literal list of expressions to check against.
- list: Vec<Expr>,
+ /// What `expr` is tested against.
+ target: InTarget,
/// `true` when the `NOT` modifier is present.
negated: bool,
/// `true` when the ClickHouse `GLOBAL` modifier is present.
global: bool,
},
- /// `[ GLOBAL ] [ NOT ] IN (SELECT ...)`
- InSubquery {
- /// Left-hand expression to test for membership.
- expr: Box<Expr>,
- /// The subquery providing the candidate values.
- subquery: Box<Query>,
- /// `true` when the `NOT` modifier is present.
- negated: bool,
- /// `true` when the ClickHouse `GLOBAL` modifier is present.
- global: bool,
- },
- /// `[ NOT ] IN UNNEST(array_expression)`
- InUnnest {
- /// Left-hand expression to test for membership.
- expr: Box<Expr>,
- /// Array expression being unnested.
- array_expr: Box<Expr>,
- /// `true` when the `NOT` modifier is present.
- negated: bool,
- },
/// `<expr> [ NOT ] BETWEEN <low> AND <high>`
Between {
/// Expression being compared.
@@ -1801,42 +1805,18 @@ impl fmt::Display for Expr {
}
Ok(())
}
- Expr::InList {
+ Expr::In {
expr,
- list,
- negated,
- global,
- } => write!(
- f,
- "{} {}{}IN ({})",
- expr,
- if *global { "GLOBAL " } else { "" },
- if *negated { "NOT " } else { "" },
- display_comma_separated(list)
- ),
- Expr::InSubquery {
- expr,
- subquery,
+ target,
negated,
global,
} => write!(
f,
- "{} {}{}IN ({})",
+ "{} {}{}IN {}",
expr,
if *global { "GLOBAL " } else { "" },
if *negated { "NOT " } else { "" },
- subquery
- ),
- Expr::InUnnest {
- expr,
- array_expr,
- negated,
- } => write!(
- f,
- "{} {}IN UNNEST({})",
- expr,
- if *negated { "NOT " } else { "" },
- array_expr
+ target
),
Expr::Between {
expr,
diff --git a/src/ast/spans.rs b/src/ast/spans.rs
index a17eba20c..8e7084fb1 100644
--- a/src/ast/spans.rs
+++ b/src/ast/spans.rs
@@ -36,9 +36,9 @@ use super::{
CreateTableOptions, Cte, Delete, DoUpdate, ExceptSelectItem, ExcludeConstraintElement,
ExcludeSelectItem, Expr, ExprWithAlias, Fetch, ForValues, FromTable, Function, FunctionArg,
FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList, FunctionArguments, GroupByExpr,
- HavingBound, IfStatement, IlikeSelectItem, IndexColumn, Insert, Interpolate, InterpolateExpr,
- Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView, LimitClause,
- MatchRecognizePattern, Measure, Merge, MergeAction, MergeClause, MergeInsertExpr,
+ HavingBound, IfStatement, IlikeSelectItem, InTarget, IndexColumn, Insert, Interpolate,
+ InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView,
+ LimitClause, MatchRecognizePattern, Measure, Merge, MergeAction, MergeClause, MergeInsertExpr,
MergeInsertKind, MergeUpdateExpr, MergeUpdateKind, NamedParenthesizedList,
NamedWindowDefinition, ObjectName, ObjectNamePart, Offset, OnConflict, OnConflictAction,
OnInsert, OpenStatement, OrderBy, OrderByExpr, OrderByKind, OutputClause, Parens, Partition,
@@ -1525,25 +1525,18 @@ impl Spanned for Expr {
} => expr.span(),
Expr::IsDistinctFrom(lhs, rhs) => lhs.span().union(&rhs.span()),
Expr::IsNotDistinctFrom(lhs, rhs) => lhs.span().union(&rhs.span()),
- Expr::InList {
+ Expr::In {
expr,
- list,
+ target,
negated: _,
global: _,
- } => union_spans(
- core::iter::once(expr.span()).chain(list.iter().map(|item| item.span())),
- ),
- Expr::InSubquery {
- expr,
- subquery,
- negated: _,
- global: _,
- } => expr.span().union(&subquery.span()),
- Expr::InUnnest {
- expr,
- array_expr,
- negated: _,
- } => expr.span().union(&array_expr.span()),
+ } => match target {
+ InTarget::List(list) => union_spans(
+ core::iter::once(expr.span()).chain(list.iter().map(|item| item.span())),
+ ),
+ InTarget::Subquery(subquery) => expr.span().union(&subquery.span()),
+ InTarget::Unnest(array_expr) => expr.span().union(&array_expr.span()),
+ },
Expr::Between {
expr,
negated: _,
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 39a3a754f..fd781d95b 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -4476,26 +4476,30 @@ impl<'a> Parser<'a> {
) -> Result<Expr, ParserError> {
// BigQuery allows `IN UNNEST(array_expression)`
// https://cloud.google.com/bigquery/docs/reference/standard-sql/operators#in_operators
- if !global && self.parse_keyword(Keyword::UNNEST) {
+ let target = if !global && self.parse_keyword(Keyword::UNNEST) {
self.expect_token(&Token::LParen)?;
let array_expr = self.parse_expr()?;
self.expect_token(&Token::RParen)?;
- return Ok(Expr::InUnnest {
- expr: Box::new(expr),
- array_expr: Box::new(array_expr),
- negated,
- });
- }
- if self.dialect.supports_in_unparenthesized_expr()
+ InTarget::Unnest(Box::new(array_expr))
+ } else if self.dialect.supports_in_unparenthesized_expr()
&& self.peek_token_ref().token != Token::LParen
{
- return Ok(Expr::InList {
- expr: Box::new(expr),
- list: vec![self.parse_subexpr(self.dialect.prec_value(Precedence::Eq))?],
- negated,
- global,
- });
- }
+ InTarget::List(vec![
+ self.parse_subexpr(self.dialect.prec_value(Precedence::Eq))?
+ ])
+ } else {
+ self.parse_in_parenthesized_target()?
+ };
+ Ok(Expr::In {
+ expr: Box::new(expr),
+ target,
+ negated,
+ global,
+ })
+ }
+
+ /// Parses the parenthesized subquery or value list of an `IN` predicate.
+ fn parse_in_parenthesized_target(&mut self) -> Result<InTarget, ParserError> {
self.expect_token(&Token::LParen)?;
// A leading query is the whole subquery only when `)` follows it,
// otherwise it is the first item of a list, as in `IN ((SELECT 1), 2)`.
@@ -4508,12 +4512,7 @@ impl<'a> Parser<'a> {
let mut missing_rparen = None;
if let Some(subquery) = self.maybe_parse(|p| p.parse_query())? {
if self.consume_token(&Token::RParen) {
- return Ok(Expr::InSubquery {
- expr: Box::new(expr),
- subquery,
- negated,
- global,
- });
+ return Ok(InTarget::Subquery(subquery));
}
missing_rparen = Some(self.index);
self.index = start;
@@ -4529,12 +4528,7 @@ impl<'a> Parser<'a> {
self.parse_comma_separated(Parser::parse_expr)
};
match list.and_then(|list| self.expect_token(&Token::RParen).map(|_| list)) {
- Ok(list) => Ok(Expr::InList {
- expr: Box::new(expr),
- list,
- negated,
- global,
- }),
+ Ok(list) => Ok(InTarget::List(list)),
Err(ParserError::RecursionLimitExceeded) => Err(ParserError::RecursionLimitExceeded),
Err(e) => match missing_rparen {
Some(index) if self.index <= index => {
diff --git a/tests/sqlparser_clickhouse.rs b/tests/sqlparser_clickhouse.rs
index 189300d87..5e5ab7e00 100644
--- a/tests/sqlparser_clickhouse.rs
+++ b/tests/sqlparser_clickhouse.rs
@@ -2120,12 +2120,12 @@ fn parse_global_in() {
let select = clickhouse().verified_only_select("SELECT * FROM t WHERE x GLOBAL IN (1, 2)");
assert_eq!(
select.selection.unwrap(),
- Expr::InList {
+ Expr::In {
expr: Box::new(Identifier(Ident::new("x"))),
- list: vec![
+ target: InTarget::List(vec![
Expr::Value(number("1").with_empty_span()),
Expr::Value(number("2").with_empty_span()),
- ],
+ ]),
negated: false,
global: true,
}
@@ -2135,9 +2135,9 @@ fn parse_global_in() {
.verified_only_select("SELECT * FROM t WHERE x GLOBAL NOT IN (SELECT y FROM t2)");
assert_eq!(
select.selection.unwrap(),
- Expr::InSubquery {
+ Expr::In {
expr: Box::new(Identifier(Ident::new("x"))),
- subquery: Box::new(clickhouse().verified_query("SELECT y FROM t2")),
+ target: InTarget::Subquery(Box::new(clickhouse().verified_query("SELECT y FROM t2"))),
negated: true,
global: true,
}
@@ -2222,7 +2222,7 @@ fn global_in_bare_table_binds_before_and() {
left,
op: BinaryOperator::And,
..
- } if matches!(*left, Expr::InList { global: true, .. })
+ } if matches!(*left, Expr::In { target: InTarget::List(_), global: true, .. })
));
}
@@ -2235,7 +2235,7 @@ fn global_in_bare_table_binds_before_equality() {
left,
op: BinaryOperator::Eq,
..
- } if matches!(*left, Expr::InList { global: true, .. })
+ } if matches!(*left, Expr::In { target: InTarget::List(_), global: true, .. })
));
}
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index 1959bcd34..30b4dafbe 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -2265,11 +2265,11 @@ fn parse_not_precedence() {
verified_expr(sql),
Expr::UnaryOp {
op: UnaryOperator::Not,
- expr: Box::new(Expr::InList {
+ expr: Box::new(Expr::In {
expr: Box::new(Expr::Identifier("a".into())),
- list: vec![Expr::Value(
+ target: InTarget::List(vec![Expr::Value(
(Value::SingleQuotedString("a".into())).with_empty_span()
- )],
+ )]),
negated: true,
global: false,
}),
@@ -2547,12 +2547,12 @@ fn parse_in_list() {
);
let select = verified_only_select(sql);
assert_eq!(
- Expr::InList {
+ Expr::In {
expr: Box::new(Expr::Identifier(Ident::new("segment"))),
- list: vec![
+ target: InTarget::List(vec![
Expr::Value((Value::SingleQuotedString("HIGH".to_string())).with_empty_span()),
Expr::Value((Value::SingleQuotedString("MED".to_string())).with_empty_span()),
- ],
+ ]),
negated,
global: false,
},
@@ -2568,9 +2568,9 @@ fn parse_in_subquery() {
let sql = "SELECT * FROM customers WHERE segment IN (SELECT segm FROM bar)";
let select = verified_only_select(sql);
assert_eq!(
- Expr::InSubquery {
+ Expr::In {
expr: Box::new(Expr::Identifier(Ident::new("segment"))),
- subquery: Box::new(verified_query("SELECT segm FROM bar")),
+ target: InTarget::Subquery(Box::new(verified_query("SELECT segm FROM bar"))),
negated: false,
global: false,
},
@@ -2583,11 +2583,11 @@ fn parse_in_union() {
let sql = "SELECT * FROM customers WHERE segment IN ((SELECT segm FROM bar) UNION (SELECT segm FROM bar2))";
let select = verified_only_select(sql);
assert_eq!(
- Expr::InSubquery {
+ Expr::In {
expr: Box::new(Expr::Identifier(Ident::new("segment"))),
- subquery: Box::new(verified_query(
+ target: InTarget::Subquery(Box::new(verified_query(
"(SELECT segm FROM bar) UNION (SELECT segm FROM bar2)"
- )),
+ ))),
negated: false,
global: false,
},
@@ -2604,10 +2604,11 @@ fn parse_in_unnest() {
);
let select = verified_only_select(sql);
assert_eq!(
- Expr::InUnnest {
+ Expr::In {
expr: Box::new(Expr::Identifier(Ident::new("segment"))),
- array_expr: Box::new(verified_expr("expr")),
+ target: InTarget::Unnest(Box::new(verified_expr("expr"))),
negated,
+ global: false,
},
select.selection.unwrap()
);
@@ -20442,9 +20443,9 @@ fn parse_in_list_leading_subquery() {
let select = verified_only_select("SELECT * FROM t WHERE x IN ((SELECT a FROM u), 1)");
assert_eq!(
- Expr::InList {
+ Expr::In {
expr: x(),
- list: vec![subquery(), Expr::value(number("1"))],
+ target: InTarget::List(vec![subquery(), Expr::value(number("1"))]),
negated: false,
global: false,
},
@@ -20453,9 +20454,12 @@ fn parse_in_list_leading_subquery() {
let select = verified_only_select("SELECT * FROM t WHERE x NOT IN (((SELECT a FROM u)), 1)");
assert_eq!(
- Expr::InList {
+ Expr::In {
expr: x(),
- list: vec![Expr::Nested(Box::new(subquery())), Expr::value(number("1"))],
+ target: InTarget::List(vec![
+ Expr::Nested(Box::new(subquery())),
+ Expr::value(number("1"))
+ ]),
negated: true,
global: false,
},
@@ -20464,16 +20468,16 @@ fn parse_in_list_leading_subquery() {
let select = verified_only_select("SELECT * FROM t WHERE x IN ((SELECT a FROM u) + 1, 2)");
assert_eq!(
- Expr::InList {
+ Expr::In {
expr: x(),
- list: vec![
+ target: InTarget::List(vec![
Expr::BinaryOp {
left: Box::new(subquery()),
op: BinaryOperator::Plus,
right: Box::new(Expr::value(number("1"))),
},
Expr::value(number("2"))
- ],
+ ]),
negated: false,
global: false,
},
@@ -20483,16 +20487,16 @@ fn parse_in_list_leading_subquery() {
let select = pg_and_generic()
.verified_only_select("SELECT * FROM t WHERE x IN ((SELECT a FROM u)[1], 2)");
assert_matches!(
- select.selection,
- Some(Expr::InList { ref list, .. })
+ select.selection.as_ref(),
+ Some(Expr::In { target: InTarget::List(list), .. })
if matches!(&list[0], Expr::CompoundFieldAccess { root, .. } if **root == subquery())
);
let select = verified_only_select("SELECT * FROM t WHERE x IN ((SELECT a FROM u))");
assert_eq!(
- Expr::InSubquery {
+ Expr::In {
expr: x(),
- subquery: Box::new(verified_query("(SELECT a FROM u)")),
+ target: InTarget::Subquery(Box::new(verified_query("(SELECT a FROM u)"))),
negated: false,
global: false,
},
diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs
index 2e60c75ef..3e3104947 100644
--- a/tests/sqlparser_sqlite.rs
+++ b/tests/sqlparser_sqlite.rs
@@ -524,7 +524,11 @@ fn parse_update_tuple_row_values() {
fn parse_where_in_empty_list() {
let sql = "SELECT * FROM t1 WHERE a IN ()";
let select = sqlite().verified_only_select(sql);
- if let Expr::InList { list, .. } = select.selection.as_ref().unwrap() {
+ if let Expr::In {
+ target: InTarget::List(list),
+ ..
+ } = select.selection.as_ref().unwrap()
+ {
assert_eq!(list.len(), 0);
} else {
unreachable!()
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org