Search in sources :

Example 61 with PlanNode

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

the class RuleAssignOutputElements method execute.

/**
 * Execute the rule.  This rule is executed exactly once during every planning
 * call.  The plan is modified in place - only properties are manipulated, structure
 * is unchanged.
 * @param plan The plan to execute rule on
 * @param metadata The metadata interface
 * @param rules The rule stack, not modified
 * @return The updated plan
 */
public PlanNode execute(PlanNode plan, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, RuleStack rules, AnalysisRecord analysisRecord, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
    // Record project node output columns in top node
    PlanNode projectNode = NodeEditor.findNodePreOrder(plan, NodeConstants.Types.PROJECT);
    if (projectNode == null) {
        return plan;
    }
    List<Expression> projectCols = (List<Expression>) projectNode.getProperty(NodeConstants.Info.PROJECT_COLS);
    assignOutputElements(plan, projectCols, metadata, capFinder, rules, analysisRecord, context);
    return plan;
}
Also used : PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) Expression(org.teiid.query.sql.symbol.Expression) List(java.util.List) ArrayList(java.util.ArrayList)

Example 62 with PlanNode

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

the class RuleAssignOutputElements method assignOutputElements.

/**
 * <p>Assign the output elements at a particular node and recurse the tree.  The
 * outputElements needed from above the node have been collected in
 * outputElements.</p>
 *
 * <p>SOURCE nodes:  If we find a SOURCE node, this must define the top
 * of a virtual group.  Physical groups can be identified by ACCESS nodes
 * at this point in the planning stage.  So, we filter the virtual elements
 * in the virtual source based on the required output elements.</p>
 *
 * <p>SET_OP nodes:  If we hit a SET_OP node, this must be a union.  Unions
 * require a lot of special care.  Unions have many branches and the projected
 * elements in each branch are "equivalent" in terms of nodes above the union.
 * This means that any filtering must occur in an identical way in all branches
 * of a union.</p>
 *
 * @param root Node to assign
 * @param outputElements Output elements needed for this node
 * @param metadata Metadata implementation
 */
private void assignOutputElements(PlanNode root, List<Expression> outputElements, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, RuleStack rules, AnalysisRecord analysisRecord, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
    int nodeType = root.getType();
    // Update this node's output columns based on parent's columns
    List<Expression> oldOutput = (List<Expression>) root.setProperty(NodeConstants.Info.OUTPUT_COLS, outputElements);
    if (root.getChildCount() == 0) {
        // update temp access
        if (root.getType() == NodeConstants.Types.SOURCE && root.getGroups().size() == 1) {
            GroupSymbol gs = root.getGroups().iterator().next();
            if (gs.getMetadataID() instanceof TempMetadataID) {
                for (Expression ex : outputElements) {
                    if (ex instanceof ElementSymbol) {
                        Object id = ((ElementSymbol) ex).getMetadataID();
                        if (id instanceof TempMetadataID) {
                            ((TempMetadataID) id).setAccessed(true);
                        }
                    }
                }
            }
        }
        return;
    }
    switch(nodeType) {
        case NodeConstants.Types.ACCESS:
            Command command = FrameUtil.getNonQueryCommand(root);
            if (command instanceof StoredProcedure) {
                // if the access node represents a stored procedure, then we can't actually change the output symbols
                root.setProperty(NodeConstants.Info.OUTPUT_COLS, command.getProjectedSymbols());
            } else {
                ProcessorPlan plan = FrameUtil.getNestedPlan(root);
                if (plan != null && (command == null || !RelationalNodeUtil.isUpdate(command))) {
                    // nested with clauses are handled as sub plans, which have a fixed set of output symbols
                    root.setProperty(NodeConstants.Info.OUTPUT_COLS, ResolverUtil.resolveElementsInGroup(root.getGroups().iterator().next(), metadata));
                }
                if (checkSymbols) {
                    Object modelId = RuleRaiseAccess.getModelIDFromAccess(root, metadata);
                    for (Expression symbol : outputElements) {
                        if (!RuleRaiseAccess.canPushSymbol(symbol, true, modelId, metadata, capFinder, analysisRecord)) {
                            throw new QueryPlannerException(QueryPlugin.Event.TEIID30258, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30258, symbol, modelId));
                        }
                    }
                }
                if (NodeEditor.findParent(root, NodeConstants.Types.PROJECT, NodeConstants.Types.SOURCE) != null) {
                    // there's a chance that partial projection was used.  we are not a defacto project node
                    // take credit for creating anything that is not an element symbol
                    LinkedHashSet<Expression> filteredElements = new LinkedHashSet<Expression>();
                    for (Expression element : outputElements) {
                        if (element instanceof ElementSymbol) {
                            filteredElements.add(element);
                        } else {
                            filteredElements.addAll(ElementCollectorVisitor.getElements(element, false));
                        }
                    }
                    outputElements = new ArrayList<Expression>(filteredElements);
                }
            }
            assignOutputElements(root.getLastChild(), outputElements, metadata, capFinder, rules, analysisRecord, context);
            break;
        case NodeConstants.Types.DUP_REMOVE:
            // targeted optimization based upon swapping the dup remove for a limit 1
            // TODO: may need to also check for grouping over constants
            boolean allConstants = true;
            for (Expression ex : outputElements) {
                if (!(EvaluatableVisitor.willBecomeConstant(SymbolMap.getExpression(ex)))) {
                    allConstants = false;
                    break;
                }
            }
            if (allConstants && addLimit(rules, root, metadata, capFinder)) {
                // TODO we could more gracefully handle the !addLimit case
                PlanNode parent = root.getParent();
                if (parent != null) {
                    NodeEditor.removeChildNode(root.getParent(), root);
                    execute(parent, metadata, capFinder, rules, analysisRecord, context);
                    return;
                }
            }
        case NodeConstants.Types.SORT:
            // correct expression positions and update the unrelated flag
            OrderBy order = (OrderBy) root.getProperty(NodeConstants.Info.SORT_ORDER);
            if (order != null && (oldOutput == null || !oldOutput.equals(outputElements))) {
                outputElements = new ArrayList<Expression>(outputElements);
                boolean hasUnrelated = false;
                for (OrderByItem item : order.getOrderByItems()) {
                    int index = outputElements.indexOf(item.getSymbol());
                    if (index != -1) {
                        item.setExpressionPosition(index);
                    } else {
                        hasUnrelated = true;
                        outputElements.add(item.getSymbol());
                    }
                }
                if (!hasUnrelated) {
                    root.setProperty(NodeConstants.Info.UNRELATED_SORT, false);
                } else {
                    root.setProperty(NodeConstants.Info.UNRELATED_SORT, true);
                }
            }
            assignOutputElements(root.getLastChild(), outputElements, metadata, capFinder, rules, analysisRecord, context);
            break;
        case NodeConstants.Types.TUPLE_LIMIT:
            assignOutputElements(root.getLastChild(), outputElements, metadata, capFinder, rules, analysisRecord, context);
            break;
        case NodeConstants.Types.SOURCE:
            {
                outputElements = (List<Expression>) determineSourceOutput(root, outputElements, metadata, capFinder);
                if (!finalRun && root.getProperty(Info.PARTITION_INFO) != null && NodeEditor.findParent(root, NodeConstants.Types.JOIN) != null) {
                    GroupSymbol group = root.getGroups().iterator().next();
                    Object modelId = RuleDecomposeJoin.getEffectiveModelId(metadata, group);
                    String name = metadata.getExtensionProperty(modelId, RuleDecomposeJoin.IMPLICIT_PARTITION_COLUMN_NAME, true);
                    if (name != null) {
                        // keep projecting the implicit partitioning column through the source so that it can
                        // be used in the decomposition logic
                        ElementSymbol es = new ElementSymbol(name, group);
                        if (!outputElements.contains(es)) {
                            es.setMetadataID(metadata.getElementID(es.getName()));
                            outputElements.add(es);
                        }
                    }
                }
                root.setProperty(NodeConstants.Info.OUTPUT_COLS, outputElements);
                List<Expression> childElements = filterVirtualElements(root, outputElements, metadata);
                assignOutputElements(root.getFirstChild(), childElements, metadata, capFinder, rules, analysisRecord, context);
                break;
            }
        case NodeConstants.Types.SET_OP:
            {
                for (PlanNode childNode : root.getChildren()) {
                    PlanNode projectNode = NodeEditor.findNodePreOrder(childNode, NodeConstants.Types.PROJECT);
                    List<Expression> projectCols = (List<Expression>) projectNode.getProperty(NodeConstants.Info.PROJECT_COLS);
                    assignOutputElements(childNode, projectCols, metadata, capFinder, rules, analysisRecord, context);
                }
                break;
            }
        default:
            {
                PlanNode sortNode = null;
                if (root.getType() == NodeConstants.Types.PROJECT) {
                    GroupSymbol intoGroup = (GroupSymbol) root.getProperty(NodeConstants.Info.INTO_GROUP);
                    if (intoGroup != null) {
                        // if this is a project into, treat the nodes under the source as a new plan root
                        PlanNode intoRoot = NodeEditor.findNodePreOrder(root, NodeConstants.Types.SOURCE);
                        execute(intoRoot.getFirstChild(), metadata, capFinder, rules, analysisRecord, context);
                        return;
                    }
                    List<Expression> projectCols = outputElements;
                    sortNode = NodeEditor.findParent(root, NodeConstants.Types.SORT, NodeConstants.Types.SOURCE);
                    if (finalRun && sortNode != null && sortNode.hasBooleanProperty(NodeConstants.Info.UNRELATED_SORT)) {
                        root.getGroups().clear();
                        root.addGroups(GroupsUsedByElementsVisitor.getGroups(projectCols));
                        root.addGroups(GroupsUsedByElementsVisitor.getGroups(root.getCorrelatedReferenceElements()));
                    }
                    root.setProperty(NodeConstants.Info.PROJECT_COLS, projectCols);
                    if (root.hasBooleanProperty(Info.HAS_WINDOW_FUNCTIONS)) {
                        Set<WindowFunction> windowFunctions = getWindowFunctions(projectCols);
                        if (windowFunctions.isEmpty()) {
                            root.setProperty(Info.HAS_WINDOW_FUNCTIONS, false);
                        }
                    }
                }
                List<Expression> requiredInput = collectRequiredInputSymbols(root, metadata, capFinder);
                // targeted optimization for unnecessary aggregation
                if (root.getType() == NodeConstants.Types.GROUP && root.hasBooleanProperty(Info.IS_OPTIONAL) && NodeEditor.findParent(root, NodeConstants.Types.ACCESS) == null) {
                    PlanNode parent = removeGroupBy(root, metadata);
                    if (!root.hasCollectionProperty(Info.GROUP_COLS)) {
                        // just lob off everything under the projection
                        PlanNode project = NodeEditor.findNodePreOrder(parent, NodeConstants.Types.PROJECT);
                        project.removeAllChildren();
                    } else if (!addLimit(rules, parent, metadata, capFinder)) {
                        // $NON-NLS-1$
                        throw new AssertionError("expected limit node to be added");
                    }
                    execute(parent, metadata, capFinder, rules, analysisRecord, context);
                    return;
                }
                // Call children recursively
                if (root.getChildCount() == 1) {
                    assignOutputElements(root.getLastChild(), requiredInput, metadata, capFinder, rules, analysisRecord, context);
                    if (!finalRun && root.getType() == NodeConstants.Types.PROJECT && sortNode != null && sortNode.hasBooleanProperty(NodeConstants.Info.UNRELATED_SORT)) {
                        // if this is the initial rule run, remove unrelated order to preserve the original projection
                        OrderBy elements = (OrderBy) sortNode.getProperty(NodeConstants.Info.SORT_ORDER);
                        outputElements = new ArrayList<Expression>(outputElements);
                        for (OrderByItem item : elements.getOrderByItems()) {
                            if (item.getExpressionPosition() == -1) {
                                outputElements.remove(item.getSymbol());
                            }
                        }
                        root.setProperty(NodeConstants.Info.PROJECT_COLS, outputElements);
                    }
                } else {
                    // determine which elements go to each side of the join
                    for (PlanNode childNode : root.getChildren()) {
                        Set<GroupSymbol> filterGroups = FrameUtil.findJoinSourceNode(childNode).getGroups();
                        List<Expression> filteredElements = filterElements(requiredInput, filterGroups);
                        // Call child recursively
                        assignOutputElements(childNode, filteredElements, metadata, capFinder, rules, analysisRecord, context);
                    }
                }
            }
    }
}
Also used : ElementSymbol(org.teiid.query.sql.symbol.ElementSymbol) LinkedHashSet(java.util.LinkedHashSet) OrderBy(org.teiid.query.sql.lang.OrderBy) Set(java.util.Set) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet) TempMetadataID(org.teiid.query.metadata.TempMetadataID) ArrayList(java.util.ArrayList) StoredProcedure(org.teiid.query.sql.lang.StoredProcedure) PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) OrderByItem(org.teiid.query.sql.lang.OrderByItem) Expression(org.teiid.query.sql.symbol.Expression) Command(org.teiid.query.sql.lang.Command) GroupSymbol(org.teiid.query.sql.symbol.GroupSymbol) List(java.util.List) ArrayList(java.util.ArrayList) ProcessorPlan(org.teiid.query.processor.ProcessorPlan) QueryPlannerException(org.teiid.api.exception.query.QueryPlannerException)

Example 63 with PlanNode

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

the class RulePushLargeIn method execute.

@Override
public PlanNode execute(PlanNode plan, QueryMetadataInterface metadata, CapabilitiesFinder capabilitiesFinder, RuleStack rules, AnalysisRecord analysisRecord, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
    for (PlanNode critNode : NodeEditor.findAllNodes(plan, NodeConstants.Types.SELECT, NodeConstants.Types.ACCESS)) {
        if (critNode.hasBooleanProperty(NodeConstants.Info.IS_HAVING) || critNode.hasBooleanProperty(NodeConstants.Info.IS_PHANTOM)) {
            continue;
        }
        Criteria crit = (Criteria) critNode.getProperty(NodeConstants.Info.SELECT_CRITERIA);
        if (!(crit instanceof SetCriteria)) {
            continue;
        }
        SetCriteria setCriteria = (SetCriteria) crit;
        if (setCriteria.isNegated() || !setCriteria.isAllConstants()) {
            continue;
        }
        // we need to be directly over an access node
        PlanNode childAccess = critNode.getFirstChild();
        accessLoop: while (true) {
            switch(childAccess.getType()) {
                case NodeConstants.Types.ACCESS:
                    break accessLoop;
                case NodeConstants.Types.SELECT:
                    break;
                default:
                    break accessLoop;
            }
            childAccess = childAccess.getFirstChild();
        }
        if (childAccess.getType() != NodeConstants.Types.ACCESS) {
            continue;
        }
        // use a dummy value to test if we can raise
        critNode.setProperty(NodeConstants.Info.SELECT_CRITERIA, new SetCriteria(setCriteria.getExpression(), Collections.EMPTY_LIST));
        boolean canRaise = RuleRaiseAccess.canRaiseOverSelect(childAccess, metadata, capabilitiesFinder, critNode, analysisRecord);
        critNode.setProperty(NodeConstants.Info.SELECT_CRITERIA, crit);
        if (!canRaise) {
            continue;
        }
        // push the crit node and mark as dependent set
        critNode.getParent().replaceChild(critNode, critNode.getFirstChild());
        childAccess.addAsParent(critNode);
        RuleRaiseAccess.performRaise(plan, childAccess, critNode);
        childAccess.setProperty(NodeConstants.Info.IS_DEPENDENT_SET, true);
        childAccess.setProperty(NodeConstants.Info.EST_CARDINALITY, null);
    }
    return plan;
}
Also used : PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) SetCriteria(org.teiid.query.sql.lang.SetCriteria) Criteria(org.teiid.query.sql.lang.Criteria) SetCriteria(org.teiid.query.sql.lang.SetCriteria)

Example 64 with PlanNode

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

the class RulePushLimit method addBranchLimit.

private void addBranchLimit(PlanNode limitNode, List<PlanNode> limitNodes, QueryMetadataInterface metadata, Expression parentLimit, Expression parentOffset, PlanNode grandChild) {
    PlanNode newLimit = newLimit(limitNode);
    newLimit.setProperty(NodeConstants.Info.MAX_TUPLE_LIMIT, op(SourceSystemFunctions.ADD_OP, parentLimit, parentOffset, metadata.getFunctionLibrary()));
    grandChild.addAsParent(newLimit);
    newLimit.setProperty(NodeConstants.Info.OUTPUT_COLS, newLimit.getFirstChild().getProperty(NodeConstants.Info.OUTPUT_COLS));
    if (NodeEditor.findParent(newLimit, NodeConstants.Types.ACCESS) == null) {
        limitNodes.add(newLimit);
    }
    if (grandChild.getType() == NodeConstants.Types.SET_OP) {
        newLimit.setProperty(Info.IS_COPIED, true);
    }
    newLimit.setProperty(Info.IS_PUSHED, true);
}
Also used : PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode)

Example 65 with PlanNode

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

the class RulePushNonJoinCriteria method execute.

/**
 * Execute the rule as described in the class comments.
 * @param plan Incoming query plan, may be modified during method and may be returned from method
 * @param metadata Metadata source
 * @param rules Rules from optimizer rule stack, may be manipulated during method
 * @return Updated query plan if rule fired, else original query plan
 */
public PlanNode execute(PlanNode plan, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, RuleStack rules, AnalysisRecord analysisRecord, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
    boolean treeChanged = false;
    boolean removeCopiedFlag = false;
    boolean pushRuleRaiseNull = false;
    for (PlanNode node : NodeEditor.findAllNodes(plan, NodeConstants.Types.JOIN)) {
        List criteria = (List) node.getProperty(NodeConstants.Info.JOIN_CRITERIA);
        JoinType joinType = (JoinType) node.getProperty(NodeConstants.Info.JOIN_TYPE);
        // criteria cannot be pushed out of a full outer join clause
        if (joinType == JoinType.JOIN_FULL_OUTER || joinType == JoinType.JOIN_CROSS) {
            continue;
        }
        Iterator crits = criteria.iterator();
        while (crits.hasNext()) {
            Criteria crit = (Criteria) crits.next();
            // special case handling for true/false criteria
            if (crit.equals(QueryRewriter.FALSE_CRITERIA) || crit.equals(QueryRewriter.UNKNOWN_CRITERIA)) {
                if (joinType == JoinType.JOIN_INNER) {
                    FrameUtil.replaceWithNullNode(node);
                } else {
                    // must be a left or right outer join, replace the inner side with null
                    FrameUtil.replaceWithNullNode(JoinUtil.getInnerSideJoinNodes(node)[0]);
                    removeCopiedFlag = true;
                }
                // since a null node has been created, raise it to its highest point
                pushRuleRaiseNull = true;
                treeChanged = true;
                break;
            } else if (crit.equals(QueryRewriter.TRUE_CRITERIA)) {
                crits.remove();
                break;
            }
            if (pushCriteria(node, crit, crits, metadata)) {
                treeChanged = true;
            }
        }
        // degrade the join if there is no criteria left
        if (criteria.isEmpty() && joinType == JoinType.JOIN_INNER) {
            node.setProperty(NodeConstants.Info.JOIN_TYPE, JoinType.JOIN_CROSS);
            treeChanged = true;
        }
        if (removeCopiedFlag) {
            // allow the criteria above the join to be eligible for pushing and copying
            PlanNode parent = node.getParent();
            while (parent != null && parent.getType() == NodeConstants.Types.SELECT) {
                parent.setProperty(NodeConstants.Info.IS_COPIED, Boolean.FALSE);
                parent = parent.getParent();
            }
        }
    }
    if (treeChanged) {
        rules.push(RuleConstants.PUSH_SELECT_CRITERIA);
    }
    if (pushRuleRaiseNull) {
        rules.push(RuleConstants.RAISE_NULL);
    }
    return plan;
}
Also used : PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) Iterator(java.util.Iterator) JoinType(org.teiid.query.sql.lang.JoinType) ArrayList(java.util.ArrayList) List(java.util.List) IsNullCriteria(org.teiid.query.sql.lang.IsNullCriteria) Criteria(org.teiid.query.sql.lang.Criteria) 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