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

Method actually_perform_transform

src/transform/src/lib.rs:459–560  ·  view source on GitHub ↗
(
        &self,
        relation: &mut MirRelationExpr,
        ctx: &mut TransformCtx,
    )

Source from the content-addressed store, hash-verified

457 fields(path.segment = self.name)
458 )]
459 fn actually_perform_transform(
460 &self,
461 relation: &mut MirRelationExpr,
462 ctx: &mut TransformCtx,
463 ) -> Result<(), TransformError> {
464 // The number of iterations for a relation to settle depends on the
465 // number of nodes in the relation. Instead of picking an arbitrary
466 // hard limit on the number of iterations, we use a soft limit and
467 // check whether the relation has become simpler after reaching it.
468 // If so, we perform another pass of transforms. Otherwise, there is
469 // a bug somewhere that prevents the relation from settling on a
470 // stable shape.
471 let mut iter_no = 0;
472 let mut seen = BTreeMap::new();
473 seen.insert(relation.hash_to_u64(), iter_no);
474 let original = relation.clone();
475 loop {
476 let prev_size = relation.size();
477 for i in iter_no..iter_no + self.limit {
478 let prev = relation.clone();
479 self.apply_transforms(relation, ctx, format!("{i:04}"))?;
480 if *relation == prev {
481 if prev_size > 100000 {
482 tracing::warn!(%prev_size, "Very big MIR plan");
483 }
484 mz_repr::explain::trace_plan(relation);
485 return Ok(());
486 }
487 let seen_i = seen.insert(relation.hash_to_u64(), i);
488 if let Some(seen_i) = seen_i {
489 // Let's see whether this is just a hash collision, or a real loop: Run the
490 // whole thing from the beginning up until `seen_i`, and compare all the plans
491 // to the current plan from the outer `for`.
492 // (It would not be enough to compare only the plan at `seen_i`, because
493 // then we could miss a real loop if there is also a hash collision somewhere
494 // in the middle of the loop, because then we'd compare the last plan of the
495 // loop not with its actual match, but with the colliding plan.)
496 let mut again = original.clone();
497 // The `+2` is because:
498 // - one `+1` is to finally get to the plan at `seen_i`,
499 // - another `+1` is because we are comparing to `relation` only _before_
500 // calling `apply_transforms`.
501 for j in 0..(seen_i + 2) {
502 if again == *relation {
503 // We really got into an infinite loop (e.g., we are oscillating between
504 // two plans). This is not catastrophic, because we can just say we are
505 // done now, but it would be great to eventually find a way to prevent
506 // these loops from happening in the first place. We have several
507 // relevant issues, see
508 // https://github.com/MaterializeInc/database-issues/issues/8197#issuecomment-2200172227
509 mz_repr::explain::trace_plan(relation);
510 error!(
511 "Fixpoint `{}` detected a loop of length {} after {} iterations",
512 self.name,
513 i - seen_i,
514 i
515 );
516 return Ok(());

Callers 1

transformMethod · 0.45

Calls 9

trace_planFunction · 0.85
hash_to_u64Method · 0.80
apply_transformsMethod · 0.80
update_last_hashMethod · 0.80
insertMethod · 0.45
cloneMethod · 0.45
sizeMethod · 0.45
iterMethod · 0.45
transformMethod · 0.45

Tested by

no test coverage detected