Skip to content

Commit 21172ca

Browse files
authored
TopK: avoid mapping errors when limit is constant (#33499)
We used to always render an operator to map the error collection, even if we could determine that the limit expression could never error. This change adjusts rendering to only render error handling if the expression is not a literal, or evaluates to an error itself. Signed-off-by: Moritz Hoffmann <[email protected]>
1 parent a334d55 commit 21172ca

File tree

2 files changed

+51
-24
lines changed

2 files changed

+51
-24
lines changed

src/compute-types/src/plan/top_k.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,32 @@ impl TopKPlan {
126126
}
127127
}
128128
}
129+
130+
/// Return the limit of the TopK, if any.
131+
pub fn limit(&self) -> Option<&mz_expr::MirScalarExpr> {
132+
match self {
133+
TopKPlan::MonotonicTop1(MonotonicTop1Plan {
134+
group_key: _,
135+
order_key: _,
136+
must_consolidate: _,
137+
}) => None,
138+
TopKPlan::MonotonicTopK(MonotonicTopKPlan {
139+
limit,
140+
arity: _,
141+
must_consolidate: _,
142+
group_key: _,
143+
order_key: _,
144+
}) => limit.as_ref(),
145+
TopKPlan::Basic(BasicTopKPlan {
146+
limit,
147+
group_key: _,
148+
order_key: _,
149+
arity: _,
150+
offset: _,
151+
buckets: _,
152+
}) => limit.as_ref(),
153+
}
154+
}
129155
}
130156

131157
impl RustType<ProtoTopKPlan> for TopKPlan {

src/compute/src/render/top_k.rs

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -71,31 +71,32 @@ where
7171
// integrated with: 1. The intra-timestamp thinning step in monotonic top-k, e.g., by
7272
// adding an error output there; 2. The validating reduction on basic top-k
7373
// (database-issues#7108).
74-
let limit_expr = match &top_k_plan {
75-
TopKPlan::MonotonicTop1(MonotonicTop1Plan { .. }) => None,
76-
TopKPlan::MonotonicTopK(MonotonicTopKPlan { limit, .. }) => limit.as_ref(),
77-
TopKPlan::Basic(BasicTopKPlan { limit, .. }) => limit.as_ref(),
78-
};
79-
if let Some(expr) = limit_expr {
80-
// Produce errors from limit selectors that error or are
81-
// negative, and nothing from limit selectors that do
82-
// not. Note that even if expr.could_error() is false,
83-
// the expression might still return a negative limit and
84-
// thus needs to be checked.
85-
let expr = expr.clone();
86-
let mut datum_vec = mz_repr::DatumVec::new();
87-
let errors = ok_input.flat_map(move |row| {
88-
let temp_storage = mz_repr::RowArena::new();
89-
let datums = datum_vec.borrow_with(&row);
90-
match expr.eval(&datums[..], &temp_storage) {
91-
Ok(l) if l != Datum::Null && l.unwrap_int64() < 0 => {
92-
Some(EvalError::NegLimit.into())
74+
75+
match top_k_plan.limit().map(|l| (l.as_literal(), l)) {
76+
None => {}
77+
Some((Some(Ok(literal)), _))
78+
if literal == Datum::Null || literal.unwrap_int64() >= 0 => {}
79+
Some((_, expr)) => {
80+
// Produce errors from limit selectors that error or are
81+
// negative, and nothing from limit selectors that do
82+
// not. Note that even if expr.could_error() is false,
83+
// the expression might still return a negative limit and
84+
// thus needs to be checked.
85+
let expr = expr.clone();
86+
let mut datum_vec = mz_repr::DatumVec::new();
87+
let errors = ok_input.flat_map(move |row| {
88+
let temp_storage = mz_repr::RowArena::new();
89+
let datums = datum_vec.borrow_with(&row);
90+
match expr.eval(&datums[..], &temp_storage) {
91+
Ok(l) if l != Datum::Null && l.unwrap_int64() < 0 => {
92+
Some(EvalError::NegLimit.into())
93+
}
94+
Ok(_) => None,
95+
Err(e) => Some(e.into()),
9396
}
94-
Ok(_) => None,
95-
Err(e) => Some(e.into()),
96-
}
97-
});
98-
err_collection = err_collection.concat(&errors);
97+
});
98+
err_collection = err_collection.concat(&errors);
99+
}
99100
}
100101

101102
let ok_result = match top_k_plan {

0 commit comments

Comments
 (0)