class ChooseJoinAlgorithm implements OptimizerRule {

    @Override
    public PlanNode execute( QueryContext context,
                             PlanNode plan,
                             LinkedList<OptimizerRule> ruleStack ) {
        // For each of the JOIN nodes ...
        for (PlanNode joinNode : plan.findAllAtOrBelow(Type.JOIN)) {
            JoinCondition condition = joinNode.getProperty(Property.JOIN_CONDITION, JoinCondition.class);
            if (useOnlyNested) {
                joinNode.setProperty(Property.JOIN_ALGORITHM, JoinAlgorithm.NESTED_LOOP);
                break;
            }

            if (condition instanceof DescendantNodeJoinCondition) {
                // It has to be a nest-loop join ...
                joinNode.setProperty(Property.JOIN_ALGORITHM, JoinAlgorithm.NESTED_LOOP);
            } else {
                joinNode.setProperty(Property.JOIN_ALGORITHM, JoinAlgorithm.MERGE);
                assert joinNode.getChildCount() == 2;

                // We can try to use the merge join, but we need to sort and remove remove duplicates ...
                // on the left and right children of the join ...
                Set<SelectorName> leftSelectors = joinNode.getFirstChild().getSelectors();
                Set<SelectorName> rightSelectors = joinNode.getLastChild().getSelectors();
                List<Object> leftSortBy = new LinkedList<Object>();
                List<Object> rightSortBy = new LinkedList<Object>();
                createOrderBysForJoinCondition(condition, leftSelectors, leftSortBy, rightSelectors, rightSortBy);

                PlanNode leftSort = new PlanNode(Type.SORT, leftSelectors);
                leftSort.setProperty(Property.SORT_ORDER_BY, leftSortBy);
                joinNode.getFirstChild().insertAsParent(leftSort);
                if (joinNode.getFirstChild().findAllAtOrBelow(Type.DUP_REMOVE).isEmpty()) {
                    // There is no duplicate removal below the left-hand side of the join, so insert it ...
                    PlanNode leftDupRemoval = new PlanNode(Type.DUP_REMOVE, leftSelectors);
                    joinNode.getFirstChild().insertAsParent(leftDupRemoval);
                }

                // There is no sort below the right-hand side of the join, so insert it ...
                PlanNode rightSort = new PlanNode(Type.SORT, rightSelectors);
                rightSort.setProperty(Property.SORT_ORDER_BY, rightSortBy);
                joinNode.getLastChild().insertAsParent(rightSort);
                if (joinNode.getLastChild().findAllAtOrBelow(Type.DUP_REMOVE).isEmpty()) {
                    // There is no duplicate removal below the right-hand side of the join, so insert it ...
                    PlanNode rightDupRemoval = new PlanNode(Type.DUP_REMOVE, rightSelectors);
                    joinNode.getLastChild().insertAsParent(rightDupRemoval);
                }
            }
        }
        return plan;
    }

    protected  ChooseJoinAlgorithm( boolean useOnlyNested );

}