presto-main/src/main/java/io/prestosql/sql/planner/sanity/DynamicFiltersChecker.java/DynamicFiltersChecker/validate
class DynamicFiltersChecker:    @Override
    public void validate(PlanNode plan, Session session, Metadata metadata, TypeAnalyzer typeAnalyzer, TypeProvider types, WarningCollector warningCollector)
    {
        plan.accept(new PlanVisitor<Set<String>, Void>()
        {
            @Override
            protected Set<String> visitPlan(PlanNode node, Void context)
            {
                Set<String> consumed = new HashSet<>();
                for (PlanNode source : node.getSources()) {
                    consumed.addAll(source.accept(this, context));
                }
                return consumed;
            }

            @Override
            public Set<String> visitOutput(OutputNode node, Void context)
            {
                Set<String> unmatched = visitPlan(node, context);
                verify(unmatched.isEmpty(), "All consumed dynamic filters could not be matched with a join.");
                return unmatched;
            }

            @Override
            public Set<String> visitJoin(JoinNode node, Void context)
            {
                Set<String> currentJoinDynamicFilters = node.getDynamicFilters().keySet();
                Set<String> consumedProbeSide = node.getLeft().accept(this, context);
                verify(difference(currentJoinDynamicFilters, consumedProbeSide).isEmpty(),
                        "Dynamic filters present in join were not fully consumed by it's probe side.");

                Set<String> consumedBuildSide = node.getRight().accept(this, context);
                verify(intersection(currentJoinDynamicFilters, consumedBuildSide).isEmpty());

                Set<String> unmatched = new HashSet<>(consumedBuildSide);
                unmatched.addAll(consumedProbeSide);
                unmatched.removeAll(currentJoinDynamicFilters);
                return ImmutableSet.copyOf(unmatched);
            }

            @Override
            public Set<String> visitFilter(FilterNode node, Void context)
            {
                ImmutableSet.Builder<String> consumed = ImmutableSet.builder();
                List<DynamicFilters.Descriptor> dynamicFilters = extractDynamicFilters(metadata, node.getPredicate()).getDynamicConjuncts();
                dynamicFilters.stream()
                        .map(DynamicFilters.Descriptor::getId)
                        .forEach(consumed::add);
                consumed.addAll(node.getSource().accept(this, context));
                return consumed.build();
            }
        }, null);
    }