Search in sources :

Example 81 with PlanNode

use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.

the class RuleRemoveOptionalJoins method execute.

public PlanNode execute(PlanNode plan, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, RuleStack rules, AnalysisRecord analysisRecord, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
    List<PlanNode> projectNodes = NodeEditor.findAllNodes(plan, NodeConstants.Types.PROJECT);
    HashSet<PlanNode> skipNodes = new HashSet<PlanNode>();
    for (PlanNode projectNode : projectNodes) {
        if (projectNode.getChildCount() == 0 || projectNode.getProperty(NodeConstants.Info.INTO_GROUP) != null) {
            continue;
        }
        PlanNode groupNode = NodeEditor.findNodePreOrder(projectNode, NodeConstants.Types.GROUP, NodeConstants.Types.SOURCE | NodeConstants.Types.JOIN);
        if (groupNode != null) {
            projectNode = groupNode;
        }
        Set<GroupSymbol> requiredForOptional = getRequiredGroupSymbols(projectNode.getFirstChild());
        boolean done = false;
        while (!done) {
            done = true;
            List<PlanNode> joinNodes = NodeEditor.findAllNodes(projectNode, NodeConstants.Types.JOIN, NodeConstants.Types.SOURCE);
            for (PlanNode planNode : joinNodes) {
                if (skipNodes.contains(planNode)) {
                    continue;
                }
                if (!planNode.getExportedCorrelatedReferences().isEmpty()) {
                    skipNodes.add(planNode);
                    continue;
                }
                Set<GroupSymbol> required = getRequiredGroupSymbols(planNode);
                List<PlanNode> removed = removeJoin(required, requiredForOptional, planNode, planNode.getFirstChild(), analysisRecord, metadata);
                if (removed != null) {
                    skipNodes.addAll(removed);
                    done = false;
                    continue;
                }
                removed = removeJoin(required, requiredForOptional, planNode, planNode.getLastChild(), analysisRecord, metadata);
                if (removed != null) {
                    skipNodes.addAll(removed);
                    done = false;
                }
            }
        }
    }
    return plan;
}
Also used : PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) GroupSymbol(org.teiid.query.sql.symbol.GroupSymbol) HashSet(java.util.HashSet)

Example 82 with PlanNode

use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.

the class RuleRemoveOptionalJoins method removeJoin.

/**
 * remove the optional node if possible
 * @throws QueryPlannerException
 * @throws TeiidComponentException
 * @throws QueryMetadataException
 */
private List<PlanNode> removeJoin(Set<GroupSymbol> required, Set<GroupSymbol> requiredForOptional, PlanNode joinNode, PlanNode optionalNode, AnalysisRecord record, QueryMetadataInterface metadata) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
    boolean correctFrame = false;
    boolean isOptional = optionalNode.hasBooleanProperty(NodeConstants.Info.IS_OPTIONAL);
    if (isOptional) {
        required = requiredForOptional;
        correctFrame = true;
    }
    if (!Collections.disjoint(optionalNode.getGroups(), required)) {
        return null;
    }
    if (isOptional) {
        // prevent bridge table removal
        HashSet<GroupSymbol> joinGroups = new HashSet<GroupSymbol>();
        PlanNode parentNode = joinNode;
        while (parentNode.getType() != NodeConstants.Types.PROJECT) {
            PlanNode current = parentNode;
            parentNode = parentNode.getParent();
            if (current.getType() != NodeConstants.Types.SELECT && current.getType() != NodeConstants.Types.JOIN) {
                continue;
            }
            Set<GroupSymbol> currentGroups = current.getGroups();
            if (current.getType() == NodeConstants.Types.JOIN) {
                currentGroups = GroupsUsedByElementsVisitor.getGroups((List<Criteria>) current.getProperty(NodeConstants.Info.JOIN_CRITERIA));
            }
            if (!Collections.disjoint(currentGroups, optionalNode.getGroups()) && !optionalNode.getGroups().containsAll(currentGroups)) {
                // we're performing a join
                boolean wasEmpty = joinGroups.isEmpty();
                boolean modified = joinGroups.addAll(current.getGroups());
                if (!wasEmpty && modified) {
                    return null;
                }
            }
        }
    }
    JoinType jt = (JoinType) joinNode.getProperty(NodeConstants.Info.JOIN_TYPE);
    boolean usesKey = false;
    boolean isRight = optionalNode == joinNode.getLastChild();
    if (!isOptional && (jt == JoinType.JOIN_INNER || (jt == JoinType.JOIN_LEFT_OUTER && isRight))) {
        usesKey = isOptionalUsingKey(joinNode, optionalNode, metadata, isRight);
    }
    if (!isOptional && !usesKey && (jt != JoinType.JOIN_LEFT_OUTER || !isRight || useNonDistinctRows(joinNode.getParent()))) {
        return null;
    }
    // remove the parent node and move the sibling node upward
    PlanNode parentNode = joinNode.getParent();
    joinNode.removeChild(optionalNode);
    joinNode.getFirstChild().setProperty(NodeConstants.Info.OUTPUT_COLS, joinNode.getProperty(NodeConstants.Info.OUTPUT_COLS));
    NodeEditor.removeChildNode(parentNode, joinNode);
    // $NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
    joinNode.recordDebugAnnotation((isOptional ? "node was marked as optional " : "node will not affect the results"), null, "Removing join node", record, null);
    while (parentNode.getType() != NodeConstants.Types.PROJECT) {
        PlanNode current = parentNode;
        parentNode = parentNode.getParent();
        if (correctFrame) {
            if (current.getType() == NodeConstants.Types.SELECT) {
                if (!Collections.disjoint(current.getGroups(), optionalNode.getGroups())) {
                    current.getFirstChild().setProperty(NodeConstants.Info.OUTPUT_COLS, current.getProperty(NodeConstants.Info.OUTPUT_COLS));
                    NodeEditor.removeChildNode(parentNode, current);
                }
            } else if (current.getType() == NodeConstants.Types.JOIN) {
                if (!Collections.disjoint(current.getGroups(), optionalNode.getGroups())) {
                    List<Criteria> crits = (List<Criteria>) current.getProperty(NodeConstants.Info.JOIN_CRITERIA);
                    if (crits != null && !crits.isEmpty()) {
                        for (Iterator<Criteria> iterator = crits.iterator(); iterator.hasNext(); ) {
                            Criteria criteria = iterator.next();
                            if (!Collections.disjoint(GroupsUsedByElementsVisitor.getGroups(criteria), optionalNode.getGroups())) {
                                iterator.remove();
                            }
                        }
                        if (crits.isEmpty()) {
                            JoinType joinType = (JoinType) current.getProperty(NodeConstants.Info.JOIN_TYPE);
                            if (joinType == JoinType.JOIN_INNER) {
                                current.setProperty(NodeConstants.Info.JOIN_TYPE, JoinType.JOIN_CROSS);
                            }
                        }
                    }
                }
            }
        } else if (current.getType() != NodeConstants.Types.JOIN) {
            break;
        }
        if (current.getType() == NodeConstants.Types.JOIN) {
            current.getGroups().removeAll(optionalNode.getGroups());
        }
    }
    return NodeEditor.findAllNodes(optionalNode, NodeConstants.Types.JOIN);
}
Also used : PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) GroupSymbol(org.teiid.query.sql.symbol.GroupSymbol) Iterator(java.util.Iterator) JoinType(org.teiid.query.sql.lang.JoinType) ArrayList(java.util.ArrayList) LinkedList(java.util.LinkedList) List(java.util.List) Criteria(org.teiid.query.sql.lang.Criteria) HashSet(java.util.HashSet)

Example 83 with PlanNode

use of org.teiid.query.optimizer.relational.plantree.PlanNode 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 84 with PlanNode

use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.

the class RuleChooseDependent method createDependentSetNode.

static PlanNode createDependentSetNode(String id, List<DependentSetCriteria.AttributeComparison> expressions) {
    DependentSetCriteria crit = createDependentSetCriteria(id, expressions);
    PlanNode selectNode = RelationalPlanner.createSelectNode(crit, false);
    selectNode.setProperty(NodeConstants.Info.IS_DEPENDENT_SET, Boolean.TRUE);
    return selectNode;
}
Also used : DependentSetCriteria(org.teiid.query.sql.lang.DependentSetCriteria) PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode)

Example 85 with PlanNode

use of org.teiid.query.optimizer.relational.plantree.PlanNode in project teiid by teiid.

the class RuleChooseDependent method handleDuplicate.

private void handleDuplicate(PlanNode joinNode, boolean isLeft, List independentExpressions, List dependentExpressions) {
    Map<Expression, Integer> seen = new HashMap<Expression, Integer>();
    for (int i = 0; i < dependentExpressions.size(); i++) {
        Expression ex = (Expression) dependentExpressions.get(i);
        Integer index = seen.get(ex);
        if (index == null) {
            seen.put(ex, i);
        } else {
            Expression e1 = (Expression) independentExpressions.get(i);
            Expression e2 = (Expression) independentExpressions.get(index);
            CompareCriteria cc = new CompareCriteria(e1, CompareCriteria.EQ, e2);
            PlanNode impliedCriteria = RelationalPlanner.createSelectNode(cc, false);
            if (isLeft) {
                joinNode.getLastChild().addAsParent(impliedCriteria);
            } else {
                joinNode.getFirstChild().addAsParent(impliedCriteria);
            }
            independentExpressions.remove(i);
            dependentExpressions.remove(i);
            i--;
        }
    }
}
Also used : AtomicInteger(java.util.concurrent.atomic.AtomicInteger) PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) Expression(org.teiid.query.sql.symbol.Expression) HashMap(java.util.HashMap) CompareCriteria(org.teiid.query.sql.lang.CompareCriteria)

Aggregations

PlanNode (org.teiid.query.optimizer.relational.plantree.PlanNode)204 Expression (org.teiid.query.sql.symbol.Expression)50 GroupSymbol (org.teiid.query.sql.symbol.GroupSymbol)50 ArrayList (java.util.ArrayList)47 List (java.util.List)43 SymbolMap (org.teiid.query.sql.util.SymbolMap)42 ElementSymbol (org.teiid.query.sql.symbol.ElementSymbol)36 Criteria (org.teiid.query.sql.lang.Criteria)35 LinkedList (java.util.LinkedList)24 CompareCriteria (org.teiid.query.sql.lang.CompareCriteria)24 Test (org.junit.Test)22 HashSet (java.util.HashSet)17 JoinType (org.teiid.query.sql.lang.JoinType)17 LinkedHashSet (java.util.LinkedHashSet)16 CompoundCriteria (org.teiid.query.sql.lang.CompoundCriteria)12 DependentSetCriteria (org.teiid.query.sql.lang.DependentSetCriteria)12 QueryPlannerException (org.teiid.api.exception.query.QueryPlannerException)11 LanguageObject (org.teiid.query.sql.LanguageObject)11 OrderBy (org.teiid.query.sql.lang.OrderBy)10 IsNullCriteria (org.teiid.query.sql.lang.IsNullCriteria)9