Search in sources :

Example 56 with PlanNode

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

the class NewCalculateCostUtil method estimateJoinNodeCost.

/**
 * Method estimateJoinNodeCost.
 * @param node
 * @param metadata
 */
private static void estimateJoinNodeCost(PlanNode node, QueryMetadataInterface metadata) throws QueryMetadataException, TeiidComponentException {
    Iterator<PlanNode> children = node.getChildren().iterator();
    PlanNode child1 = children.next();
    float childCost1 = child1.getCardinality();
    PlanNode child2 = children.next();
    float childCost2 = child2.getCardinality();
    if (childCost1 == UNKNOWN_VALUE || childCost2 == UNKNOWN_VALUE) {
        setCardinalityEstimate(node, null, true, metadata);
        return;
    }
    JoinType joinType = (JoinType) node.getProperty(NodeConstants.Info.JOIN_TYPE);
    List joinCriteria = (List) node.getProperty(NodeConstants.Info.JOIN_CRITERIA);
    float baseCost = childCost1 * childCost2;
    float leftPercent = 1;
    float rightPercent = 1;
    if (joinCriteria != null && !joinCriteria.isEmpty()) {
        List<Expression> leftExpressions = null;
        List<Expression> rightExpressions = null;
        List<Criteria> nonEquiJoinCriteria = null;
        if (!node.hasCollectionProperty(NodeConstants.Info.LEFT_EXPRESSIONS)) {
            Collection<GroupSymbol> leftGroups = child1.getGroups();
            Collection<GroupSymbol> rightGroups = child2.getGroups();
            leftExpressions = new ArrayList<Expression>();
            rightExpressions = new ArrayList<Expression>();
            nonEquiJoinCriteria = new ArrayList<Criteria>();
            RuleChooseJoinStrategy.separateCriteria(leftGroups, rightGroups, leftExpressions, rightExpressions, joinCriteria, nonEquiJoinCriteria);
        } else {
            leftExpressions = (List<Expression>) node.getProperty(NodeConstants.Info.LEFT_EXPRESSIONS);
            rightExpressions = (List<Expression>) node.getProperty(NodeConstants.Info.RIGHT_EXPRESSIONS);
            nonEquiJoinCriteria = (List<Criteria>) node.getProperty(NodeConstants.Info.NON_EQUI_JOIN_CRITERIA);
        }
        float leftNdv = getNDVEstimate(child1, metadata, childCost1, leftExpressions, false);
        float rightNdv = getNDVEstimate(child2, metadata, childCost2, rightExpressions, false);
        float leftNdv1 = getNDVEstimate(child1, metadata, childCost1, leftExpressions, null);
        float rightNdv1 = getNDVEstimate(child2, metadata, childCost2, rightExpressions, null);
        if (leftNdv == UNKNOWN_VALUE) {
            leftNdv = leftNdv1;
        }
        if (rightNdv == UNKNOWN_VALUE) {
            rightNdv = rightNdv1;
        }
        if (leftNdv != UNKNOWN_VALUE && rightNdv != UNKNOWN_VALUE) {
            // Compensate for estimates by assuming a 1-many relationship
            if (leftNdv1 > 2 * leftNdv && leftNdv > rightNdv) {
                leftNdv = (float) Math.sqrt(rightNdv * leftNdv);
            }
            if (rightNdv1 > 2 * rightNdv && rightNdv > leftNdv) {
                rightNdv = (float) Math.sqrt(rightNdv * leftNdv);
            }
            baseCost = (childCost1 / leftNdv) * (childCost2 / rightNdv) * Math.min(leftNdv, rightNdv);
            leftPercent = Math.min(leftNdv, rightNdv) / leftNdv;
            rightPercent = Math.min(leftNdv, rightNdv) / rightNdv;
        } else {
            nonEquiJoinCriteria = joinCriteria;
        }
        if (!nonEquiJoinCriteria.isEmpty()) {
            Criteria crit = Criteria.combineCriteria(nonEquiJoinCriteria);
            // TODO: we may be able to get a fairly accurate join estimate if the
            // unknown side is being joined with a key
            baseCost = recursiveEstimateCostOfCriteria(baseCost, node, crit, metadata);
        }
    }
    Float cost = null;
    if (JoinType.JOIN_CROSS.equals(joinType) || JoinType.JOIN_INNER.equals(joinType)) {
        cost = baseCost;
    } else if (JoinType.JOIN_FULL_OUTER.equals(joinType)) {
        cost = Math.max((childCost1 + childCost2), baseCost);
    } else if (JoinType.JOIN_LEFT_OUTER.equals(joinType)) {
        cost = Math.max(childCost1, baseCost);
    } else if (JoinType.JOIN_SEMI.equals(joinType) || JoinType.JOIN_ANTI_SEMI.equals(joinType)) {
        cost = Math.min(childCost1, baseCost);
    }
    setCardinalityEstimate(node, cost, true, metadata, leftPercent, rightPercent);
}
Also used : PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) Expression(org.teiid.query.sql.symbol.Expression) GroupSymbol(org.teiid.query.sql.symbol.GroupSymbol)

Example 57 with PlanNode

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

the class NewCalculateCostUtil method getOutputCols.

private static List<? extends Expression> getOutputCols(PlanNode node, QueryMetadataInterface metadata) throws QueryMetadataException, TeiidComponentException {
    List<Expression> outputCols = (List<Expression>) node.getProperty(Info.OUTPUT_COLS);
    if (outputCols != null) {
        return outputCols;
    }
    PlanNode projectNode = NodeEditor.findNodePreOrder(node, NodeConstants.Types.PROJECT | NodeConstants.Types.GROUP | NodeConstants.Types.SOURCE | NodeConstants.Types.JOIN | NodeConstants.Types.NULL);
    if (projectNode != null) {
        node = projectNode;
    }
    if (node.getType() == NodeConstants.Types.PROJECT) {
        return (List<? extends Expression>) node.getProperty(NodeConstants.Info.PROJECT_COLS);
    } else if (node.getType() == NodeConstants.Types.GROUP) {
        SymbolMap map = (SymbolMap) node.getProperty(Info.SYMBOL_MAP);
        return map.getKeys();
    }
    LinkedList<ElementSymbol> elements = new LinkedList<ElementSymbol>();
    for (GroupSymbol group : node.getGroups()) {
        elements.addAll(ResolverUtil.resolveElementsInGroup(group, metadata));
    }
    return elements;
}
Also used : ElementSymbol(org.teiid.query.sql.symbol.ElementSymbol) 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)

Example 58 with PlanNode

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

the class RuleAssignOutputElements method collectRequiredInputSymbols.

/**
 * Collect all required input symbols for a given node.  Input symbols
 * are any symbols that are required in the processing of this node,
 * for instance to create a new element symbol or sort on it, etc.
 * @param node Node to collect for
 * @param metadata
 * @param capFinder
 * @throws TeiidComponentException
 * @throws QueryMetadataException
 */
private List<Expression> collectRequiredInputSymbols(PlanNode node, QueryMetadataInterface metadata, CapabilitiesFinder capFinder) throws QueryMetadataException, TeiidComponentException {
    Set<Expression> requiredSymbols = new LinkedHashSet<Expression>();
    Set<Expression> createdSymbols = new HashSet<Expression>();
    List<Expression> outputCols = (List<Expression>) node.getProperty(NodeConstants.Info.OUTPUT_COLS);
    switch(node.getType()) {
        case NodeConstants.Types.PROJECT:
            {
                List<Expression> projectCols = (List<Expression>) node.getProperty(NodeConstants.Info.PROJECT_COLS);
                PlanNode accessParent = NodeEditor.findParent(node, NodeConstants.Types.ACCESS);
                PlanNode accessNode = null;
                if (accessParent == null) {
                    // find the direct access node
                    accessNode = NodeEditor.findNodePreOrder(node, NodeConstants.Types.ACCESS, NodeConstants.Types.SOURCE | NodeConstants.Types.JOIN | NodeConstants.Types.SET_OP | NodeConstants.Types.GROUP);
                }
                for (Expression ss : projectCols) {
                    if (ss instanceof AliasSymbol) {
                        createdSymbols.add(ss);
                        ss = ((AliasSymbol) ss).getSymbol();
                    }
                    if (ss instanceof WindowFunction || ss instanceof ExpressionSymbol) {
                        createdSymbols.add(ss);
                    }
                    if (!pushProjection(node, metadata, capFinder, requiredSymbols, accessParent, accessNode, ss)) {
                        ElementCollectorVisitor.getElements(ss, requiredSymbols);
                    }
                }
                break;
            }
        case NodeConstants.Types.SELECT:
            Criteria selectCriteria = (Criteria) node.getProperty(NodeConstants.Info.SELECT_CRITERIA);
            ElementCollectorVisitor.getElements(selectCriteria, requiredSymbols);
            break;
        case NodeConstants.Types.JOIN:
            List<Criteria> crits = (List) node.getProperty(NodeConstants.Info.JOIN_CRITERIA);
            if (crits != null) {
                for (Criteria joinCriteria : crits) {
                    ElementCollectorVisitor.getElements(joinCriteria, requiredSymbols);
                }
            }
            break;
        case NodeConstants.Types.GROUP:
            List<Expression> groupCols = (List<Expression>) node.getProperty(NodeConstants.Info.GROUP_COLS);
            PlanNode accessParent = NodeEditor.findParent(node, NodeConstants.Types.ACCESS);
            PlanNode accessNode = null;
            if (accessParent == null) {
                // find the direct access node
                accessNode = NodeEditor.findNodePreOrder(node.getFirstChild(), NodeConstants.Types.ACCESS, NodeConstants.Types.SOURCE | NodeConstants.Types.JOIN | NodeConstants.Types.SET_OP | NodeConstants.Types.GROUP);
            }
            if (groupCols != null) {
                for (Expression expression : groupCols) {
                    if (!pushProjection(node, metadata, capFinder, requiredSymbols, accessParent, accessNode, expression)) {
                        ElementCollectorVisitor.getElements(expression, requiredSymbols);
                    }
                }
            }
            SymbolMap symbolMap = (SymbolMap) node.getProperty(NodeConstants.Info.SYMBOL_MAP);
            Set<ElementSymbol> usedAggregates = new HashSet<ElementSymbol>();
            // Take credit for creating any aggregates that are needed above
            for (Expression outputSymbol : outputCols) {
                if (!(outputSymbol instanceof ElementSymbol)) {
                    continue;
                }
                createdSymbols.add(outputSymbol);
                Expression ex = symbolMap.getMappedExpression((ElementSymbol) outputSymbol);
                if (ex instanceof AggregateSymbol) {
                    AggregateSymbol agg = (AggregateSymbol) ex;
                    Expression[] aggExprs = agg.getArgs();
                    for (Expression expression : aggExprs) {
                        if (!pushProjection(node, metadata, capFinder, requiredSymbols, accessParent, accessNode, expression)) {
                            ElementCollectorVisitor.getElements(expression, requiredSymbols);
                        }
                    }
                    OrderBy orderBy = agg.getOrderBy();
                    if (orderBy != null) {
                        ElementCollectorVisitor.getElements(orderBy, requiredSymbols);
                    }
                    Expression condition = agg.getCondition();
                    if (condition != null) {
                        ElementCollectorVisitor.getElements(condition, requiredSymbols);
                    }
                    usedAggregates.add((ElementSymbol) outputSymbol);
                }
            }
            // update the aggs in the symbolmap
            for (Map.Entry<ElementSymbol, Expression> entry : new ArrayList<Map.Entry<ElementSymbol, Expression>>(symbolMap.asMap().entrySet())) {
                if (entry.getValue() instanceof AggregateSymbol && !usedAggregates.contains(entry.getKey())) {
                    symbolMap.asUpdatableMap().remove(entry.getKey());
                }
            }
            if (requiredSymbols.isEmpty() && usedAggregates.isEmpty()) {
                node.setProperty(Info.IS_OPTIONAL, true);
            }
            break;
    }
    // Gather elements from correlated subquery references;
    for (SymbolMap refs : node.getAllReferences()) {
        for (Expression expr : refs.asMap().values()) {
            ElementCollectorVisitor.getElements(expr, requiredSymbols);
        }
    }
    // Add any columns to required that are in this node's output but were not created here
    for (Expression currentOutputSymbol : outputCols) {
        if (!createdSymbols.contains(currentOutputSymbol) && (finalRun || node.getType() != NodeConstants.Types.PROJECT || currentOutputSymbol instanceof ElementSymbol)) {
            requiredSymbols.add(currentOutputSymbol);
        }
    }
    // TODO: this should depend upon whether the expressions are deterministic
    if (node.getType() == NodeConstants.Types.PROJECT) {
        Set<Expression> expressions = new HashSet<Expression>();
        for (Iterator<Expression> iterator = requiredSymbols.iterator(); iterator.hasNext(); ) {
            Expression ses = iterator.next();
            if (!expressions.add(SymbolMap.getExpression(ses))) {
                iterator.remove();
            }
        }
    }
    return new ArrayList<Expression>(requiredSymbols);
}
Also used : LinkedHashSet(java.util.LinkedHashSet) ElementSymbol(org.teiid.query.sql.symbol.ElementSymbol) OrderBy(org.teiid.query.sql.lang.OrderBy) AggregateSymbol(org.teiid.query.sql.symbol.AggregateSymbol) ArrayList(java.util.ArrayList) SymbolMap(org.teiid.query.sql.util.SymbolMap) ExpressionSymbol(org.teiid.query.sql.symbol.ExpressionSymbol) Criteria(org.teiid.query.sql.lang.Criteria) WindowFunction(org.teiid.query.sql.symbol.WindowFunction) PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) AliasSymbol(org.teiid.query.sql.symbol.AliasSymbol) Expression(org.teiid.query.sql.symbol.Expression) List(java.util.List) ArrayList(java.util.ArrayList) Map(java.util.Map) SymbolMap(org.teiid.query.sql.util.SymbolMap) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet)

Example 59 with PlanNode

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

the class RuleAssignOutputElements method removeGroupBy.

static PlanNode removeGroupBy(PlanNode root, QueryMetadataInterface metadata) throws QueryPlannerException {
    PlanNode next = root.getFirstChild();
    NodeEditor.removeChildNode(root.getParent(), root);
    SymbolMap symbolMap = (SymbolMap) root.getProperty(NodeConstants.Info.SYMBOL_MAP);
    if (!symbolMap.asMap().isEmpty()) {
        FrameUtil.convertFrame(next.getParent(), symbolMap.asMap().keySet().iterator().next().getGroupSymbol(), null, symbolMap.asMap(), metadata);
    }
    PlanNode parent = next.getParent();
    while (parent.getParent() != null && parent.getParent().getType() != NodeConstants.Types.SOURCE && parent.getParent().getType() != NodeConstants.Types.SET_OP) {
        parent = parent.getParent();
    }
    return parent;
}
Also used : PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) SymbolMap(org.teiid.query.sql.util.SymbolMap)

Example 60 with PlanNode

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

the class RuleAssignOutputElements method filterVirtualElements.

/**
 * <p>This method looks at a source node, which defines a virtual group, and filters the
 * virtual elements defined by the group down into just the output elements needed
 * by that source node.  This means, for instance, that the PROJECT node at the top
 * of the virtual group might need to have some elements removed from the project as
 * those elements are no longer needed.  </p>
 *
 * <p>One special case that is handled here is when a virtual group is defined by
 * a UNION ALL.  In this case, the various branches of the union have elements defined
 * and filtering must occur identically in all branches of the union.  </p>
 *
 * @param sourceNode Node to filter
 * @param metadata Metadata implementation
 * @return The filtered list of columns for this node (used in recursing tree)
 * @throws QueryPlannerException
 */
static List<Expression> filterVirtualElements(PlanNode sourceNode, List<Expression> outputColumns, QueryMetadataInterface metadata) throws QueryPlannerException {
    PlanNode virtualRoot = sourceNode.getLastChild();
    // Update project cols - typically there is exactly one and that node can
    // just get the filteredCols determined above.  In the case of one or more
    // nested set operations (UNION, INTERSECT, EXCEPT) there will be 2 or more
    // projects.
    List<PlanNode> allProjects = NodeEditor.findAllNodes(virtualRoot, NodeConstants.Types.PROJECT, NodeConstants.Types.PROJECT);
    int[] filteredIndex = new int[outputColumns.size()];
    Arrays.fill(filteredIndex, -1);
    SymbolMap symbolMap = (SymbolMap) sourceNode.getProperty(NodeConstants.Info.SYMBOL_MAP);
    List<ElementSymbol> originalOrder = symbolMap.getKeys();
    boolean updateGroups = outputColumns.size() != originalOrder.size();
    boolean[] seenIndex = new boolean[outputColumns.size()];
    boolean newSymbols = false;
    int newSymbolIndex = 0;
    for (int i = 0; i < outputColumns.size(); i++) {
        Expression expr = outputColumns.get(i);
        filteredIndex[i] = originalOrder.indexOf(expr);
        if (filteredIndex[i] == -1) {
            updateGroups = true;
            // we're adding this symbol, which needs to be updated against respective symbol maps
            newSymbols = true;
        } else {
            newSymbolIndex++;
        }
        if (!updateGroups) {
            seenIndex[filteredIndex[i]] = true;
        }
    }
    if (!updateGroups) {
        for (boolean b : seenIndex) {
            if (!b) {
                updateGroups = true;
                break;
            }
        }
    }
    List<Expression> newCols = null;
    for (int i = allProjects.size() - 1; i >= 0; i--) {
        PlanNode projectNode = allProjects.get(i);
        List<Expression> projectCols = (List<Expression>) projectNode.getProperty(NodeConstants.Info.PROJECT_COLS);
        newCols = RelationalNode.projectTuple(filteredIndex, projectCols, true);
        if (newSymbols) {
            SymbolMap childMap = SymbolMap.createSymbolMap(symbolMap.getKeys(), projectCols);
            for (int j = 0; j < filteredIndex.length; j++) {
                if (filteredIndex[j] != -1) {
                    continue;
                }
                Expression ex = (Expression) outputColumns.get(j).clone();
                ExpressionMappingVisitor.mapExpressions(ex, childMap.asMap());
                newCols.set(j, ex);
                if (i == 0) {
                    filteredIndex[j] = newSymbolIndex++;
                }
            }
        }
        projectNode.setProperty(NodeConstants.Info.PROJECT_COLS, newCols);
        if (updateGroups) {
            projectNode.getGroups().clear();
            projectNode.addGroups(GroupsUsedByElementsVisitor.getGroups(newCols));
            projectNode.addGroups(GroupsUsedByElementsVisitor.getGroups(projectNode.getCorrelatedReferenceElements()));
        }
    }
    if (!updateGroups) {
        for (int i : filteredIndex) {
            if (i != filteredIndex[i]) {
                updateGroups = true;
                break;
            }
        }
    }
    if (updateGroups) {
        SymbolMap newMap = new SymbolMap();
        List<Expression> originalExpressionOrder = symbolMap.getValues();
        for (int i = 0; i < filteredIndex.length; i++) {
            if (filteredIndex[i] < originalOrder.size()) {
                newMap.addMapping(originalOrder.get(filteredIndex[i]), originalExpressionOrder.get(filteredIndex[i]));
            }
        // else TODO: we may need to create a fake symbol
        }
        sourceNode.setProperty(NodeConstants.Info.SYMBOL_MAP, newMap);
    }
    // Create output columns for virtual group project
    return newCols;
}
Also used : ElementSymbol(org.teiid.query.sql.symbol.ElementSymbol) PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) Expression(org.teiid.query.sql.symbol.Expression) SymbolMap(org.teiid.query.sql.util.SymbolMap) List(java.util.List) ArrayList(java.util.ArrayList)

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