Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(binder): add const case-when evaluation optimization during binding #14965

Merged
merged 15 commits into from
Feb 23, 2024
83 changes: 82 additions & 1 deletion src/frontend/src/binder/expr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,60 @@ impl Binder {
Ok(func_call.into())
}

/// The optimization check for the following case-when expression pattern
/// e.g., select case 1 when (...) then (...) else (...) end;
fn check_constant_case_when_optimization(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason I create another check function is that we can't check all the optimizable case-when patterns within only one pass, and they indeed have subtle but significant difference, which may not be generalized.

&mut self,
conditions: Vec<Expr>,
results_expr: Vec<ExprImpl>,
operand: Option<Box<Expr>>,
fallback: Option<ExprImpl>,
constant_case_when_eval_inputs: &mut Vec<ExprImpl>,
) -> bool {
// The operand value to be compared later
let operand_value;

if let Some(operand) = operand {
let Ok(operand) = self.bind_expr_inner(*operand) else {
return false;
};
if !operand.is_const() {
return false;
}
operand_value = operand;
} else {
return false;
}

for (condition, result) in zip_eq_fast(conditions, results_expr) {
if let Expr::Value(_) = condition.clone() {
let Ok(res) = self.bind_expr_inner(condition.clone()) else {
return false;
};
// Found a match
if res == operand_value {
constant_case_when_eval_inputs.push(result);
return true;
}
} else {
return false;
}
}

// Otherwise this will eventually go through fallback arm
debug_assert!(
constant_case_when_eval_inputs.is_empty(),
"expect `inputs` to be empty"
);

let Some(fallback) = fallback else {
return false;
};

constant_case_when_eval_inputs.push(fallback);
true
}

/// The helper function to check if the current case-when
/// expression in `bind_case` could be optimized
/// into `ConstantLookupExpression`
Expand All @@ -493,6 +547,12 @@ impl Binder {
let Ok(operand) = self.bind_expr_inner(*operand) else {
return false;
};
// This optimization should be done in subsequent optimization phase
// if the operand is const
// e.g., select case 1 when 1 then 114514 else 1919810 end;
if operand.is_const() {
return false;
}
constant_lookup_inputs.push(operand);
} else {
return false;
Expand All @@ -506,7 +566,7 @@ impl Binder {
constant_lookup_inputs.push(input);
} else {
// If at least one condition is not in the simple form / not constant,
// we can NOT do the subsequent optimization then
// we can NOT do the subsequent optimization pass
return false;
}

Expand Down Expand Up @@ -538,6 +598,27 @@ impl Binder {
.transpose()?;

let mut constant_lookup_inputs = Vec::new();
let mut constant_case_when_eval_inputs = Vec::new();

let constant_case_when_flag = self.check_constant_case_when_optimization(
conditions.clone(),
results_expr.clone(),
operand.clone(),
else_result_expr.clone(),
&mut constant_case_when_eval_inputs,
);

if constant_case_when_flag {
// Sanity check
if constant_case_when_eval_inputs.len() != 1 {
return Err(ErrorCode::BindError(
"expect `constant_case_when_eval_inputs` only contains a single bound expression".to_string()
)
.into());
}
// Directly return the first element of the vector
return Ok(constant_case_when_eval_inputs[0].take());
}

// See if the case-when expression can be optimized
let optimize_flag = self.check_bind_case_optimization(
Expand Down
Loading