Skip to content

Commit c51d864

Browse files
mbasmanovafacebook-github-bot
authored andcommitted
feat: Allow propagating constant folding exceptions (facebookincubator#13680)
Summary: Pull Request resolved: facebookincubator#13680 Enhance velox::exec::tryEvaluateConstantExpression to allow for propagating exceptions occurred during evaluation of a constant expression. Specifically, constant folding 5 / 0 fails with "division by zero". Before this change, the error would be swallowed. After this change the error would be propagated unless optional parameter suppressEvaluationFailures is set to true. Remove velox::exec::evaluateConstantExpression as it is no longer used / needed. Reviewed By: bikramSingh91 Differential Revision: D76152526 fbshipit-source-id: 190abf1c75f8034e58c70fa665abaaca4ba5a189
1 parent 0436d0c commit c51d864

File tree

3 files changed

+40
-44
lines changed

3 files changed

+40
-44
lines changed

velox/expression/Expr.cpp

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1683,11 +1683,7 @@ void Expr::appendInputsSql(
16831683
}
16841684

16851685
bool Expr::isConstant() const {
1686-
if (!isDeterministic()) {
1687-
return false;
1688-
}
1689-
1690-
return distinctFields_.empty();
1686+
return isDeterministic() && distinctFields_.empty();
16911687
}
16921688

16931689
namespace {
@@ -2041,25 +2037,25 @@ core::ExecCtx* SimpleExpressionEvaluator::ensureExecCtx() {
20412037
return execCtx_.get();
20422038
}
20432039

2044-
VectorPtr evaluateConstantExpression(
2045-
const core::TypedExprPtr& expr,
2046-
memory::MemoryPool* pool) {
2047-
auto result = tryEvaluateConstantExpression(expr, pool);
2048-
VELOX_USER_CHECK_NOT_NULL(
2049-
result, "Expression is not constant-foldable: {}", expr->toString());
2050-
return result;
2051-
}
2052-
20532040
VectorPtr tryEvaluateConstantExpression(
20542041
const core::TypedExprPtr& expr,
2055-
memory::MemoryPool* pool) {
2056-
auto data = BaseVector::create<RowVector>(ROW({}), 1, pool);
2057-
2042+
memory::MemoryPool* pool,
2043+
bool suppressEvaluationFailures) {
20582044
auto queryCtx = velox::core::QueryCtx::create();
20592045
velox::core::ExecCtx execCtx{pool, queryCtx.get()};
20602046
velox::exec::ExprSet exprSet({expr}, &execCtx);
20612047

2062-
if (exprSet.expr(0)->is<ConstantExpr>()) {
2048+
// The construction of ExprSet involves compiling and constant folding the
2049+
// expression. If constant folding succeeded, then we get a ConstantExpr.
2050+
// Constant folding may fail because expression is not constant-foldable or if
2051+
// an error happened during evaluation (5 / 0 fails with "division by zero").
2052+
// If constant folding didn't succeed, but suppressEvaluationFailures is
2053+
// false, we need to re-evaluate the expression to propagate the failure.
2054+
const bool doEvaluate = exprSet.expr(0)->is<ConstantExpr>() ||
2055+
(!suppressEvaluationFailures && exprSet.expr(0)->isConstant());
2056+
2057+
if (doEvaluate) {
2058+
auto data = BaseVector::create<RowVector>(ROW({}), 1, pool);
20632059
velox::exec::EvalCtx evalCtx(&execCtx, &exprSet, data.get());
20642060
velox::SelectivityVector singleRow(1);
20652061
std::vector<velox::VectorPtr> results(1);

velox/expression/Expr.h

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -805,19 +805,18 @@ std::unique_ptr<ExprSet> makeExprSetFromFlag(
805805
std::vector<core::TypedExprPtr>&& source,
806806
core::ExecCtx* execCtx);
807807

808-
/// Evaluates a deterministic expression that doesn't depend on any inputs and
809-
/// returns the result as single-row vector. Throws if expression is
810-
/// non-deterministic or has dependencies.
811-
VectorPtr evaluateConstantExpression(
812-
const core::TypedExprPtr& expr,
813-
memory::MemoryPool* pool);
814-
815808
/// Evaluates a deterministic expression that doesn't depend on any inputs and
816809
/// returns the result as single-row vector. Returns nullptr if the expression
817810
/// is non-deterministic or has dependencies.
811+
///
812+
/// By default, propagates failures that occur during evaluation of the
813+
/// expression. For example, evaluating 5 / 0 throws "division by zero". If
814+
/// 'suppressEvaluationFailures' is true, these failures are swallowed and the
815+
/// caller receives a nullptr result.
818816
VectorPtr tryEvaluateConstantExpression(
819817
const core::TypedExprPtr& expr,
820-
memory::MemoryPool* pool);
818+
memory::MemoryPool* pool,
819+
bool suppressEvaluationFailures = false);
821820

822821
/// Returns a string representation of the expression trees annotated with
823822
/// runtime statistics. Expected to be called after calling ExprSet::eval one or

velox/expression/tests/ExprTest.cpp

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4987,7 +4987,13 @@ TEST_F(ExprTest, disabledeferredLazyLoading) {
49874987
TEST_F(ExprTest, evaluateConstantExpression) {
49884988
auto eval = [&](const std::string& sql) {
49894989
auto expr = parseExpression(sql, ROW({"a"}, {BIGINT()}));
4990-
return exec::evaluateConstantExpression(expr, pool());
4990+
return exec::tryEvaluateConstantExpression(expr, pool());
4991+
};
4992+
4993+
auto evalNoThrow = [&](const std::string& sql) {
4994+
auto expr = parseExpression(sql, ROW({"a"}, {BIGINT()}));
4995+
return exec::tryEvaluateConstantExpression(
4996+
expr, pool(), true /* supressEvaluationFailures */);
49914997
};
49924998

49934999
assertEqualVectors(eval("1 + 2"), makeConstant<int64_t>(3, 1));
@@ -5009,28 +5015,23 @@ TEST_F(ExprTest, evaluateConstantExpression) {
50095015
"try(coalesce(array_min_by(array[1, 2, 3], x -> x / 0), 0::INTEGER))"),
50105016
makeNullConstant(TypeKind::INTEGER, 1));
50115017

5012-
auto tryEval = [&](const std::string& sql) {
5013-
auto expr = parseExpression(sql, ROW({"a"}, {BIGINT()}));
5014-
return exec::tryEvaluateConstantExpression(expr, pool());
5015-
};
5018+
EXPECT_TRUE(eval("a + 1") == nullptr);
50165019

5017-
VELOX_ASSERT_THROW(eval("a + 1"), "Expression is not constant-foldable");
5018-
ASSERT_TRUE(tryEval("a + 1") == nullptr);
5020+
EXPECT_TRUE(eval("rand() + 1.0") == nullptr);
50195021

5020-
VELOX_ASSERT_THROW(
5021-
eval("rand() + 1.0"), "Expression is not constant-foldable");
5022-
ASSERT_TRUE(tryEval("rand() + 1.0") == nullptr);
5022+
EXPECT_TRUE(eval("transform(array[1, 2, 3], x -> (x * 2) + a)") == nullptr);
50235023

5024-
VELOX_ASSERT_THROW(
5025-
eval("transform(array[1, 2, 3], x -> (x * 2) + a)"),
5026-
"Expression is not constant-foldable");
5027-
ASSERT_TRUE(
5028-
tryEval("transform(array[1, 2, 3], x -> (x * 2) + a)") == nullptr);
5024+
EXPECT_TRUE(eval("transform(array[1, 2, 3], x -> x + rand())") == nullptr);
5025+
5026+
VELOX_ASSERT_THROW(eval("5 / 0"), "division by zero");
5027+
EXPECT_TRUE(evalNoThrow("5 / 0") == nullptr);
5028+
5029+
VELOX_ASSERT_THROW(eval("1 + 5 / 0"), "division by zero");
5030+
EXPECT_TRUE(evalNoThrow("1 + 5 / 0") == nullptr);
50295031

50305032
VELOX_ASSERT_THROW(
5031-
eval("transform(array[1, 2, 3], x -> x + rand())"),
5032-
"Expression is not constant-foldable");
5033-
ASSERT_TRUE(tryEval("transform(array[1, 2, 3], x -> x + rand())") == nullptr);
5033+
eval("transform(array[1, 2, 3], x -> x / 0)"), "division by zero");
5034+
EXPECT_TRUE(evalNoThrow("transform(array[1, 2, 3], x -> x / 0)") == nullptr);
50345035
}
50355036

50365037
TEST_F(ExprTest, isDeterministic) {

0 commit comments

Comments
 (0)