Search in sources :

Example 21 with SymbolMap

use of org.teiid.query.sql.util.SymbolMap in project teiid by teiid.

the class RulePushSelectCriteria method pushAcrossSetOp.

boolean pushAcrossSetOp(PlanNode critNode, PlanNode setOp, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
    // Find source node above union and grab the symbol map
    PlanNode sourceNode = NodeEditor.findParent(setOp, NodeConstants.Types.SOURCE);
    GroupSymbol virtualGroup = sourceNode.getGroups().iterator().next();
    if (createdNodes == null) {
        satisfyConditions(critNode, sourceNode, metadata);
    }
    SymbolMap symbolMap = (SymbolMap) sourceNode.getProperty(NodeConstants.Info.SYMBOL_MAP);
    SymbolMap childMap = symbolMap;
    // Move criteria to first child of union - names are the same, so no symbol mapping
    LinkedList<PlanNode> unionChildren = new LinkedList<PlanNode>();
    collectUnionChildren(setOp, unionChildren);
    int movedCount = 0;
    for (PlanNode planNode : unionChildren) {
        // Find first project node
        PlanNode projectNode = NodeEditor.findNodePreOrder(planNode, NodeConstants.Types.PROJECT);
        if (childMap == null) {
            childMap = SymbolMap.createSymbolMap(symbolMap.getKeys(), (List) projectNode.getProperty(NodeConstants.Info.PROJECT_COLS));
        }
        // we cannot simply move the node in the case where placing above or below the access would be invalid
        boolean handleSetOp = false;
        PlanNode accessNode = NodeEditor.findNodePreOrder(planNode, NodeConstants.Types.ACCESS, NodeConstants.Types.PROJECT);
        if (accessNode != null && NodeEditor.findParent(projectNode, NodeConstants.Types.SET_OP, NodeConstants.Types.ACCESS) != null) {
            handleSetOp = true;
        }
        // Move the node
        if (placeConvertedSelectNode(critNode, virtualGroup, projectNode, childMap, metadata)) {
            if (handleSetOp) {
                PlanNode newSelect = projectNode.getFirstChild();
                projectNode.replaceChild(newSelect, newSelect.getFirstChild());
                Object modelID = RuleRaiseAccess.getModelIDFromAccess(accessNode, metadata);
                Criteria crit = (Criteria) newSelect.getProperty(NodeConstants.Info.SELECT_CRITERIA);
                if (newSelect.hasBooleanProperty(NodeConstants.Info.IS_DEPENDENT_SET) && context != null && CapabilitiesUtil.supportsInlineView(modelID, metadata, capFinder) && CriteriaCapabilityValidatorVisitor.canPushLanguageObject(crit, modelID, metadata, capFinder, null)) {
                    accessNode.getFirstChild().addAsParent(newSelect);
                    List<Expression> old = (List<Expression>) projectNode.getProperty(NodeConstants.Info.PROJECT_COLS);
                    // create a project node based upon the created group and add it as the parent of the select
                    PlanNode project = RelationalPlanner.createProjectNode(LanguageObject.Util.deepClone(old, Expression.class));
                    newSelect.addAsParent(project);
                    // $NON-NLS-1$
                    PlanNode newSourceNode = RuleDecomposeJoin.rebuild(new GroupSymbol("intermediate"), null, newSelect.getFirstChild(), metadata, context, projectNode);
                    newSourceNode.setProperty(NodeConstants.Info.INLINE_VIEW, true);
                    accessNode.addGroups(newSourceNode.getGroups());
                    markDependent(newSelect, accessNode, metadata, capFinder);
                } else {
                    // or an inline view could be used similar to the above
                    if (createdNodes != null) {
                        createdNodes.remove(newSelect);
                    }
                    childMap = null;
                    continue;
                }
            }
            movedCount++;
        }
        // create a new symbol map for the other children
        childMap = null;
    }
    // TODO - the logic here could be made more intelligent about EXCEPT and INTERSECT.
    if (movedCount == unionChildren.size()) {
        critNode.setProperty(NodeConstants.Info.IS_PHANTOM, Boolean.TRUE);
        return true;
    }
    // otherwise mark it as pushed so we don't consider it again
    critNode.setProperty(NodeConstants.Info.IS_PUSHED, Boolean.TRUE);
    // if any moved, then we need to continue
    return movedCount != 0;
}
Also used : PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) Expression(org.teiid.query.sql.symbol.Expression) GroupSymbol(org.teiid.query.sql.symbol.GroupSymbol) SymbolMap(org.teiid.query.sql.util.SymbolMap) LanguageObject(org.teiid.query.sql.LanguageObject) CompoundCriteria(org.teiid.query.sql.lang.CompoundCriteria) DependentSetCriteria(org.teiid.query.sql.lang.DependentSetCriteria) Criteria(org.teiid.query.sql.lang.Criteria) CompareCriteria(org.teiid.query.sql.lang.CompareCriteria)

Example 22 with SymbolMap

use of org.teiid.query.sql.util.SymbolMap in project teiid by teiid.

the class RuleChooseDependent method fullyPush.

/**
 * Check for fully pushable dependent joins
 * currently we only look for the simplistic scenario where there are no intervening
 * nodes above the dependent side
 * @param independentExpressions
 */
private boolean fullyPush(PlanNode sourceNode, PlanNode joinNode, QueryMetadataInterface metadata, CapabilitiesFinder capabilitiesFinder, CommandContext context, PlanNode indNode, RuleStack rules, MakeDep makeDep, AnalysisRecord analysisRecord, List independentExpressions) throws QueryMetadataException, TeiidComponentException, QueryPlannerException {
    if (sourceNode.getType() != NodeConstants.Types.ACCESS) {
        // don't remove as we may raise an access node to make this possible
        return false;
    }
    Object modelID = RuleRaiseAccess.getModelIDFromAccess(sourceNode, metadata);
    boolean hasHint = false;
    if (makeDep != null && makeDep.getJoin() != null) {
        if (!makeDep.getJoin()) {
            // $NON-NLS-1$ //$NON-NLS-2$
            sourceNode.recordDebugAnnotation("cannot pushdown dependent join", modelID, "honoring hint", analysisRecord, null);
            return false;
        }
        hasHint = true;
    }
    if (!CapabilitiesUtil.supports(Capability.FULL_DEPENDENT_JOIN, modelID, metadata, capabilitiesFinder)) {
        if (hasHint) {
            // $NON-NLS-1$ //$NON-NLS-2$
            sourceNode.recordDebugAnnotation("cannot pushdown dependent join", modelID, "dependent join pushdown needs enabled at the source", analysisRecord, null);
        }
        return false;
    }
    List<? extends Expression> projected = (List<? extends Expression>) indNode.getProperty(Info.OUTPUT_COLS);
    if (projected == null) {
        PlanNode plan = sourceNode;
        while (plan.getParent() != null) {
            plan = plan.getParent();
        }
        new RuleAssignOutputElements(false).execute(plan, metadata, capabilitiesFinder, null, AnalysisRecord.createNonRecordingRecord(), context);
        projected = (List<? extends Expression>) indNode.getProperty(Info.OUTPUT_COLS);
    }
    if (!hasHint) {
        // require no lobs
        for (Expression ex : projected) {
            if (DataTypeManager.isLOB(ex.getClass())) {
                return false;
            }
        }
        // old optimizer tests had no buffermanager
        if (context.getBufferManager() == null) {
            return false;
        }
        if (makeDep != null && makeDep.getMax() != null) {
            // if the user specifies a max, it's best to just use a regular dependent join
            return false;
        }
    }
    /*
    	 * check to see how far the access node can be raised 
    	 */
    PlanNode tempAccess = NodeFactory.getNewNode(NodeConstants.Types.ACCESS);
    // $NON-NLS-1$
    GroupSymbol gs = RulePlaceAccess.recontextSymbol(new GroupSymbol("TEIID_TEMP"), context.getGroups());
    gs.setDefinition(null);
    tempAccess.addGroup(gs);
    tempAccess.setProperty(Info.MODEL_ID, modelID);
    indNode.addAsParent(tempAccess);
    PlanNode originalSource = sourceNode;
    sourceNode = originalSource.clone();
    // more deeply clone
    if (sourceNode.hasCollectionProperty(Info.ACCESS_PATTERNS)) {
        sourceNode.setProperty(Info.ACCESS_PATTERNS, new ArrayList<AccessPattern>((List) sourceNode.getProperty(Info.ACCESS_PATTERNS)));
    }
    if (sourceNode.hasCollectionProperty(Info.CONFORMED_SOURCES)) {
        sourceNode.setProperty(Info.CONFORMED_SOURCES, new LinkedHashSet<Object>((Set) sourceNode.getProperty(Info.CONFORMED_SOURCES)));
    }
    originalSource.addAsParent(sourceNode);
    boolean raised = false;
    boolean moreProcessing = false;
    boolean first = true;
    while (sourceNode.getParent() != null && RuleRaiseAccess.raiseAccessNode(sourceNode, sourceNode, metadata, capabilitiesFinder, true, null, context) != null) {
        raised = true;
        if (first) {
            // raising over join required
            first = false;
            continue;
        }
        switch(sourceNode.getFirstChild().getType()) {
            case NodeConstants.Types.PROJECT:
                // TODO: check for correlated subqueries
                if (sourceNode.getFirstChild().hasBooleanProperty(Info.HAS_WINDOW_FUNCTIONS)) {
                    moreProcessing = true;
                }
                break;
            case NodeConstants.Types.SORT:
            case NodeConstants.Types.DUP_REMOVE:
            case NodeConstants.Types.GROUP:
            case NodeConstants.Types.SELECT:
            case NodeConstants.Types.TUPLE_LIMIT:
            case NodeConstants.Types.JOIN:
                moreProcessing = true;
                break;
        }
    }
    if (!raised) {
        tempAccess.getParent().replaceChild(tempAccess, tempAccess.getFirstChild());
        sourceNode.getParent().replaceChild(sourceNode, sourceNode.getFirstChild());
        return false;
    }
    if (!moreProcessing && !hasHint) {
        // restore the plan
        if (sourceNode.getParent() != null) {
            sourceNode.getParent().replaceChild(sourceNode, sourceNode.getFirstChild());
        } else {
            sourceNode.removeAllChildren();
        }
        return false;
    }
    originalSource.getParent().replaceChild(originalSource, originalSource.getFirstChild());
    // all the references to any groups from this join have to changed over to the new group
    // and we need to insert a source/project node to turn this into a proper plan
    PlanNode project = NodeFactory.getNewNode(NodeConstants.Types.PROJECT);
    PlanNode source = NodeFactory.getNewNode(NodeConstants.Types.SOURCE);
    source.addGroup(gs);
    project.setProperty(Info.OUTPUT_COLS, projected);
    project.setProperty(Info.PROJECT_COLS, projected);
    Set<GroupSymbol> newGroups = Collections.singleton(gs);
    ArrayList<ElementSymbol> virtualSymbols = new ArrayList<ElementSymbol>(projected.size());
    for (int i = 0; i < projected.size(); i++) {
        // $NON-NLS-1$
        ElementSymbol es = new ElementSymbol("col" + (i + 1));
        Expression ex = projected.get(i);
        es.setType(ex.getType());
        virtualSymbols.add(es);
        // TODO: set a metadata id from either side
        if (ex instanceof ElementSymbol) {
            es.setMetadataID(((ElementSymbol) ex).getMetadataID());
        }
    }
    List<ElementSymbol> newCols = RulePushAggregates.defineNewGroup(gs, virtualSymbols, metadata);
    SymbolMap symbolMap = SymbolMap.createSymbolMap(newCols, projected);
    Map<Expression, ElementSymbol> inverse = symbolMap.inserseMapping();
    // TODO: the util logic should handle multiple groups
    for (GroupSymbol group : indNode.getGroups()) {
        FrameUtil.convertFrame(joinNode, group, newGroups, inverse, metadata);
    }
    // add the source a new group for the join
    indNode.addAsParent(source);
    // convert the lower plan into a subplan
    // it needs to be rooted by a project - a view isn't really needed
    indNode.removeFromParent();
    project.addFirstChild(indNode);
    // run the remaining rules against the subplan
    RuleStack ruleCopy = rules.clone();
    RuleChooseDependent ruleChooseDependent = new RuleChooseDependent();
    ruleChooseDependent.traditionalOnly = true;
    ruleCopy.push(ruleChooseDependent);
    if (indNode.getType() == NodeConstants.Types.ACCESS) {
        PlanNode root = RuleRaiseAccess.raiseAccessNode(project, indNode, metadata, capabilitiesFinder, true, null, context);
        if (root != project) {
            project = root;
        }
    }
    // fully plan the sub-plan with the remaining rules
    project = rules.getPlanner().executeRules(ruleCopy, project);
    source.setProperty(Info.SYMBOL_MAP, symbolMap);
    source.setProperty(Info.SUB_PLAN, project);
    return true;
}
Also used : ElementSymbol(org.teiid.query.sql.symbol.ElementSymbol) LinkedHashSet(java.util.LinkedHashSet) Set(java.util.Set) ArrayList(java.util.ArrayList) SymbolMap(org.teiid.query.sql.util.SymbolMap) PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) AccessPattern(org.teiid.query.resolver.util.AccessPattern) Expression(org.teiid.query.sql.symbol.Expression) GroupSymbol(org.teiid.query.sql.symbol.GroupSymbol) ArrayList(java.util.ArrayList) List(java.util.List) RuleStack(org.teiid.query.optimizer.relational.RuleStack)

Example 23 with SymbolMap

use of org.teiid.query.sql.util.SymbolMap in project teiid by teiid.

the class RuleCollapseSource method buildQuery.

void buildQuery(PlanNode accessRoot, PlanNode node, Query query, CommandContext context, CapabilitiesFinder capFinder) throws QueryMetadataException, TeiidComponentException, QueryPlannerException {
    QueryMetadataInterface metadata = context.getMetadata();
    // visit source and join nodes as they appear
    Object modelID = RuleRaiseAccess.getModelIDFromAccess(accessRoot, metadata);
    switch(node.getType()) {
        case NodeConstants.Types.JOIN:
            {
                prepareSubqueries(node.getSubqueryContainers());
                JoinType joinType = (JoinType) node.getProperty(NodeConstants.Info.JOIN_TYPE);
                List<Criteria> crits = (List<Criteria>) node.getProperty(NodeConstants.Info.JOIN_CRITERIA);
                if (crits == null || crits.isEmpty()) {
                    crits = new ArrayList<Criteria>();
                } else {
                    RuleChooseJoinStrategy.filterOptionalCriteria(crits, false);
                    if (crits.isEmpty() && joinType == JoinType.JOIN_INNER) {
                        joinType = JoinType.JOIN_CROSS;
                    }
                }
                PlanNode left = node.getFirstChild();
                PlanNode right = node.getLastChild();
                /* special handling is needed to determine criteria placement.
                 * 
                 * if the join is a left outer join, criteria from the right side will be added to the on clause
                 */
                Criteria savedCriteria = null;
                buildQuery(accessRoot, left, query, context, capFinder);
                if (joinType == JoinType.JOIN_LEFT_OUTER) {
                    savedCriteria = query.getCriteria();
                    query.setCriteria(null);
                }
                buildQuery(accessRoot, right, query, context, capFinder);
                if (joinType == JoinType.JOIN_LEFT_OUTER) {
                    moveWhereClauseIntoOnClause(query, crits);
                    query.setCriteria(savedCriteria);
                }
                if (joinType == JoinType.JOIN_LEFT_OUTER || joinType == JoinType.JOIN_FULL_OUTER) {
                    boolean subqueryOn = CapabilitiesUtil.supports(Capability.CRITERIA_ON_SUBQUERY, modelID, metadata, capFinder);
                    if (!subqueryOn) {
                        for (SubqueryContainer<?> subqueryContainer : ValueIteratorProviderCollectorVisitor.getValueIteratorProviders(crits)) {
                            if (subqueryContainer instanceof Evaluatable && subqueryContainer.getCommand().getCorrelatedReferences() == null) {
                                ((Evaluatable) subqueryContainer).setShouldEvaluate(true);
                            } else {
                                // $NON-NLS-1$
                                throw new AssertionError("On clause not expected to contain non-evaluatable subqueries");
                            }
                        }
                    }
                }
                // Get last two clauses added to the FROM and combine them into a JoinPredicate
                From from = query.getFrom();
                List<FromClause> clauses = from.getClauses();
                int lastClause = clauses.size() - 1;
                FromClause clause1 = clauses.get(lastClause - 1);
                FromClause clause2 = clauses.get(lastClause);
                // so this may not be needed moving forward
                if (!joinType.isOuter() && !CapabilitiesUtil.supports(Capability.QUERY_FROM_JOIN_INNER, modelID, metadata, capFinder)) {
                    joinType = JoinType.JOIN_LEFT_OUTER;
                    if (!crits.isEmpty()) {
                        if (!useLeftOuterJoin(query, metadata, crits, right.getGroups())) {
                            if (!useLeftOuterJoin(query, metadata, crits, left.getGroups())) {
                                // $NON-NLS-1$
                                throw new AssertionError("Could not convert inner to outer join.");
                            }
                            FromClause temp = clause1;
                            clause1 = clause2;
                            clause2 = temp;
                        }
                    }
                }
                // correct the criteria or the join type if necessary
                if (joinType != JoinType.JOIN_CROSS && crits.isEmpty()) {
                    crits.add(QueryRewriter.TRUE_CRITERIA);
                } else if (joinType == JoinType.JOIN_CROSS && !crits.isEmpty()) {
                    joinType = JoinType.JOIN_INNER;
                }
                JoinPredicate jp = new JoinPredicate(clause1, clause2, joinType, crits);
                // Replace last two clauses with new predicate
                clauses.remove(lastClause);
                clauses.set(lastClause - 1, jp);
                return;
            }
        case NodeConstants.Types.SOURCE:
            {
                boolean pushedTableProcedure = false;
                GroupSymbol symbol = node.getGroups().iterator().next();
                if (node.hasBooleanProperty(Info.INLINE_VIEW)) {
                    PlanNode child = node.getFirstChild();
                    QueryCommand newQuery = createQuery(context, capFinder, accessRoot, child);
                    // ensure that the group is consistent
                    SubqueryFromClause sfc = new SubqueryFromClause(symbol, newQuery);
                    SymbolMap map = (SymbolMap) node.getProperty(NodeConstants.Info.CORRELATED_REFERENCES);
                    if (map != null) {
                        ExpressionMappingVisitor visitor = new RuleMergeCriteria.ReferenceReplacementVisitor(map);
                        DeepPostOrderNavigator.doVisit(newQuery, visitor);
                        sfc.setLateral(true);
                    }
                    query.getFrom().addClause(sfc);
                    // ensure that the column names are consistent
                    Query q = newQuery.getProjectedQuery();
                    List<Expression> expressions = q.getSelect().getSymbols();
                    List<Expression> outputCols = (List<Expression>) node.getProperty(NodeConstants.Info.OUTPUT_COLS);
                    Map<Expression, String> corrected = null;
                    for (int i = 0; i < outputCols.size(); i++) {
                        Expression ex = expressions.get(i);
                        Expression expected = outputCols.get(i);
                        String name = Symbol.getShortName(expected);
                        if (!name.equals(Symbol.getShortName(ex))) {
                            expressions.set(i, new AliasSymbol(name, SymbolMap.getExpression(ex)));
                            corrected = new HashMap<Expression, String>();
                            corrected.put(ex, name);
                        }
                    }
                    if (corrected != null && newQuery.getOrderBy() != null) {
                        for (OrderByItem item : newQuery.getOrderBy().getOrderByItems()) {
                            String name = corrected.get(item.getSymbol());
                            if (name != null) {
                                item.setSymbol(new AliasSymbol(name, SymbolMap.getExpression(item.getSymbol())));
                            }
                        }
                    }
                    // so we'll unwrap that here
                    if (newQuery instanceof Query) {
                        q = (Query) newQuery;
                        if (q.getFrom() != null && q.getFrom().getClauses().size() == 1 && q.getFrom().getClauses().get(0) instanceof SubqueryFromClause) {
                            SubqueryFromClause nested = (SubqueryFromClause) q.getFrom().getClauses().get(0);
                            if (nested.getCommand() instanceof StoredProcedure) {
                                sfc.setCommand(nested.getCommand());
                            }
                        }
                    }
                    return;
                }
                // handle lateral join of a procedure
                Command command = (Command) node.getProperty(NodeConstants.Info.VIRTUAL_COMMAND);
                if (command instanceof StoredProcedure) {
                    StoredProcedure storedProcedure = (StoredProcedure) command;
                    storedProcedure.setPushedInQuery(true);
                    SubqueryFromClause subqueryFromClause = new SubqueryFromClause(symbol, storedProcedure);
                    // TODO: it would be better to directly add
                    query.getFrom().addClause(subqueryFromClause);
                    pushedTableProcedure = true;
                }
                PlanNode subPlan = (PlanNode) node.getProperty(Info.SUB_PLAN);
                if (subPlan != null) {
                    Map<GroupSymbol, PlanNode> subPlans = (Map<GroupSymbol, PlanNode>) accessRoot.getProperty(Info.SUB_PLANS);
                    if (subPlans == null) {
                        subPlans = new HashMap<GroupSymbol, PlanNode>();
                        accessRoot.setProperty(Info.SUB_PLANS, subPlans);
                    }
                    subPlans.put(symbol, subPlan);
                }
                if (!pushedTableProcedure) {
                    query.getFrom().addGroup(symbol);
                }
                break;
            }
    }
    for (PlanNode childNode : node.getChildren()) {
        buildQuery(accessRoot, childNode, query, context, capFinder);
    }
    switch(node.getType()) {
        case NodeConstants.Types.SELECT:
            {
                Criteria crit = (Criteria) node.getProperty(NodeConstants.Info.SELECT_CRITERIA);
                prepareSubqueries(node.getSubqueryContainers());
                if (!node.hasBooleanProperty(NodeConstants.Info.IS_HAVING)) {
                    query.setCriteria(CompoundCriteria.combineCriteria(query.getCriteria(), crit));
                } else {
                    query.setHaving(CompoundCriteria.combineCriteria(query.getHaving(), crit));
                }
                break;
            }
        case NodeConstants.Types.SORT:
            {
                prepareSubqueries(node.getSubqueryContainers());
                processOrderBy(node, query, modelID, context, capFinder);
                break;
            }
        case NodeConstants.Types.DUP_REMOVE:
            {
                boolean distinct = true;
                PlanNode grouping = NodeEditor.findNodePreOrder(node.getFirstChild(), NodeConstants.Types.GROUP, NodeConstants.Types.SOURCE);
                if (grouping != null) {
                    List groups = (List) grouping.getProperty(NodeConstants.Info.GROUP_COLS);
                    if (groups == null || groups.isEmpty()) {
                        distinct = false;
                    }
                }
                query.getSelect().setDistinct(distinct);
                break;
            }
        case NodeConstants.Types.GROUP:
            {
                List groups = (List) node.getProperty(NodeConstants.Info.GROUP_COLS);
                if (groups != null && !groups.isEmpty()) {
                    query.setGroupBy(new GroupBy(groups));
                    if (node.hasBooleanProperty(Info.ROLLUP)) {
                        query.getGroupBy().setRollup(true);
                    }
                }
                break;
            }
        case NodeConstants.Types.TUPLE_LIMIT:
            {
                processLimit(node, query, metadata);
                break;
            }
    }
}
Also used : HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) List(java.util.List) ArrayList(java.util.ArrayList) QueryMetadataInterface(org.teiid.query.metadata.QueryMetadataInterface) SymbolMap(org.teiid.query.sql.util.SymbolMap) ExpressionMappingVisitor(org.teiid.query.sql.visitor.ExpressionMappingVisitor) Evaluatable(org.teiid.query.sql.lang.SubqueryContainer.Evaluatable) Map(java.util.Map) HashMap(java.util.HashMap) SymbolMap(org.teiid.query.sql.util.SymbolMap)

Example 24 with SymbolMap

use of org.teiid.query.sql.util.SymbolMap in project teiid by teiid.

the class RuleDecomposeJoin method buildUnion.

private PlanNode buildUnion(PlanNode unionNode, PlanNode otherSide, List<Criteria> criteria, List<int[]> matches, List<PlanNode> branches, List<PlanNode> otherBranches, JoinType joinType) {
    SymbolMap symbolMap = (SymbolMap) unionNode.getParent().getProperty(Info.SYMBOL_MAP);
    SymbolMap otherSymbolMap = (SymbolMap) otherSide.getProperty(Info.SYMBOL_MAP);
    List<PlanNode> joins = new LinkedList<PlanNode>();
    for (int i = 0; i < matches.size(); i++) {
        int[] is = matches.get(i);
        PlanNode branch = branches.get(is[0]);
        PlanNode branchSource = createSource(unionNode.getParent().getGroups().iterator().next(), branch, symbolMap);
        PlanNode otherBranch = otherBranches.get(is[1]);
        PlanNode otherBranchSource = createSource(otherSide.getGroups().iterator().next(), otherBranch, otherSymbolMap);
        PlanNode newJoinNode = NodeFactory.getNewNode(NodeConstants.Types.JOIN);
        newJoinNode.addLastChild(branchSource);
        newJoinNode.addLastChild(otherBranchSource);
        newJoinNode.setProperty(Info.JOIN_STRATEGY, JoinStrategyType.NESTED_LOOP);
        newJoinNode.setProperty(Info.JOIN_TYPE, joinType);
        newJoinNode.setProperty(Info.JOIN_CRITERIA, LanguageObject.Util.deepClone(criteria, Criteria.class));
        newJoinNode.addGroups(branchSource.getGroups());
        newJoinNode.addGroups(otherBranchSource.getGroups());
        PlanNode projectPlanNode = NodeFactory.getNewNode(NodeConstants.Types.PROJECT);
        newJoinNode.addAsParent(projectPlanNode);
        Select allSymbols = new Select(symbolMap.getKeys());
        allSymbols.addSymbols(otherSymbolMap.getKeys());
        if (i == 0) {
            QueryRewriter.makeSelectUnique(allSymbols, false);
        }
        projectPlanNode.setProperty(NodeConstants.Info.PROJECT_COLS, allSymbols.getSymbols());
        projectPlanNode.addGroups(newJoinNode.getGroups());
        joins.add(projectPlanNode);
    }
    PlanNode newUnion = RulePlanUnions.buildUnionTree(unionNode, joins);
    return newUnion;
}
Also used : PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) Select(org.teiid.query.sql.lang.Select) SymbolMap(org.teiid.query.sql.util.SymbolMap) Criteria(org.teiid.query.sql.lang.Criteria) LinkedList(java.util.LinkedList)

Example 25 with SymbolMap

use of org.teiid.query.sql.util.SymbolMap in project teiid by teiid.

the class RuleDecomposeJoin method decomposeJoin.

public PlanNode decomposeJoin(PlanNode joinNode, PlanNode root, QueryMetadataInterface metadata, CommandContext context) throws TeiidComponentException, QueryPlannerException {
    if (joinNode.getParent() == null) {
        // already processed
        return root;
    }
    JoinType joinType = (JoinType) joinNode.getProperty(Info.JOIN_TYPE);
    if (joinType == JoinType.JOIN_ANTI_SEMI || joinType == JoinType.JOIN_CROSS) {
        return root;
    }
    PlanNode left = joinNode.getFirstChild();
    while (left.getType() != NodeConstants.Types.SOURCE) {
        if (left.getType() == NodeConstants.Types.SELECT && left.hasBooleanProperty(Info.IS_PHANTOM)) {
            left = left.getFirstChild();
        } else {
            return root;
        }
    }
    Map<ElementSymbol, List<Set<Constant>>> partitionInfo = (Map<ElementSymbol, List<Set<Constant>>>) left.getProperty(Info.PARTITION_INFO);
    if (partitionInfo == null) {
        return root;
    }
    PlanNode unionNode = left.getFirstChild();
    if (unionNode.getType() != NodeConstants.Types.SET_OP) {
        return root;
    }
    PlanNode right = joinNode.getLastChild();
    while (right.getType() != NodeConstants.Types.SOURCE) {
        if (right.getType() == NodeConstants.Types.SELECT && right.hasBooleanProperty(Info.IS_PHANTOM)) {
            right = right.getFirstChild();
        } else {
            return root;
        }
    }
    Map<ElementSymbol, List<Set<Constant>>> rightPartionInfo = (Map<ElementSymbol, List<Set<Constant>>>) right.getProperty(Info.PARTITION_INFO);
    if (rightPartionInfo == null) {
        return root;
    }
    List<Criteria> criteria = (List<Criteria>) joinNode.getProperty(Info.JOIN_CRITERIA);
    List<Expression> expr = new ArrayList<Expression>();
    List<Expression> exprOther = new ArrayList<Expression>();
    RuleChooseJoinStrategy.separateCriteria(unionNode.getParent().getGroups(), right.getGroups(), expr, exprOther, criteria, new LinkedList<Criteria>());
    // if implicit, we assume that partitions match
    ElementSymbol es = getImplicitPartitionColumn(metadata, left);
    ElementSymbol esOther = getImplicitPartitionColumn(metadata, right);
    if (es != null && esOther != null && getEffectiveModelId(metadata, es.getGroupSymbol()) == getEffectiveModelId(metadata, esOther.getGroupSymbol())) {
        expr.add(es);
        exprOther.add(esOther);
    }
    if (expr.isEmpty()) {
        // no equi-join
        return root;
    }
    List<int[]> matches = findMatches(partitionInfo, rightPartionInfo, expr, exprOther);
    if (matches == null) {
        // no non-overlapping partitions
        return root;
    }
    int branchSize = partitionInfo.values().iterator().next().size();
    int otherBranchSize = rightPartionInfo.values().iterator().next().size();
    if (matches.isEmpty()) {
        if (joinType == JoinType.JOIN_INNER || joinType == JoinType.JOIN_SEMI) {
            // no matches mean that we can just insert a null node (false criteria) and be done with it
            PlanNode critNode = NodeFactory.getNewNode(NodeConstants.Types.SELECT);
            critNode.setProperty(Info.SELECT_CRITERIA, QueryRewriter.FALSE_CRITERIA);
            unionNode.addAsParent(critNode);
        } else if (joinType == JoinType.JOIN_LEFT_OUTER) {
            joinNode.getParent().replaceChild(joinNode, left);
        } else if (joinType == JoinType.JOIN_FULL_OUTER) {
            joinNode.setProperty(Info.JOIN_CRITERIA, QueryRewriter.FALSE_CRITERIA);
        }
        return root;
    }
    List<PlanNode> branches = new ArrayList<PlanNode>();
    // TODO: find union children from RulePushAggregates
    RulePushSelectCriteria.collectUnionChildren(unionNode, branches);
    if (branches.size() != branchSize) {
        // sanity check
        return root;
    }
    List<PlanNode> otherBranches = new ArrayList<PlanNode>();
    RulePushSelectCriteria.collectUnionChildren(right.getFirstChild(), otherBranches);
    if (otherBranches.size() != otherBranchSize) {
        // sanity check
        return root;
    }
    PlanNode newUnion = buildUnion(unionNode, right, criteria, matches, branches, otherBranches, joinType);
    GroupSymbol leftGroup = left.getGroups().iterator().next();
    PlanNode view = rebuild(leftGroup, joinNode, newUnion, metadata, context, left, right);
    // preserve the model of the virtual group as we'll look for this when checking for implicit behavior
    ((TempMetadataID) (view.getGroups().iterator().next().getMetadataID())).getTableData().setModel(getEffectiveModelId(metadata, leftGroup));
    SymbolMap symbolmap = (SymbolMap) view.getProperty(Info.SYMBOL_MAP);
    HashMap<ElementSymbol, List<Set<Constant>>> newPartitionInfo = new LinkedHashMap<ElementSymbol, List<Set<Constant>>>();
    Map<Expression, ElementSymbol> inverse = symbolmap.inserseMapping();
    for (int[] match : matches) {
        updatePartitionInfo(partitionInfo, matches, inverse, newPartitionInfo, match[0]);
        updatePartitionInfo(rightPartionInfo, matches, inverse, newPartitionInfo, match[1]);
    }
    view.setProperty(Info.PARTITION_INFO, newPartitionInfo);
    // since we've created a new union node, there's a chance we can decompose again
    if (view.getParent().getType() == NodeConstants.Types.JOIN) {
        return decomposeJoin(view.getParent(), root, metadata, context);
    }
    return root;
}
Also used : ElementSymbol(org.teiid.query.sql.symbol.ElementSymbol) Set(java.util.Set) Constant(org.teiid.query.sql.symbol.Constant) ArrayList(java.util.ArrayList) JoinType(org.teiid.query.sql.lang.JoinType) SymbolMap(org.teiid.query.sql.util.SymbolMap) Criteria(org.teiid.query.sql.lang.Criteria) LinkedHashMap(java.util.LinkedHashMap) PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) Expression(org.teiid.query.sql.symbol.Expression) GroupSymbol(org.teiid.query.sql.symbol.GroupSymbol) ArrayList(java.util.ArrayList) LinkedList(java.util.LinkedList) List(java.util.List) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) Map(java.util.Map) SymbolMap(org.teiid.query.sql.util.SymbolMap)

Aggregations

SymbolMap (org.teiid.query.sql.util.SymbolMap)56 PlanNode (org.teiid.query.optimizer.relational.plantree.PlanNode)37 ElementSymbol (org.teiid.query.sql.symbol.ElementSymbol)28 Expression (org.teiid.query.sql.symbol.Expression)28 GroupSymbol (org.teiid.query.sql.symbol.GroupSymbol)21 ArrayList (java.util.ArrayList)16 List (java.util.List)15 Criteria (org.teiid.query.sql.lang.Criteria)10 LinkedList (java.util.LinkedList)8 Map (java.util.Map)8 HashMap (java.util.HashMap)6 Constant (org.teiid.query.sql.symbol.Constant)6 LinkedHashSet (java.util.LinkedHashSet)5 QueryPlannerException (org.teiid.api.exception.query.QueryPlannerException)5 CompareCriteria (org.teiid.query.sql.lang.CompareCriteria)5 OrderBy (org.teiid.query.sql.lang.OrderBy)5 HashSet (java.util.HashSet)4 ExpressionMappingVisitor (org.teiid.query.sql.visitor.ExpressionMappingVisitor)4 LinkedHashMap (java.util.LinkedHashMap)3 Set (java.util.Set)3