Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion datafusion/core/src/dataframe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -827,7 +827,6 @@ impl TableProvider for DataFrame {

#[cfg(test)]
mod tests {
use arrow::array::Int32Array;
use std::vec;

use super::*;
Expand Down
12 changes: 4 additions & 8 deletions datafusion/core/src/physical_plan/planner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,19 +111,15 @@ fn create_physical_name(e: &Expr, is_first_expr: bool) -> Result<String> {
let right = create_physical_name(right, false)?;
Ok(format!("{} {} {}", left, op, right))
}
Expr::Case {
expr,
when_then_expr,
else_expr,
} => {
Expr::Case(case) => {
let mut name = "CASE ".to_string();
if let Some(e) = expr {
if let Some(e) = &case.expr {
let _ = write!(name, "{:?} ", e);
}
for (w, t) in when_then_expr {
for (w, t) in &case.when_then_expr {
let _ = write!(name, "WHEN {:?} THEN {:?} ", w, t);
}
if let Some(e) = else_expr {
if let Some(e) = &case.else_expr {
let _ = write!(name, "ELSE {:?} ", e);
}
name += "END";
Expand Down
12 changes: 6 additions & 6 deletions datafusion/expr/src/conditional_expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
// specific language governing permissions and limitations
// under the License.

use crate::expr::Case;
///! Conditional expressions
use crate::{expr_schema::ExprSchemable, Expr};
use arrow::datatypes::DataType;
Expand Down Expand Up @@ -108,16 +109,15 @@ impl CaseBuilder {
}
}

Ok(Expr::Case {
expr: self.expr.clone(),
when_then_expr: self
.when_expr
Ok(Expr::Case(Case::new(
self.expr.clone(),
self.when_expr
.iter()
.zip(self.then_expr.iter())
.map(|(w, t)| (Box::new(w.clone()), Box::new(t.clone())))
.collect(),
else_expr: self.else_expr.clone(),
})
self.else_expr.clone(),
)))
}
}

Expand Down
60 changes: 35 additions & 25 deletions datafusion/expr/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,14 +176,7 @@ pub enum Expr {
/// [WHEN ...]
/// [ELSE result]
/// END
Case {
/// Optional base expression that can be compared to literal values in the "when" expressions
expr: Option<Box<Expr>>,
/// One or more when/then expressions
when_then_expr: Vec<(Box<Expr>, Box<Expr>)>,
/// Optional "else" expression
else_expr: Option<Box<Expr>>,
},
Case(Case),
/// Casts the expression to a given type and will return a runtime error if the expression cannot be cast.
/// This expression is guaranteed to have a fixed type.
Cast {
Expand Down Expand Up @@ -292,6 +285,32 @@ pub enum Expr {
GroupingSet(GroupingSet),
}

/// CASE expression
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Case {
/// Optional base expression that can be compared to literal values in the "when" expressions
pub expr: Option<Box<Expr>>,
/// One or more when/then expressions
pub when_then_expr: Vec<(Box<Expr>, Box<Expr>)>,
/// Optional "else" expression
pub else_expr: Option<Box<Expr>>,
}

impl Case {
/// Create a new Case expression
pub fn new(
expr: Option<Box<Expr>>,
when_then_expr: Vec<(Box<Expr>, Box<Expr>)>,
else_expr: Option<Box<Expr>>,
) -> Self {
Self {
expr,
when_then_expr,
else_expr,
}
}
}

/// Grouping sets
/// See https://www.postgresql.org/docs/current/queries-table-expressions.html#QUERIES-GROUPING-SETS
/// for Postgres definition.
Expand Down Expand Up @@ -601,20 +620,15 @@ impl fmt::Debug for Expr {
Expr::Column(c) => write!(f, "{}", c),
Expr::ScalarVariable(_, var_names) => write!(f, "{}", var_names.join(".")),
Expr::Literal(v) => write!(f, "{:?}", v),
Expr::Case {
expr,
when_then_expr,
else_expr,
..
} => {
Expr::Case(case) => {
write!(f, "CASE ")?;
if let Some(e) = expr {
if let Some(e) = &case.expr {
write!(f, "{:?} ", e)?;
}
for (w, t) in when_then_expr {
for (w, t) in &case.when_then_expr {
write!(f, "WHEN {:?} THEN {:?} ", w, t)?;
}
if let Some(e) = else_expr {
if let Some(e) = &case.else_expr {
write!(f, "ELSE {:?} ", e)?;
}
write!(f, "END")
Expand Down Expand Up @@ -957,22 +971,18 @@ fn create_name(e: &Expr) -> Result<String> {
);
Ok(s)
}
Expr::Case {
expr,
when_then_expr,
else_expr,
} => {
Expr::Case(case) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you wanted to avoid &case I think you could do like

Suggested change
Expr::Case(case) => {
Expr::Case(Case {
expr,
when_then_expr,
else_expr,
}) => {

Maybe the & is nicer syntax 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I debated between the two styles too. I don't have a strong preference either way

let mut name = "CASE ".to_string();
if let Some(e) = expr {
if let Some(e) = &case.expr {
let e = create_name(e)?;
let _ = write!(name, "{} ", e);
}
for (w, t) in when_then_expr {
for (w, t) in &case.when_then_expr {
let when = create_name(w)?;
let then = create_name(t)?;
let _ = write!(name, "WHEN {} THEN {} ", when, then);
}
if let Some(e) = else_expr {
if let Some(e) = &case.else_expr {
let e = create_name(e)?;
let _ = write!(name, "ELSE {} ", e);
}
Expand Down
21 changes: 7 additions & 14 deletions datafusion/expr/src/expr_rewriter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

//! Expression rewriter

use crate::expr::GroupingSet;
use crate::expr::{Case, GroupingSet};
use crate::logical_plan::{Aggregate, Projection};
use crate::utils::{from_plan, grouping_set_to_exprlist};
use crate::{Expr, ExprSchemable, LogicalPlan};
Expand Down Expand Up @@ -184,13 +184,10 @@ impl ExprRewritable for Expr {
high: rewrite_boxed(high, rewriter)?,
negated,
},
Expr::Case {
expr,
when_then_expr,
else_expr,
} => {
let expr = rewrite_option_box(expr, rewriter)?;
let when_then_expr = when_then_expr
Expr::Case(case) => {
let expr = rewrite_option_box(case.expr, rewriter)?;
let when_then_expr = case
.when_then_expr
.into_iter()
.map(|(when, then)| {
Ok((
Expand All @@ -200,13 +197,9 @@ impl ExprRewritable for Expr {
})
.collect::<Result<Vec<_>>>()?;

let else_expr = rewrite_option_box(else_expr, rewriter)?;
let else_expr = rewrite_option_box(case.else_expr, rewriter)?;

Expr::Case {
expr,
when_then_expr,
else_expr,
}
Expr::Case(Case::new(expr, when_then_expr, else_expr))
}
Expr::Cast { expr, data_type } => Expr::Cast {
expr: rewrite_boxed(expr, rewriter)?,
Expand Down
13 changes: 5 additions & 8 deletions datafusion/expr/src/expr_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ impl ExprSchemable for Expr {
Expr::Column(c) => Ok(schema.data_type(c)?.clone()),
Expr::ScalarVariable(ty, _) => Ok(ty.clone()),
Expr::Literal(l) => Ok(l.get_datatype()),
Expr::Case { when_then_expr, .. } => when_then_expr[0].1.get_type(schema),
Expr::Case(case) => case.when_then_expr[0].1.get_type(schema),
Expr::Cast { data_type, .. } | Expr::TryCast { data_type, .. } => {
Ok(data_type.clone())
}
Expand Down Expand Up @@ -164,19 +164,16 @@ impl ExprSchemable for Expr {
| Expr::InList { expr, .. } => expr.nullable(input_schema),
Expr::Column(c) => input_schema.nullable(c),
Expr::Literal(value) => Ok(value.is_null()),
Expr::Case {
when_then_expr,
else_expr,
..
} => {
Expr::Case(case) => {
// this expression is nullable if any of the input expressions are nullable
let then_nullable = when_then_expr
let then_nullable = case
.when_then_expr
.iter()
.map(|(_, t)| t.nullable(input_schema))
.collect::<Result<Vec<_>>>()?;
if then_nullable.contains(&true) {
Ok(true)
} else if let Some(e) = else_expr {
} else if let Some(e) = &case.else_expr {
e.nullable(input_schema)
} else {
// CASE produces NULL if there is no `else` expr
Expand Down
12 changes: 4 additions & 8 deletions datafusion/expr/src/expr_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,24 +153,20 @@ impl ExprVisitable for Expr {
let visitor = low.accept(visitor)?;
high.accept(visitor)
}
Expr::Case {
expr,
when_then_expr,
else_expr,
} => {
let visitor = if let Some(expr) = expr.as_ref() {
Expr::Case(case) => {
let visitor = if let Some(expr) = case.expr.as_ref() {
expr.accept(visitor)
} else {
Ok(visitor)
}?;
let visitor = when_then_expr.iter().try_fold(
let visitor = case.when_then_expr.iter().try_fold(
visitor,
|visitor, (when, then)| {
let visitor = when.accept(visitor)?;
then.accept(visitor)
},
)?;
if let Some(else_expr) = else_expr.as_ref() {
if let Some(else_expr) = case.else_expr.as_ref() {
else_expr.accept(visitor)
} else {
Ok(visitor)
Expand Down
Loading