Extracts common expressions from multiple `Self` into a result `Self`. The argument `mfps` are mutated so that each are functionaly equivalent to their corresponding input, when composed atop the resulting `Self`. The `extract_exprs` argument is temporary, as we roll out the `extract_common_mfp_expressions` flag.
(mfps: &mut [&mut Self])
| 478 | /// |
| 479 | /// The `extract_exprs` argument is temporary, as we roll out the `extract_common_mfp_expressions` flag. |
| 480 | pub fn extract_common(mfps: &mut [&mut Self]) -> Self { |
| 481 | match mfps.len() { |
| 482 | 0 => { |
| 483 | panic!("Cannot call method on empty arguments"); |
| 484 | } |
| 485 | 1 => { |
| 486 | let output_arity = mfps[0].projection.len(); |
| 487 | std::mem::replace(mfps[0], MapFilterProject::new(output_arity)) |
| 488 | } |
| 489 | _ => { |
| 490 | // More generally, we convert each mfp to ANF, at which point we can |
| 491 | // repeatedly extract atomic expressions that depend only on input |
| 492 | // columns, migrate them to an input mfp, and repeat until no such |
| 493 | // expressions exist. At this point, we can also migrate predicates |
| 494 | // and then determine and push down projections. |
| 495 | |
| 496 | // Prepare a return `Self`. |
| 497 | let mut result_mfp = MapFilterProject::new(mfps[0].input_arity); |
| 498 | |
| 499 | // We convert each mfp to ANF, using `memoize_expressions`. |
| 500 | for mfp in mfps.iter_mut() { |
| 501 | mfp.memoize_expressions(); |
| 502 | } |
| 503 | |
| 504 | // We repeatedly extract common expressions, until none remain. |
| 505 | let mut done = false; |
| 506 | while !done { |
| 507 | // We use references to determine common expressions, and must |
| 508 | // introduce a scope here to drop the borrows before mutation. |
| 509 | let common = { |
| 510 | // The input arity may increase as we iterate, so recapture. |
| 511 | let input_arity = result_mfp.projection.len(); |
| 512 | let mut prev: BTreeSet<_> = mfps[0] |
| 513 | .expressions |
| 514 | .iter() |
| 515 | .filter(|e| e.support().last() < Some(&input_arity)) |
| 516 | .collect(); |
| 517 | let mut next = BTreeSet::default(); |
| 518 | for mfp in mfps[1..].iter() { |
| 519 | for expr in mfp.expressions.iter() { |
| 520 | if prev.contains(expr) { |
| 521 | next.insert(expr); |
| 522 | } |
| 523 | } |
| 524 | std::mem::swap(&mut prev, &mut next); |
| 525 | next.clear(); |
| 526 | } |
| 527 | prev.into_iter().cloned().collect::<Vec<_>>() |
| 528 | }; |
| 529 | // Without new common expressions, we should terminate the loop. |
| 530 | done = common.is_empty(); |
| 531 | |
| 532 | // Migrate each expression in `common` to `result_mfp`. |
| 533 | for expr in common.into_iter() { |
| 534 | // Update each mfp by removing expr and updating column references. |
| 535 | for mfp in mfps.iter_mut() { |
| 536 | // With `expr` next in `result_mfp`, it is as if we are rotating it to |
| 537 | // be the first expression in `mfp`, and then removing it from `mfp` and |
nothing calls this directly
no test coverage detected