MCPcopy Create free account
hub / github.com/MaterializeInc/materialize / flat_map

Method flat_map

src/expr/src/interpret.rs:298–377  ·  view source on GitHub ↗

This method "maps" a function across the `ResultSpec`. As mentioned above, `ResultSpec` represents an approximate set of results. If we actually stored each result in the set, `flat_map` could be implemented by passing each result to the function one-by-one and unioning the resulting sets. This is possible when our values set is empty or contains a single datum, but when it contains a range, we c

(
        &self,
        is_monotone: bool,
        mut result_map: impl FnMut(Result<Datum<'a>, EvalError>) -> ResultSpec<'a>,
    )

Source from the content-addressed store, hash-verified

296 /// mapping the endpoints;
297 /// - using a safe default when we can't infer a tighter bound on the set, eg. [Self::anything].
298 fn flat_map(
299 &self,
300 is_monotone: bool,
301 mut result_map: impl FnMut(Result<Datum<'a>, EvalError>) -> ResultSpec<'a>,
302 ) -> ResultSpec<'a> {
303 let null_spec = if self.nullable {
304 result_map(Ok(Datum::Null))
305 } else {
306 ResultSpec::nothing()
307 };
308
309 let error_spec = if self.fallible {
310 // Since we only care about whether / not an error is possible, and not the specific
311 // error, create an arbitrary error here.
312 // NOTE! This assumes that functions do not discriminate on the type of the error.
313 let map_err = result_map(Err(EvalError::Internal("".into())));
314 let raise_err = ResultSpec::fails();
315 // SQL has a very loose notion of evaluation order: https://www.postgresql.org/docs/current/sql-expressions.html#SYNTAX-EXPRESS-EVAL
316 // Here, we account for the possibility that the expression is evaluated strictly,
317 // raising the error, or that it's evaluated lazily by the result_map function
318 // (which may return a non-error result even when given an error as input).
319 raise_err.union(map_err)
320 } else {
321 ResultSpec::nothing()
322 };
323
324 let values_spec = match self.values {
325 Values::Empty => ResultSpec::nothing(),
326 // If this range contains a single datum, just call the function.
327 Values::Within(min, max) if min == max => result_map(Ok(min)),
328 // If this is a range of booleans, we know all the values... just try them.
329 Values::Within(Datum::False, Datum::True) => {
330 result_map(Ok(Datum::False)).union(result_map(Ok(Datum::True)))
331 }
332 // Otherwise, if our function is monotonic, we can try mapping the input
333 // range to an output range.
334 Values::Within(min, max) if is_monotone => {
335 let min_result = result_map(Ok(min));
336 let max_result = result_map(Ok(max));
337 match (min_result, max_result) {
338 // If both endpoints give us a range, the result is a union of those ranges.
339 (
340 ResultSpec {
341 nullable: false,
342 fallible: false,
343 values: a_values,
344 },
345 ResultSpec {
346 nullable: false,
347 fallible: false,
348 values: b_values,
349 },
350 ) => ResultSpec {
351 nullable: false,
352 fallible: false,
353 values: a_values.union(b_values),
354 },
355 // If both endpoints are null, we assume the whole range maps to null.

Callers 4

unaryMethod · 0.45
binaryMethod · 0.45
eval_loopMethod · 0.45
condMethod · 0.45

Calls 1

unionMethod · 0.45

Tested by

no test coverage detected