@Override
  public Result apply(JoinNode joinNode, Captures captures, Context context) {
    Set<Symbol> globallyUsableInputs =
        ImmutableSet.<Symbol>builder()
            .addAll(joinNode.getOutputSymbols())
            .addAll(
                joinNode.getFilter().map(SymbolsExtractor::extractUnique).orElse(ImmutableSet.of()))
            .build();

    Set<Symbol> leftUsableInputs =
        ImmutableSet.<Symbol>builder()
            .addAll(globallyUsableInputs)
            .addAll(
                joinNode.getCriteria().stream().map(JoinNode.EquiJoinClause::getLeft).iterator())
            .addAll(
                joinNode
                    .getAsofCriteria()
                    .map(criteria -> ImmutableSet.of(criteria.getLeft()))
                    .orElse(ImmutableSet.of()))
            // .addAll(joinNode.getLeftHashSymbol().map(ImmutableSet::of).orElse(ImmutableSet.of()))
            .build();

    Set<Symbol> rightUsableInputs =
        ImmutableSet.<Symbol>builder()
            .addAll(globallyUsableInputs)
            .addAll(
                joinNode.getCriteria().stream().map(JoinNode.EquiJoinClause::getRight).iterator())
            .addAll(
                joinNode
                    .getAsofCriteria()
                    .map(criteria -> ImmutableSet.of(criteria.getRight()))
                    .orElse(ImmutableSet.of()))
            // .addAll(joinNode.getRightHashSymbol().map(ImmutableSet::of).orElse(ImmutableSet.of()))
            .build();

    return restrictChildOutputs(
            context.getIdAllocator(), joinNode, leftUsableInputs, rightUsableInputs)
        .map(Result::ofPlanNode)
        .orElse(Result.empty());
  }
Joins support output symbol selection, so make any project-off of child columns explicit in
project nodes.
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneJoinChildrenColumns.java