@@ -1192,20 +1192,36 @@ void Expr::evalWithMemo(
1192
1192
VectorPtr base;
1193
1193
distinctFields_[0 ]->evalSpecialForm (rows, context, base);
1194
1194
1195
+ // evalWithNulls may throw an exception. If this happens during constant
1196
+ // folding, the exception is suppressed and the Expr object may be reused.
1197
+ // Hence, it is important to update state in way that ensure "valid" state in
1198
+ // case of exceptions.
1199
+ //
1200
+ // Also, note that the same expression running on same data may pass or may
1201
+ // fail depending on whether it runs under TRY or not.
1202
+ //
1203
+ // An example expression that triggers these edge cases:
1204
+ //
1205
+ // try(coalesce(array_min_by(array[1, 2, 3], x -> x / 0), 0::INTEGER))
1206
+
1195
1207
if (base.get () != baseOfDictionaryRawPtr_ ||
1196
1208
baseOfDictionaryWeakPtr_.expired ()) {
1197
1209
baseOfDictionaryRepeats_ = 0 ;
1198
- baseOfDictionaryWeakPtr_ = base ;
1199
- baseOfDictionaryRawPtr_ = base. get () ;
1210
+ baseOfDictionaryWeakPtr_. reset () ;
1211
+ baseOfDictionaryRawPtr_ = nullptr ;
1200
1212
context.releaseVector (baseOfDictionary_);
1201
1213
context.releaseVector (dictionaryCache_);
1214
+
1202
1215
evalWithNulls (rows, context, result);
1216
+ baseOfDictionaryWeakPtr_ = base;
1217
+ baseOfDictionaryRawPtr_ = base.get ();
1203
1218
return ;
1204
1219
}
1205
- ++baseOfDictionaryRepeats_;
1206
1220
1207
- if (baseOfDictionaryRepeats_ == 1 ) {
1221
+ if (baseOfDictionaryRepeats_ == 0 ) {
1208
1222
evalWithNulls (rows, context, result);
1223
+
1224
+ ++baseOfDictionaryRepeats_;
1209
1225
baseOfDictionary_ = base;
1210
1226
dictionaryCache_ = result;
1211
1227
if (!cachedDictionaryIndices_) {
@@ -1217,6 +1233,8 @@ void Expr::evalWithMemo(
1217
1233
return ;
1218
1234
}
1219
1235
1236
+ ++baseOfDictionaryRepeats_;
1237
+
1220
1238
if (cachedDictionaryIndices_) {
1221
1239
LocalSelectivityVector cachedHolder (context, rows);
1222
1240
auto cached = cachedHolder.get ();
@@ -1242,31 +1260,34 @@ void Expr::evalWithMemo(
1242
1260
1243
1261
evalWithNulls (*uncached, context, result);
1244
1262
context.deselectErrors (*uncached);
1245
- context.exprSet ()->addToMemo (this );
1246
- auto newCacheSize = uncached->end ();
1247
-
1248
- // dictionaryCache_ is valid only for cachedDictionaryIndices_. Hence, a
1249
- // safe call to BaseVector::ensureWritable must include all the rows not
1250
- // covered by cachedDictionaryIndices_. If BaseVector::ensureWritable is
1251
- // called only for a subset of rows not covered by
1252
- // cachedDictionaryIndices_, it will attempt to copy rows that are not
1253
- // valid leading to a crash.
1254
- LocalSelectivityVector allUncached (context, dictionaryCache_->size ());
1255
- allUncached.get ()->setAll ();
1256
- allUncached.get ()->deselect (*cachedDictionaryIndices_);
1257
- context.ensureWritable (*allUncached.get (), type (), dictionaryCache_);
1258
-
1259
- if (cachedDictionaryIndices_->size () < newCacheSize) {
1260
- cachedDictionaryIndices_->resize (newCacheSize, false );
1261
- }
1262
1263
1263
- cachedDictionaryIndices_->select (*uncached);
1264
+ if (uncached->hasSelections ()) {
1265
+ context.exprSet ()->addToMemo (this );
1266
+ auto newCacheSize = uncached->end ();
1267
+
1268
+ // dictionaryCache_ is valid only for cachedDictionaryIndices_. Hence, a
1269
+ // safe call to BaseVector::ensureWritable must include all the rows not
1270
+ // covered by cachedDictionaryIndices_. If BaseVector::ensureWritable is
1271
+ // called only for a subset of rows not covered by
1272
+ // cachedDictionaryIndices_, it will attempt to copy rows that are not
1273
+ // valid leading to a crash.
1274
+ LocalSelectivityVector allUncached (context, dictionaryCache_->size ());
1275
+ allUncached.get ()->setAll ();
1276
+ allUncached.get ()->deselect (*cachedDictionaryIndices_);
1277
+ context.ensureWritable (*allUncached.get (), type (), dictionaryCache_);
1278
+
1279
+ if (cachedDictionaryIndices_->size () < newCacheSize) {
1280
+ cachedDictionaryIndices_->resize (newCacheSize, false );
1281
+ }
1264
1282
1265
- // Resize the dictionaryCache_ to accommodate all the necessary rows.
1266
- if (dictionaryCache_->size () < uncached->end ()) {
1267
- dictionaryCache_->resize (uncached->end ());
1283
+ cachedDictionaryIndices_->select (*uncached);
1284
+
1285
+ // Resize the dictionaryCache_ to accommodate all the necessary rows.
1286
+ if (dictionaryCache_->size () < uncached->end ()) {
1287
+ dictionaryCache_->resize (uncached->end ());
1288
+ }
1289
+ dictionaryCache_->copy (result.get (), *uncached, nullptr );
1268
1290
}
1269
- dictionaryCache_->copy (result.get (), *uncached, nullptr );
1270
1291
}
1271
1292
context.releaseVector (base);
1272
1293
}
@@ -1660,12 +1681,8 @@ bool Expr::isConstant() const {
1660
1681
if (!isDeterministic ()) {
1661
1682
return false ;
1662
1683
}
1663
- for (auto & input : inputs_) {
1664
- if (!input->is <ConstantExpr>()) {
1665
- return false ;
1666
- }
1667
- }
1668
- return true ;
1684
+
1685
+ return distinctFields_.empty ();
1669
1686
}
1670
1687
1671
1688
namespace {
@@ -2022,17 +2039,30 @@ core::ExecCtx* SimpleExpressionEvaluator::ensureExecCtx() {
2022
2039
VectorPtr evaluateConstantExpression (
2023
2040
const core::TypedExprPtr& expr,
2024
2041
memory::MemoryPool* pool) {
2042
+ auto result = tryEvaluateConstantExpression (expr, pool);
2043
+ VELOX_USER_CHECK_NOT_NULL (
2044
+ result, " Expression is not constant-foldable: {}" , expr->toString ());
2045
+ return result;
2046
+ }
2047
+
2048
+ VectorPtr tryEvaluateConstantExpression (
2049
+ const core::TypedExprPtr& expr,
2050
+ memory::MemoryPool* pool) {
2025
2051
auto data = BaseVector::create<RowVector>(ROW ({}), 1 , pool);
2026
2052
2027
2053
auto queryCtx = velox::core::QueryCtx::create ();
2028
2054
velox::core::ExecCtx execCtx{pool, queryCtx.get ()};
2029
2055
velox::exec::ExprSet exprSet ({expr}, &execCtx);
2030
- velox::exec::EvalCtx evalCtx (&execCtx, &exprSet, data.get ());
2031
2056
2032
- velox::SelectivityVector singleRow (1 );
2033
- std::vector<velox::VectorPtr> results (1 );
2034
- exprSet.eval (singleRow, evalCtx, results);
2035
- return results.at (0 );
2057
+ if (exprSet.expr (0 )->is <ConstantExpr>()) {
2058
+ velox::exec::EvalCtx evalCtx (&execCtx, &exprSet, data.get ());
2059
+ velox::SelectivityVector singleRow (1 );
2060
+ std::vector<velox::VectorPtr> results (1 );
2061
+ exprSet.eval (singleRow, evalCtx, results);
2062
+ return results.at (0 );
2063
+ }
2064
+
2065
+ return nullptr ;
2036
2066
}
2037
2067
2038
2068
} // namespace facebook::velox::exec
0 commit comments