Source:
<code><START> public Optional<PlanNode> planTableScan(TableScanNode node, Expression predicate) <END> { Expression deterministicPredicate = stripNonDeterministicConjuncts(predicate); DomainTranslator.ExtractionResult decomposedPredicate = DomainTranslator.fromPredicate( metadata, session, deterministicPredicate, symbolAllocator.getTypes()); TupleDomain<ColumnHandle> simplifiedConstraint = decomposedPredicate.getTupleDomain() .transform(node.getAssignments()::get) .intersect(node.getCurrentConstraint()); List<TableLayoutResult> layouts = metadata.getLayouts( session, node.getTable(), new Constraint<>(simplifiedConstraint, bindings -> true), Optional.of(ImmutableSet.copyOf(node.getAssignments().values()))); layouts = layouts.stream() .filter(layoutHasAllNeededOutputs(node)) .collect(toImmutableList()); if (layouts.isEmpty()) { return Optional.of(new ValuesNode(idAllocator.getNextId(), node.getOutputSymbols(), ImmutableList.of())); } TableLayoutResult layout = layouts.get(0); TableScanNode rewrittenTableScan = new TableScanNode( node.getId(), node.getTable(), node.getOutputSymbols(), node.getAssignments(), Optional.of(layout.getLayout().getHandle()), simplifiedConstraint.intersect(layout.getLayout().getPredicate()), Optional.ofNullable(node.getOriginalConstraint()).orElse(predicate)); Map<ColumnHandle, Symbol> assignments = ImmutableBiMap.copyOf(node.getAssignments()).inverse(); Expression resultingPredicate = combineConjuncts( decomposedPredicate.getRemainingExpression(), stripDeterministicConjuncts(predicate), DomainTranslator.toPredicate(layout.getUnenforcedConstraint().transform(assignments::get))); PlanNode result = rewrittenTableScan; if (!BooleanLiteral.TRUE_LITERAL.equals(resultingPredicate)) { result = new FilterNode(idAllocator.getNextId(), rewrittenTableScan, resultingPredicate); } if (result instanceof FilterNode && !planChanged(node, rewrittenTableScan, predicate, resultingPredicate)) { return Optional.empty(); } return Optional.of(result); }</code><technical_language>I TableLayoutRewriter responsibility check plan changed. rule checks is required in order loop forever. Other callers of planTableScan care if plan changed</technical_language>

Target:
public PlanNode planTableScan(TableScanNode node, Expression predicate) { Expression deterministicPredicate = stripNonDeterministicConjuncts(predicate); DomainTranslator.ExtractionResult decomposedPredicate = DomainTranslator.fromPredicate( metadata, session, deterministicPredicate, symbolAllocator.getTypes()); TupleDomain<ColumnHandle> simplifiedConstraint = decomposedPredicate.getTupleDomain() .transform(node.getAssignments()::get) .intersect(node.getCurrentConstraint()); List<TableLayoutResult> layouts = metadata.getLayouts( session, node.getTable(), new Constraint<>(simplifiedConstraint, bindings -> true), Optional.of(ImmutableSet.copyOf(node.getAssignments().values()))); layouts = layouts.stream() .filter(layoutHasAllNeededOutputs(node)) .collect(toImmutableList()); if (layouts.isEmpty()) { return new ValuesNode(idAllocator.getNextId(), node.getOutputSymbols(), ImmutableList.of()); } TableLayoutResult layout = layouts.get(0); TableScanNode rewrittenTableScan = new TableScanNode( node.getId(), node.getTable(), node.getOutputSymbols(), node.getAssignments(), Optional.of(layout.getLayout().getHandle()), simplifiedConstraint.intersect(layout.getLayout().getPredicate()), Optional.ofNullable(node.getOriginalConstraint()).orElse(predicate)); Map<ColumnHandle, Symbol> assignments = ImmutableBiMap.copyOf(node.getAssignments()).inverse(); Expression resultingPredicate = combineConjuncts( decomposedPredicate.getRemainingExpression(), stripDeterministicConjuncts(predicate), DomainTranslator.toPredicate(layout.getUnenforcedConstraint().transform(assignments::get))); PlanNode result = rewrittenTableScan; if (!BooleanLiteral.TRUE_LITERAL.equals(resultingPredicate)) { result = new FilterNode(idAllocator.getNextId(), rewrittenTableScan, resultingPredicate); } return result; }