protected QueryPlan compileJoinQuery()

in phoenix-core-client/src/main/java/org/apache/phoenix/compile/QueryCompiler.java [375:657]


    protected QueryPlan compileJoinQuery(JoinCompiler.Strategy strategy, StatementContext context, JoinTable joinTable, boolean asSubquery, boolean projectPKColumns, List<OrderByNode> orderBy) throws SQLException {
        byte[] emptyByteArray = new byte[0];
        List<JoinSpec> joinSpecs = joinTable.getJoinSpecs();
        boolean wildcardIncludesDynamicCols = context.getConnection().getQueryServices()
                .getConfiguration().getBoolean(WILDCARD_QUERY_DYNAMIC_COLS_ATTRIB,
                        DEFAULT_WILDCARD_QUERY_DYNAMIC_COLS_ATTRIB);
        switch (strategy) {
            case HASH_BUILD_RIGHT: {
                boolean[] starJoinVector = joinTable.getStarJoinVector();
                Table table = joinTable.getLeftTable();
                PTable initialProjectedTable;
                TableRef tableRef;
                SelectStatement query;
                TupleProjector tupleProjector;
                if (!table.isSubselect()) {
                    context.setCurrentTable(table.getTableRef());
                    initialProjectedTable = table.createProjectedTable(!projectPKColumns, context);
                    tableRef = table.getTableRef();
                    table.projectColumns(context.getScan());
                    query = joinTable.getAsSingleSubquery(table.getAsSubquery(orderBy), asSubquery);
                    tupleProjector = new TupleProjector(initialProjectedTable);
                } else {
                    SelectStatement subquery = table.getAsSubquery(orderBy);
                    QueryPlan plan = compileSubquery(subquery, false);
                    initialProjectedTable = table.createProjectedTable(plan.getProjector());
                    tableRef = plan.getTableRef();
                    context.getScan().setFamilyMap(plan.getContext().getScan().getFamilyMap());
                    query = joinTable.getAsSingleSubquery((SelectStatement) plan.getStatement(), asSubquery);
                    tupleProjector = new TupleProjector(plan.getProjector());
                }
                context.setCurrentTable(tableRef);
                PTable projectedTable = initialProjectedTable;
                int count = joinSpecs.size();
                ImmutableBytesPtr[] joinIds = new ImmutableBytesPtr[count];
                List<Expression>[] joinExpressions = new List[count];
                JoinType[] joinTypes = new JoinType[count];
                PTable[] tables = new PTable[count];
                int[] fieldPositions = new int[count];
                StatementContext[] subContexts = new StatementContext[count];
                QueryPlan[] subPlans = new QueryPlan[count];
                HashSubPlan[] hashPlans = new HashSubPlan[count];
                fieldPositions[0] = projectedTable.getColumns().size() - projectedTable.getPKColumns().size();
                for (int i = 0; i < count; i++) {
                    JoinSpec joinSpec = joinSpecs.get(i);
                    Scan subScan = ScanUtil.newScan(originalScan);
                    subContexts[i] = new StatementContext(statement, context.getResolver(), context.getBindManager(), subScan, new SequenceManager(statement));
                    subPlans[i] = compileJoinQuery(
                            subContexts[i],
                            joinSpec.getRhsJoinTable(),
                            true,
                            true,
                            null);
                    boolean hasPostReference = joinSpec.getRhsJoinTable().hasPostReference();
                    if (hasPostReference) {
                        tables[i] = subContexts[i].getResolver().getTables().get(0).getTable();
                        projectedTable = JoinCompiler.joinProjectedTables(projectedTable, tables[i], joinSpec.getType());
                    } else {
                        tables[i] = null;
                    }
                }
                for (int i = 0; i < count; i++) {
                    JoinSpec joinSpec = joinSpecs.get(i);
                    context.setResolver(FromCompiler.getResolverForProjectedTable(projectedTable, context.getConnection(), query.getUdfParseNodes()));
                    joinIds[i] = new ImmutableBytesPtr(emptyByteArray); // place-holder
                    Pair<List<Expression>, List<Expression>> joinConditions = joinSpec.compileJoinConditions(context, subContexts[i], strategy);
                    joinExpressions[i] = joinConditions.getFirst();
                    List<Expression> hashExpressions = joinConditions.getSecond();
                    Pair<Expression, Expression> keyRangeExpressions = new Pair<Expression, Expression>(null, null);
                    boolean optimized = getKeyExpressionCombinations(
                            keyRangeExpressions,
                            context,
                            joinTable.getOriginalJoinSelectStatement(),
                            tableRef,
                            joinSpec.getType(),
                            joinExpressions[i],
                            hashExpressions);
                    Expression keyRangeLhsExpression = keyRangeExpressions.getFirst();
                    Expression keyRangeRhsExpression = keyRangeExpressions.getSecond();
                    joinTypes[i] = joinSpec.getType();
                    if (i < count - 1) {
                        fieldPositions[i + 1] = fieldPositions[i] + (tables[i] == null ? 0 : (tables[i].getColumns().size() - tables[i].getPKColumns().size()));
                    }
                    hashPlans[i] = new HashSubPlan(i, subPlans[i], optimized ? null : hashExpressions, joinSpec.isSingleValueOnly(), usePersistentCache, keyRangeLhsExpression, keyRangeRhsExpression);
                }
                TupleProjector.serializeProjectorIntoScan(context.getScan(), tupleProjector,
                        wildcardIncludesDynamicCols);
                QueryPlan plan = compileSingleFlatQuery(
                        context,
                        query,
                        asSubquery,
                        !asSubquery && joinTable.isAllLeftJoin(),
                        null, true, false);
                Expression postJoinFilterExpression = joinTable.compilePostFilterExpression(context);
                Integer limit = null;
                Integer offset = null;
                if (!query.isAggregate() && !query.isDistinct() && query.getOrderBy().isEmpty()) {
                    limit = plan.getLimit();
                    offset = plan.getOffset();
                }
                HashJoinInfo joinInfo = new HashJoinInfo(projectedTable, joinIds, joinExpressions, joinTypes,
                        starJoinVector, tables, fieldPositions, postJoinFilterExpression, QueryUtil.getOffsetLimit(limit, offset));
                return HashJoinPlan.create(joinTable.getOriginalJoinSelectStatement(), plan, joinInfo, hashPlans);
            }
            case HASH_BUILD_LEFT: {
                JoinSpec lastJoinSpec = joinSpecs.get(joinSpecs.size() - 1);
                JoinType type = lastJoinSpec.getType();
                JoinTable rhsJoinTable = lastJoinSpec.getRhsJoinTable();
                Table rhsTable = rhsJoinTable.getLeftTable();
                JoinTable lhsJoin = joinTable.createSubJoinTable(statement.getConnection());
                Scan subScan = ScanUtil.newScan(originalScan);
                StatementContext lhsCtx = new StatementContext(statement, context.getResolver(), context.getBindManager(), subScan, new SequenceManager(statement));
                QueryPlan lhsPlan = compileJoinQuery(lhsCtx, lhsJoin, true, true, null);
                PTable rhsProjTable;
                TableRef rhsTableRef;
                SelectStatement rhs;
                TupleProjector tupleProjector;
                if (!rhsTable.isSubselect()) {
                    context.setCurrentTable(rhsTable.getTableRef());
                    rhsProjTable = rhsTable.createProjectedTable(!projectPKColumns, context);
                    rhsTableRef = rhsTable.getTableRef();
                    rhsTable.projectColumns(context.getScan());
                    rhs = rhsJoinTable.getAsSingleSubquery(rhsTable.getAsSubquery(orderBy), asSubquery);
                    tupleProjector = new TupleProjector(rhsProjTable);
                } else {
                    SelectStatement subquery = rhsTable.getAsSubquery(orderBy);
                    QueryPlan plan = compileSubquery(subquery, false);
                    rhsProjTable = rhsTable.createProjectedTable(plan.getProjector());
                    rhsTableRef = plan.getTableRef();
                    context.getScan().setFamilyMap(plan.getContext().getScan().getFamilyMap());
                    rhs = rhsJoinTable.getAsSingleSubquery((SelectStatement) plan.getStatement(), asSubquery);
                    tupleProjector = new TupleProjector(plan.getProjector());
                }
                context.setCurrentTable(rhsTableRef);
                context.setResolver(FromCompiler.getResolverForProjectedTable(rhsProjTable, context.getConnection(), rhs.getUdfParseNodes()));
                ImmutableBytesPtr[] joinIds = new ImmutableBytesPtr[]{new ImmutableBytesPtr(emptyByteArray)};
                Pair<List<Expression>, List<Expression>> joinConditions = lastJoinSpec.compileJoinConditions(lhsCtx, context, strategy);
                List<Expression> joinExpressions = joinConditions.getSecond();
                List<Expression> hashExpressions = joinConditions.getFirst();
                boolean needsMerge = lhsJoin.hasPostReference();
                PTable lhsTable = needsMerge ? lhsCtx.getResolver().getTables().get(0).getTable() : null;
                int fieldPosition = needsMerge ? rhsProjTable.getColumns().size() - rhsProjTable.getPKColumns().size() : 0;
                PTable projectedTable = needsMerge ? JoinCompiler.joinProjectedTables(rhsProjTable, lhsTable, type == JoinType.Right ? JoinType.Left : type) : rhsProjTable;
                TupleProjector.serializeProjectorIntoScan(context.getScan(), tupleProjector,
                        wildcardIncludesDynamicCols);
                context.setResolver(FromCompiler.getResolverForProjectedTable(projectedTable, context.getConnection(), rhs.getUdfParseNodes()));
                QueryPlan rhsPlan = compileSingleFlatQuery(
                        context,
                        rhs,
                        asSubquery,
                        !asSubquery && type == JoinType.Right,
                        null,
                        true,
                        false);
                Expression postJoinFilterExpression = joinTable.compilePostFilterExpression(context);
                Integer limit = null;
                Integer offset = null;
                if (!rhs.isAggregate() && !rhs.isDistinct() && rhs.getOrderBy().isEmpty()) {
                    limit = rhsPlan.getLimit();
                    offset = rhsPlan.getOffset();
                }
                HashJoinInfo joinInfo = new HashJoinInfo(projectedTable, joinIds, new List[]{joinExpressions},
                        new JoinType[]{type == JoinType.Right ? JoinType.Left : type}, new boolean[]{true},
                        new PTable[]{lhsTable}, new int[]{fieldPosition}, postJoinFilterExpression, QueryUtil.getOffsetLimit(limit, offset));
                boolean usePersistentCache = joinTable.getOriginalJoinSelectStatement().getHint().hasHint(Hint.USE_PERSISTENT_CACHE);
                Pair<Expression, Expression> keyRangeExpressions = new Pair<Expression, Expression>(null, null);
                getKeyExpressionCombinations(
                        keyRangeExpressions,
                        context,
                        joinTable.getOriginalJoinSelectStatement(),
                        rhsTableRef,
                        type,
                        joinExpressions,
                        hashExpressions);
                return HashJoinPlan.create(
                        joinTable.getOriginalJoinSelectStatement(),
                        rhsPlan,
                        joinInfo,
                        new HashSubPlan[]{
                                new HashSubPlan(
                                        0,
                                        lhsPlan,
                                        hashExpressions,
                                        false,
                                        usePersistentCache,
                                        keyRangeExpressions.getFirst(),
                                        keyRangeExpressions.getSecond())});
            }
            case SORT_MERGE: {
                JoinTable lhsJoin =  joinTable.createSubJoinTable(statement.getConnection());
                JoinSpec lastJoinSpec = joinSpecs.get(joinSpecs.size() - 1);
                JoinType type = lastJoinSpec.getType();
                JoinTable rhsJoin = lastJoinSpec.getRhsJoinTable();
                if (type == JoinType.Right) {
                    JoinTable temp = lhsJoin;
                    lhsJoin = rhsJoin;
                    rhsJoin = temp;
                }

                List<EqualParseNode> joinConditionNodes = lastJoinSpec.getOnConditions();
                List<OrderByNode> lhsOrderBy = Lists.<OrderByNode>newArrayListWithExpectedSize(joinConditionNodes.size());
                List<OrderByNode> rhsOrderBy = Lists.<OrderByNode>newArrayListWithExpectedSize(joinConditionNodes.size());
                for (EqualParseNode condition : joinConditionNodes) {
                    lhsOrderBy.add(NODE_FACTORY.orderBy(type == JoinType.Right ? condition.getRHS() : condition.getLHS(), false, true));
                    rhsOrderBy.add(NODE_FACTORY.orderBy(type == JoinType.Right ? condition.getLHS() : condition.getRHS(), false, true));
                }

                Scan lhsScan = ScanUtil.newScan(originalScan);
                StatementContext lhsCtx = new StatementContext(statement, context.getResolver(), context.getBindManager(), lhsScan, new SequenceManager(statement));
                boolean preserveRowkey = !projectPKColumns && type != JoinType.Full;
                QueryPlan lhsPlan = compileJoinQuery(lhsCtx, lhsJoin, true, !preserveRowkey, lhsOrderBy);
                PTable lhsProjTable = lhsCtx.getResolver().getTables().get(0).getTable();

                Scan rhsScan = ScanUtil.newScan(originalScan);
                StatementContext rhsCtx = new StatementContext(statement, context.getResolver(), context.getBindManager(), rhsScan, new SequenceManager(statement));
                QueryPlan rhsPlan = compileJoinQuery(rhsCtx, rhsJoin, true, true, rhsOrderBy);
                PTable rhsProjTable = rhsCtx.getResolver().getTables().get(0).getTable();

                Pair<List<Expression>, List<Expression>> joinConditions = lastJoinSpec.compileJoinConditions(type == JoinType.Right ? rhsCtx : lhsCtx, type == JoinType.Right ? lhsCtx : rhsCtx, strategy);
                List<Expression> lhsKeyExpressions = type == JoinType.Right ? joinConditions.getSecond() : joinConditions.getFirst();
                List<Expression> rhsKeyExpressions = type == JoinType.Right ? joinConditions.getFirst() : joinConditions.getSecond();

                boolean needsMerge = rhsJoin.hasPostReference();
                int fieldPosition = needsMerge ? lhsProjTable.getColumns().size() - lhsProjTable.getPKColumns().size() : 0;
                PTable projectedTable = needsMerge ? JoinCompiler.joinProjectedTables(lhsProjTable, rhsProjTable, type == JoinType.Right ? JoinType.Left : type) : lhsProjTable;

                ColumnResolver resolver = FromCompiler.getResolverForProjectedTable(projectedTable, context.getConnection(), joinTable.getOriginalJoinSelectStatement().getUdfParseNodes());
                TableRef tableRef = resolver.getTables().get(0);
                StatementContext subCtx = new StatementContext(statement, resolver, context.getBindManager(), ScanUtil.newScan(originalScan), new SequenceManager(statement));
                subCtx.setCurrentTable(tableRef);
                QueryPlan innerPlan = new SortMergeJoinPlan(
                        subCtx,
                        joinTable.getOriginalJoinSelectStatement(),
                        tableRef,
                        type == JoinType.Right ? JoinType.Left : type,
                        lhsPlan,
                        rhsPlan,
                        new Pair<List<Expression>,List<Expression>>(lhsKeyExpressions, rhsKeyExpressions),
                        rhsKeyExpressions,
                        projectedTable,
                        lhsProjTable,
                        needsMerge ? rhsProjTable : null,
                        fieldPosition,
                        lastJoinSpec.isSingleValueOnly(),
                        new Pair<List<OrderByNode>,List<OrderByNode>>(lhsOrderBy, rhsOrderBy));
                context.setCurrentTable(tableRef);
                context.setResolver(resolver);
                TableNode from = NODE_FACTORY.namedTable(tableRef.getTableAlias(), NODE_FACTORY.table(tableRef.getTable().getSchemaName().getString(), tableRef.getTable().getTableName().getString()));
                ParseNode where = joinTable.getPostFiltersCombined();
                SelectStatement select = asSubquery ?
                        NODE_FACTORY.select(
                                from,
                                joinTable.getOriginalJoinSelectStatement().getHint(),
                                false,
                                Collections.<AliasedNode>emptyList(),
                                where,
                                null,
                                null,
                                orderBy,
                                null,
                                null,
                                0,
                                false,
                                joinTable.getOriginalJoinSelectStatement().hasSequence(),
                                Collections.<SelectStatement>emptyList(),
                                joinTable.getOriginalJoinSelectStatement().getUdfParseNodes()) :
                         NODE_FACTORY.select(
                                 joinTable.getOriginalJoinSelectStatement(),
                                 from,
                                 where);

                return compileSingleFlatQuery(
                        context,
                        select,
                        asSubquery,
                        false,
                        innerPlan,
                        true,
                        false);
            }
            default:
                throw new IllegalArgumentException("Invalid join strategy '" + strategy + "'");
        }
    }