Skip to content

Commit fd578aa

Browse files
committed
mtrlz/sql: support CASE expressions
1 parent fcd8c5d commit fd578aa

File tree

3 files changed

+82
-0
lines changed

3 files changed

+82
-0
lines changed

src/materialize/dataflow/render.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,5 +466,10 @@ fn eval_expr(expr: &Expr, datum: &Datum) -> Datum {
466466
let datum2 = eval_expr(expr2, datum);
467467
(func.func())(datum1, datum2)
468468
}
469+
Expr::If { cond, then, els } => match eval_expr(cond, datum) {
470+
Datum::True => eval_expr(then, datum),
471+
Datum::False | Datum::Null => eval_expr(els, datum),
472+
d => panic!("IF condition evaluated to non-boolean datum {:?}", d),
473+
},
469474
}
470475
}

src/materialize/dataflow/types.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,11 @@ pub enum Expr {
226226
expr1: Box<Expr>,
227227
expr2: Box<Expr>,
228228
},
229+
If {
230+
cond: Box<Expr>,
231+
then: Box<Expr>,
232+
els: Box<Expr>,
233+
},
229234
// /// A function call that takes an arbitrary number of arguments.
230235
// CallMany {
231236
// fn: fn(Vec<Datum>) -> Datum,

src/materialize/sql/mod.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,6 +1059,12 @@ impl Planner {
10591059
high,
10601060
negated,
10611061
} => self.plan_between(ctx, expr, low, high, *negated, plan),
1062+
ASTNode::SQLCase {
1063+
operand,
1064+
conditions,
1065+
results,
1066+
else_result,
1067+
} => self.plan_case(ctx, operand, conditions, results, else_result, plan),
10621068
ASTNode::SQLNested(expr) => self.plan_expr(ctx, expr, plan),
10631069
ASTNode::SQLFunction {
10641070
name,
@@ -1311,6 +1317,72 @@ impl Planner {
13111317
self.plan_expr(ctx, &both, plan)
13121318
}
13131319

1320+
fn plan_case<'a>(
1321+
&self,
1322+
ctx: &ExprContext,
1323+
operand: &'a Option<Box<ASTNode>>,
1324+
conditions: &'a [ASTNode],
1325+
results: &'a [ASTNode],
1326+
else_result: &'a Option<Box<ASTNode>>,
1327+
plan: &SQLPlan,
1328+
) -> Result<(Expr, Type), failure::Error> {
1329+
let mut case_exprs = Vec::new();
1330+
let mut result_type: Option<Type> = None;
1331+
for (c, r) in conditions.iter().zip(results) {
1332+
let c = match operand {
1333+
Some(operand) => ASTNode::SQLBinaryExpr {
1334+
left: operand.clone(),
1335+
op: SQLOperator::Eq,
1336+
right: Box::new(c.clone()),
1337+
},
1338+
None => c.clone(),
1339+
};
1340+
let (cexpr, ctype) = self.plan_expr(ctx, &c, plan)?;
1341+
if ctype.ftype != FType::Bool {
1342+
bail!("CASE expression has non-boolean type {:?}", ctype.ftype);
1343+
}
1344+
let (rexpr, rtype) = self.plan_expr(ctx, r, plan)?;
1345+
match &result_type {
1346+
Some(result_type) => {
1347+
if result_type.ftype != rtype.ftype {
1348+
bail!(
1349+
"CASE expression does not have uniform result type: {:?} vs {:?}",
1350+
result_type.ftype,
1351+
rtype.ftype
1352+
);
1353+
}
1354+
}
1355+
None => result_type = Some(rtype),
1356+
}
1357+
case_exprs.push((cexpr, rexpr));
1358+
}
1359+
// conditions and results must be non-empty, which implies that
1360+
// result_type must be non-None.
1361+
let result_type = result_type.unwrap();
1362+
let mut expr = match else_result {
1363+
Some(else_result) => {
1364+
let (expr, typ) = self.plan_expr(ctx, else_result, plan)?;
1365+
if typ.ftype != result_type.ftype {
1366+
bail!(
1367+
"CASE expression does not have uniform result type: {:?} vs {:?}",
1368+
result_type.ftype,
1369+
typ.ftype
1370+
);
1371+
}
1372+
expr
1373+
}
1374+
None => Expr::Literal(Datum::Null),
1375+
};
1376+
for (cexpr, rexpr) in case_exprs.into_iter().rev() {
1377+
expr = Expr::If {
1378+
cond: Box::new(cexpr),
1379+
then: Box::new(rexpr),
1380+
els: Box::new(expr),
1381+
}
1382+
}
1383+
Ok((expr, result_type))
1384+
}
1385+
13141386
fn plan_literal<'a>(&self, l: &'a Value) -> Result<(Expr, Type), failure::Error> {
13151387
let (datum, ftype) = match l {
13161388
Value::Long(i) => (Datum::Int64(*i), FType::Int64),

0 commit comments

Comments
 (0)