Search in sources :

Example 6 with SymbolMap

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

the class RuleMergeCriteria method planMergeJoin.

/**
 * Look for:
 * [NOT] EXISTS ( )
 * IN ( ) / SOME ( )
 *
 * and replace with a semi join
 */
private PlanNode planMergeJoin(PlanNode current, PlanNode root) throws QueryMetadataException, TeiidComponentException {
    float sourceCost = NewCalculateCostUtil.computeCostForTree(current.getFirstChild(), metadata);
    Criteria crit = (Criteria) current.getProperty(NodeConstants.Info.SELECT_CRITERIA);
    PlannedResult plannedResult = findSubquery(crit, true);
    if (plannedResult.query == null) {
        return current;
    }
    if (sourceCost != NewCalculateCostUtil.UNKNOWN_VALUE && sourceCost < RuleChooseDependent.DEFAULT_INDEPENDENT_CARDINALITY && !plannedResult.mergeJoin) {
        // TODO: see if a dependent join applies the other direction
        return current;
    }
    RelationalPlan originalPlan = (RelationalPlan) plannedResult.query.getProcessorPlan();
    Number originalCardinality = originalPlan.getRootNode().getEstimateNodeCardinality();
    if (!plannedResult.mergeJoin && originalCardinality.floatValue() == NewCalculateCostUtil.UNKNOWN_VALUE) {
        // if it's currently unknown, removing criteria won't make it any better
        return current;
    }
    Collection<GroupSymbol> leftGroups = FrameUtil.findJoinSourceNode(current).getGroups();
    if (!planQuery(leftGroups, false, plannedResult)) {
        if (plannedResult.mergeJoin && analysisRecord != null && analysisRecord.recordAnnotations()) {
            // $NON-NLS-1$ //$NON-NLS-2$
            this.analysisRecord.addAnnotation(new Annotation(Annotation.HINTS, "Could not plan as a merge join: " + crit, "ignoring MJ hint", Priority.HIGH));
        }
        return current;
    }
    // check if the child is already ordered.  TODO: see if the ordering is compatible.
    PlanNode childSort = NodeEditor.findNodePreOrder(root, NodeConstants.Types.SORT, NodeConstants.Types.SOURCE | NodeConstants.Types.JOIN);
    if (childSort != null) {
        if (plannedResult.mergeJoin && analysisRecord != null && analysisRecord.recordAnnotations()) {
            // $NON-NLS-1$ //$NON-NLS-2$
            this.analysisRecord.addAnnotation(new Annotation(Annotation.HINTS, "Could not plan as a merge join since the parent join requires a sort: " + crit, "ignoring MJ hint", Priority.HIGH));
        }
        return current;
    }
    // add an order by, which hopefully will get pushed down
    plannedResult.query.setOrderBy(new OrderBy(plannedResult.rightExpressions).clone());
    for (OrderByItem item : plannedResult.query.getOrderBy().getOrderByItems()) {
        int index = plannedResult.query.getProjectedSymbols().indexOf(item.getSymbol());
        if (index >= 0 && !(item.getSymbol() instanceof ElementSymbol)) {
            item.setSymbol((Expression) plannedResult.query.getProjectedSymbols().get(index).clone());
        }
        item.setExpressionPosition(index);
    }
    try {
        // clone the symbols as they may change during planning
        List<Expression> projectedSymbols = LanguageObject.Util.deepClone(plannedResult.query.getProjectedSymbols(), Expression.class);
        // NOTE: we could tap into the relationalplanner at a lower level to get this in a plan node form,
        // the major benefit would be to reuse the dependent join planning logic if possible.
        RelationalPlan subPlan = (RelationalPlan) QueryOptimizer.optimizePlan(plannedResult.query, metadata, idGenerator, capFinder, analysisRecord, context);
        Number planCardinality = subPlan.getRootNode().getEstimateNodeCardinality();
        if (!plannedResult.mergeJoin) {
            // if we don't have a specific hint, then use costing
            if (planCardinality.floatValue() == NewCalculateCostUtil.UNKNOWN_VALUE || planCardinality.floatValue() > 10000000 || (sourceCost == NewCalculateCostUtil.UNKNOWN_VALUE && planCardinality.floatValue() > 1000) || (sourceCost != NewCalculateCostUtil.UNKNOWN_VALUE && sourceCost * originalCardinality.floatValue() < planCardinality.floatValue() / (100 * Math.log(Math.max(4, sourceCost))))) {
                // bail-out if both are unknown or the new plan is too large
                if (analysisRecord != null && analysisRecord.recordDebug()) {
                    // $NON-NLS-1$ //$NON-NLS-2$
                    current.recordDebugAnnotation("cost of merge join plan was not favorable", null, "semi merge join will not be used", analysisRecord, metadata);
                }
                return current;
            }
        }
        // assume dependent
        if ((sourceCost != NewCalculateCostUtil.UNKNOWN_VALUE && planCardinality.floatValue() != NewCalculateCostUtil.UNKNOWN_VALUE && planCardinality.floatValue() < sourceCost / 8) || (sourceCost == NewCalculateCostUtil.UNKNOWN_VALUE && planCardinality.floatValue() <= 1000)) {
            plannedResult.makeInd = true;
        }
        /*if (plannedResult.makeInd 
					&& plannedResult.query.getCorrelatedReferences() == null
					&& !plannedResult.not
					&& plannedResult.leftExpressions.size() == 1) {
            	//TODO: this should just be a dependent criteria node to avoid sorts
            }*/
        // $NON-NLS-1$ //$NON-NLS-2$
        current.recordDebugAnnotation("Conditions met (hint or cost)", null, "Converting to a semi merge join", analysisRecord, metadata);
        PlanNode semiJoin = NodeFactory.getNewNode(NodeConstants.Types.JOIN);
        semiJoin.addGroups(current.getGroups());
        Set<GroupSymbol> groups = GroupsUsedByElementsVisitor.getGroups(plannedResult.rightExpressions);
        semiJoin.addGroups(groups);
        semiJoin.setProperty(NodeConstants.Info.JOIN_STRATEGY, JoinStrategyType.MERGE);
        semiJoin.setProperty(NodeConstants.Info.JOIN_TYPE, plannedResult.not ? JoinType.JOIN_ANTI_SEMI : JoinType.JOIN_SEMI);
        semiJoin.setProperty(NodeConstants.Info.NON_EQUI_JOIN_CRITERIA, plannedResult.nonEquiJoinCriteria);
        List<Criteria> joinCriteria = new ArrayList<Criteria>();
        joinCriteria.addAll(plannedResult.nonEquiJoinCriteria);
        for (int i = 0; i < plannedResult.leftExpressions.size(); i++) {
            joinCriteria.add(new CompareCriteria((Expression) plannedResult.rightExpressions.get(i), CompareCriteria.EQ, (Expression) plannedResult.leftExpressions.get(i)));
        }
        semiJoin.setProperty(NodeConstants.Info.JOIN_CRITERIA, joinCriteria);
        // nested subqueries are possibly being promoted, so they need their references updated
        List<SymbolMap> refMaps = semiJoin.getAllReferences();
        SymbolMap parentRefs = plannedResult.query.getCorrelatedReferences();
        for (SymbolMap refs : refMaps) {
            for (Map.Entry<ElementSymbol, Expression> ref : refs.asUpdatableMap().entrySet()) {
                Expression expr = ref.getValue();
                if (expr instanceof ElementSymbol) {
                    Expression convertedExpr = parentRefs.getMappedExpression((ElementSymbol) expr);
                    if (convertedExpr != null) {
                        ref.setValue(convertedExpr);
                    }
                }
                semiJoin.getGroups().addAll(GroupsUsedByElementsVisitor.getGroups(ref.getValue()));
            }
        }
        semiJoin.setProperty(NodeConstants.Info.LEFT_EXPRESSIONS, plannedResult.leftExpressions);
        semiJoin.getGroups().addAll(GroupsUsedByElementsVisitor.getGroups(plannedResult.leftExpressions));
        semiJoin.setProperty(NodeConstants.Info.RIGHT_EXPRESSIONS, plannedResult.rightExpressions);
        semiJoin.getGroups().addAll(GroupsUsedByElementsVisitor.getGroups(plannedResult.rightExpressions));
        semiJoin.setProperty(NodeConstants.Info.SORT_RIGHT, SortOption.ALREADY_SORTED);
        semiJoin.setProperty(NodeConstants.Info.OUTPUT_COLS, root.getProperty(NodeConstants.Info.OUTPUT_COLS));
        List childOutput = (List) current.getFirstChild().getProperty(NodeConstants.Info.OUTPUT_COLS);
        PlanNode toCorrect = root;
        while (toCorrect != current) {
            toCorrect.setProperty(NodeConstants.Info.OUTPUT_COLS, childOutput);
            toCorrect = toCorrect.getFirstChild();
        }
        PlanNode node = NodeFactory.getNewNode(NodeConstants.Types.ACCESS);
        node.setProperty(NodeConstants.Info.PROCESSOR_PLAN, subPlan);
        node.setProperty(NodeConstants.Info.OUTPUT_COLS, projectedSymbols);
        node.setProperty(NodeConstants.Info.EST_CARDINALITY, planCardinality);
        node.addGroups(groups);
        root.addAsParent(semiJoin);
        semiJoin.addLastChild(node);
        PlanNode result = current.getParent();
        NodeEditor.removeChildNode(result, current);
        RuleImplementJoinStrategy.insertSort(semiJoin.getFirstChild(), (List<Expression>) plannedResult.leftExpressions, semiJoin, metadata, capFinder, true, context);
        if (plannedResult.makeInd && !plannedResult.not) {
            // TODO: would like for an enhanced sort merge with the semi dep option to avoid the sorting
            // this is a little different than a typical dependent join in that the right is the independent side
            String id = RuleChooseDependent.nextId();
            PlanNode dep = RuleChooseDependent.getDependentCriteriaNode(id, plannedResult.rightExpressions, plannedResult.leftExpressions, node, metadata, null, false, null);
            semiJoin.getFirstChild().addAsParent(dep);
            semiJoin.setProperty(NodeConstants.Info.DEPENDENT_VALUE_SOURCE, id);
            this.dependent = true;
        }
        return result;
    } catch (QueryPlannerException e) {
        // can't be done - probably access patterns - what about dependent
        return current;
    }
}
Also used : ElementSymbol(org.teiid.query.sql.symbol.ElementSymbol) ArrayList(java.util.ArrayList) RelationalPlan(org.teiid.query.processor.relational.RelationalPlan) PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) List(java.util.List) ArrayList(java.util.ArrayList) LinkedList(java.util.LinkedList) SymbolMap(org.teiid.query.sql.util.SymbolMap) Annotation(org.teiid.client.plan.Annotation) Expression(org.teiid.query.sql.symbol.Expression) GroupSymbol(org.teiid.query.sql.symbol.GroupSymbol) Map(java.util.Map) SymbolMap(org.teiid.query.sql.util.SymbolMap) QueryPlannerException(org.teiid.api.exception.query.QueryPlannerException)

Example 7 with SymbolMap

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

the class RuleMergeVirtual method doMerge.

static PlanNode doMerge(PlanNode frame, PlanNode root, boolean beforeDecomposeJoin, QueryMetadataInterface metadata, CapabilitiesFinder capFinder) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
    if (frame.hasBooleanProperty(Info.NO_UNNEST)) {
        return root;
    }
    GroupSymbol virtualGroup = frame.getGroups().iterator().next();
    // check to see if frame represents a proc relational query.
    if (virtualGroup.isProcedure()) {
        return root;
    }
    List<PlanNode> sources = NodeEditor.findAllNodes(frame.getFirstChild(), NodeConstants.Types.SOURCE, NodeConstants.Types.SOURCE);
    SymbolMap references = (SymbolMap) frame.getProperty(NodeConstants.Info.CORRELATED_REFERENCES);
    if (references != null) {
        if (!sources.isEmpty()) {
            // correlated nested table commands should not be merged
            return root;
        }
        // this is ok only if all of the references go above the correlating join
        // currently this check is simplistic - just look at the parent join more nested scenarios won't work
        PlanNode parentJoin = NodeEditor.findParent(frame, NodeConstants.Types.JOIN, NodeConstants.Types.SOURCE | NodeConstants.Types.GROUP);
        if (parentJoin != null && !parentJoin.getGroups().containsAll(GroupsUsedByElementsVisitor.getGroups(references.getValues()))) {
            return root;
        }
    }
    PlanNode parentProject = NodeEditor.findParent(frame, NodeConstants.Types.PROJECT);
    // Check whether the upper frame is a SELECT INTO
    if (parentProject.getProperty(NodeConstants.Info.INTO_GROUP) != null) {
        return root;
    }
    if (!FrameUtil.canConvertAccessPatterns(frame)) {
        return root;
    }
    PlanNode projectNode = frame.getFirstChild();
    // Check if lower frame has only a stored procedure execution - this cannot be merged to parent frame
    if (FrameUtil.isProcedure(projectNode)) {
        return root;
    }
    SymbolMap symbolMap = (SymbolMap) frame.getProperty(NodeConstants.Info.SYMBOL_MAP);
    PlanNode sortNode = NodeEditor.findParent(parentProject, NodeConstants.Types.SORT, NodeConstants.Types.SOURCE);
    if (sortNode != null && sortNode.hasBooleanProperty(NodeConstants.Info.UNRELATED_SORT)) {
        OrderBy sortOrder = (OrderBy) sortNode.getProperty(NodeConstants.Info.SORT_ORDER);
        boolean unrelated = false;
        for (OrderByItem item : sortOrder.getOrderByItems()) {
            if (!item.isUnrelated()) {
                continue;
            }
            Collection<ElementSymbol> elements = ElementCollectorVisitor.getElements(item.getSymbol(), true);
            for (ElementSymbol elementSymbol : elements) {
                if (virtualGroup.equals(elementSymbol.getGroupSymbol())) {
                    unrelated = true;
                }
            }
        }
        // the lower frame cannot contain DUP_REMOVE, GROUP, UNION if unrelated
        if (unrelated && NodeEditor.findNodePreOrder(frame, NodeConstants.Types.DUP_REMOVE, NodeConstants.Types.PROJECT) != null || NodeEditor.findNodePreOrder(frame, NodeConstants.Types.SET_OP, NodeConstants.Types.SOURCE) != null || NodeEditor.findNodePreOrder(frame, NodeConstants.Types.GROUP, NodeConstants.Types.SOURCE) != null) {
            return root;
        }
    }
    PlanNode parentJoin = NodeEditor.findParent(frame, NodeConstants.Types.JOIN, NodeConstants.Types.SOURCE | NodeConstants.Types.GROUP);
    // 3. if the frame has no sources
    if (projectNode.getType() != NodeConstants.Types.PROJECT || NodeEditor.findNodePreOrder(frame.getFirstChild(), NodeConstants.Types.GROUP, NodeConstants.Types.SOURCE | NodeConstants.Types.JOIN) != null || sources.isEmpty()) {
        PlanNode parentSource = NodeEditor.findParent(parentProject, NodeConstants.Types.SOURCE);
        if (beforeDecomposeJoin && parentSource != null && parentSource.hasProperty(Info.PARTITION_INFO) && !NodeEditor.findAllNodes(frame.getFirstChild(), NodeConstants.Types.SET_OP, NodeConstants.Types.SOURCE).isEmpty()) {
            // don't bother to merge until after
            return root;
        }
        root = checkForSimpleProjection(frame, root, parentProject, metadata, capFinder);
        if (frame.getParent() == null || !sources.isEmpty() || projectNode.getType() != NodeConstants.Types.PROJECT || parentJoin == null) {
            // only consider no sources when the frame is simple and there is a parent join
            return root;
        }
        if (sources.isEmpty() && parentJoin != null) {
            JoinType jt = (JoinType) parentJoin.getProperty(Info.JOIN_TYPE);
            if (jt.isOuter()) {
                // cannot remove if the no source side is an outer side, or if it can change the meaning of the plan
                return root;
            }
            PlanNode joinToTest = parentJoin;
            while (joinToTest != null) {
                if (FrameUtil.findJoinSourceNode(joinToTest.getFirstChild()).getGroups().contains(virtualGroup)) {
                    // scan all sources under the other side as there could be a join structure
                    for (PlanNode node : NodeEditor.findAllNodes(joinToTest.getLastChild(), NodeConstants.Types.SOURCE, NodeConstants.Types.SOURCE)) {
                        SymbolMap map = (SymbolMap) node.getProperty(NodeConstants.Info.CORRELATED_REFERENCES);
                        if (map != null && GroupsUsedByElementsVisitor.getGroups(map.getValues()).contains(virtualGroup)) {
                            // TODO: we don't have the logic yet to then replace the correlated references
                            return root;
                        }
                    }
                }
                joinToTest = NodeEditor.findParent(joinToTest, NodeConstants.Types.JOIN, NodeConstants.Types.SOURCE | NodeConstants.Types.GROUP);
            }
        }
    }
    if (!checkJoinCriteria(frame.getFirstChild(), virtualGroup, parentJoin)) {
        return root;
    }
    // we don't have to check for null dependent with no source without criteria since there must be a row
    if (!checkProjectedSymbols(projectNode, virtualGroup, parentJoin, metadata, sources, !sources.isEmpty() || frame.getParent() != parentJoin, parentProject)) {
        // TODO: propagate constants if just inhibited by subquery/non-deterministic expressions
        return root;
    }
    // Otherwise merge should work
    // Convert parent frame before merge
    Set<GroupSymbol> groups = Collections.emptySet();
    if (!sources.isEmpty()) {
        groups = FrameUtil.findJoinSourceNode(projectNode).getGroups();
    } else if (references != null) {
        // convert from correlated form to regular references
        RuleMergeCriteria.ReferenceReplacementVisitor rrv = new RuleMergeCriteria.ReferenceReplacementVisitor(references);
        for (Map.Entry<ElementSymbol, Expression> entry : symbolMap.asUpdatableMap().entrySet()) {
            if (entry.getValue() instanceof Reference) {
                Expression ex = rrv.replaceExpression(entry.getValue());
                entry.setValue(ex);
            } else {
                PreOrPostOrderNavigator.doVisit(entry.getValue(), rrv, PreOrPostOrderNavigator.PRE_ORDER);
            }
        }
    }
    FrameUtil.convertFrame(frame, virtualGroup, groups, symbolMap.asMap(), metadata);
    PlanNode parentBottom = frame.getParent();
    prepareFrame(frame);
    if (sources.isEmpty() && parentJoin != null) {
        // special handling for no sources
        PlanNode parent = frame;
        List<PlanNode> criteriaNodes = new ArrayList<PlanNode>();
        while (parent.getParent() != parentJoin) {
            parent = parent.getParent();
            if (!parent.hasBooleanProperty(Info.IS_PHANTOM)) {
                criteriaNodes.add(parent);
            }
        }
        PlanNode parentNode = parentJoin.getParent();
        parentJoin.removeChild(parent);
        PlanNode other = parentJoin.getFirstChild();
        NodeEditor.removeChildNode(parentNode, parentJoin);
        JoinType jt = (JoinType) parentJoin.getProperty(Info.JOIN_TYPE);
        if (!jt.isOuter()) {
            // if we are not an outer join then the join/parent criteria is effectively
            // applied to the other side
            List<Criteria> joinCriteria = (List<Criteria>) parentJoin.getProperty(Info.JOIN_CRITERIA);
            if (joinCriteria != null) {
                for (Criteria crit : joinCriteria) {
                    PlanNode critNode = RelationalPlanner.createSelectNode(crit, false);
                    criteriaNodes.add(critNode);
                }
            }
            if (!criteriaNodes.isEmpty()) {
                for (PlanNode selectNode : criteriaNodes) {
                    selectNode.removeAllChildren();
                    selectNode.removeFromParent();
                    other.addAsParent(selectNode);
                }
            }
        }
    } else {
        // Remove top 2 nodes (SOURCE, PROJECT) of virtual group - they're no longer needed
        NodeEditor.removeChildNode(parentBottom, frame);
        NodeEditor.removeChildNode(parentBottom, projectNode);
    }
    return root;
}
Also used : OrderBy(org.teiid.query.sql.lang.OrderBy) ElementSymbol(org.teiid.query.sql.symbol.ElementSymbol) Reference(org.teiid.query.sql.symbol.Reference) 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) PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) OrderByItem(org.teiid.query.sql.lang.OrderByItem) 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)

Example 8 with SymbolMap

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

the class RulePlaceAccess method addAlias.

/**
 * Ensures that the group is uniquely named within the current optimizer run
 *
 * @param sourceNode
 * @param groups
 * @param metadata
 * @throws QueryMetadataException
 * @throws TeiidComponentException
 * @throws QueryPlannerException
 */
private void addAlias(PlanNode sourceNode, CommandContext cc, Set<String> groups, QueryMetadataInterface metadata) throws QueryMetadataException, TeiidComponentException, QueryPlannerException {
    // select with no from
    if (sourceNode.getGroups().isEmpty()) {
        return;
    }
    // insert, update, delete, create, etc.
    if (FrameUtil.getNonQueryCommand(sourceNode.getParent()) != null) {
        return;
    }
    PlanNode parentProject = NodeEditor.findParent(sourceNode, NodeConstants.Types.PROJECT);
    // the source over a project into cannot conflict with any other groups
    if (parentProject.hasProperty(NodeConstants.Info.INTO_GROUP)) {
        return;
    }
    GroupSymbol group = sourceNode.getGroups().iterator().next();
    if (groups.add(group.getName())) {
        if (group.getDefinition() != null) {
            cc.getAliasMapping().put(group.getName(), group.getName());
        }
        // this is the first instance of the group
        return;
    }
    List<PlanNode> childProjects = null;
    if (sourceNode.getChildCount() > 0) {
        childProjects = NodeEditor.findAllNodes(sourceNode.getFirstChild(), NodeConstants.Types.PROJECT, NodeConstants.Types.SOURCE);
    }
    GroupSymbol newGroup = recontextSymbol(group, groups);
    if (group.getDefinition() != null) {
        cc.getAliasMapping().put(newGroup.getName(), group.getName());
    }
    // the expressions in the map will all be element symbols
    Map<ElementSymbol, Expression> replacementSymbols = FrameUtil.buildSymbolMap(group, newGroup, metadata);
    FrameUtil.convertFrame(sourceNode, group, new HashSet<GroupSymbol>(Arrays.asList(newGroup)), replacementSymbols, metadata);
    // correct the lower symbol map
    if (childProjects != null) {
        SymbolMap symbolMap = (SymbolMap) sourceNode.getProperty(NodeConstants.Info.SYMBOL_MAP);
        SymbolMap replacementMap = new SymbolMap();
        for (Map.Entry<ElementSymbol, Expression> entry : symbolMap.asMap().entrySet()) {
            replacementMap.addMapping((ElementSymbol) replacementSymbols.get(entry.getKey()), entry.getValue());
        }
        sourceNode.setProperty(NodeConstants.Info.SYMBOL_MAP, replacementMap);
    }
}
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) Map(java.util.Map) SymbolMap(org.teiid.query.sql.util.SymbolMap)

Example 9 with SymbolMap

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

the class RulePlanSorts method optimizeSorts.

private PlanNode optimizeSorts(boolean parentBlocking, PlanNode node, PlanNode root, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, AnalysisRecord record, CommandContext context) throws QueryMetadataException, TeiidComponentException, QueryPlannerException {
    node = NodeEditor.findNodePreOrder(node, NodeConstants.Types.SORT | NodeConstants.Types.DUP_REMOVE | NodeConstants.Types.GROUP | NodeConstants.Types.JOIN | NodeConstants.Types.SET_OP, NodeConstants.Types.ACCESS);
    if (node == null) {
        return root;
    }
    switch(node.getType()) {
        case NodeConstants.Types.SORT:
            parentBlocking = true;
            if (node.hasBooleanProperty(NodeConstants.Info.IS_DUP_REMOVAL)) {
                break;
            }
            if (mergeSortWithDupRemoval(node)) {
                node.setProperty(NodeConstants.Info.IS_DUP_REMOVAL, true);
            } else {
                root = checkForProjectOptimization(node, root, metadata, capFinder, record, context);
                if (NodeEditor.findParent(node, NodeConstants.Types.ACCESS) != null) {
                    return root;
                }
            }
            OrderBy orderBy = (OrderBy) node.getProperty(NodeConstants.Info.SORT_ORDER);
            List<Expression> orderColumns = orderBy.getSortKeys();
            List<Expression> sortExpressions = new ArrayList<Expression>(orderColumns.size());
            PlanNode possibleSort = NodeEditor.findNodePreOrder(node, NodeConstants.Types.GROUP, NodeConstants.Types.SOURCE | NodeConstants.Types.ACCESS);
            if (possibleSort != null && !possibleSort.hasBooleanProperty(Info.ROLLUP)) {
                boolean otherExpression = false;
                SymbolMap groupMap = (SymbolMap) possibleSort.getProperty(Info.SYMBOL_MAP);
                for (Expression singleElementSymbol : orderColumns) {
                    Expression ex = SymbolMap.getExpression(singleElementSymbol);
                    if (ex instanceof ElementSymbol) {
                        sortExpressions.add(groupMap.getMappedExpression((ElementSymbol) ex));
                    } else {
                        otherExpression = true;
                        break;
                    }
                }
                List<Expression> exprs = (List<Expression>) possibleSort.getProperty(Info.GROUP_COLS);
                if (!otherExpression && exprs != null && exprs.containsAll(sortExpressions)) {
                    exprs.removeAll(sortExpressions);
                    exprs.addAll(0, sortExpressions);
                    if (node.getParent() == null) {
                        root = node.getFirstChild();
                        root.removeFromParent();
                        Object cols = node.getProperty(NodeConstants.Info.OUTPUT_COLS);
                        root.setProperty(NodeConstants.Info.OUTPUT_COLS, cols);
                        if (root.getType() == NodeConstants.Types.PROJECT) {
                            root.setProperty(NodeConstants.Info.PROJECT_COLS, cols);
                        }
                        node = root;
                    } else {
                        PlanNode nextNode = node.getFirstChild();
                        NodeEditor.removeChildNode(node.getParent(), node);
                        node = nextNode;
                    }
                    possibleSort.setProperty(Info.SORT_ORDER, orderBy);
                }
            }
            break;
        case NodeConstants.Types.DUP_REMOVE:
            if (parentBlocking) {
                node.setType(NodeConstants.Types.SORT);
                node.setProperty(NodeConstants.Info.IS_DUP_REMOVAL, true);
            }
            break;
        case NodeConstants.Types.GROUP:
            if (!node.hasCollectionProperty(NodeConstants.Info.GROUP_COLS)) {
                break;
            }
            SymbolMap map = (SymbolMap) node.getProperty(Info.SYMBOL_MAP);
            boolean cardinalityDependent = false;
            boolean canOptimize = true;
            for (Expression ex : map.asMap().values()) {
                if (ex instanceof AggregateSymbol) {
                    AggregateSymbol agg = (AggregateSymbol) ex;
                    if (agg.isCardinalityDependent()) {
                        cardinalityDependent = true;
                        break;
                    }
                } else if (!(ex instanceof ElementSymbol)) {
                    // there is an expression in the grouping columns
                    canOptimize = false;
                    break;
                }
            }
            if (canOptimize && mergeSortWithDupRemovalAcrossSource(node)) {
                node.setProperty(NodeConstants.Info.IS_DUP_REMOVAL, true);
                if (cardinalityDependent) {
                    PlanNode source = NodeEditor.findNodePreOrder(node, NodeConstants.Types.SOURCE);
                    List<Expression> sourceOutput = (List<Expression>) source.getProperty(Info.OUTPUT_COLS);
                    PlanNode child = node.getFirstChild();
                    while (child != source) {
                        child.setProperty(Info.OUTPUT_COLS, sourceOutput);
                        child = child.getFirstChild();
                    }
                }
            }
            // TODO: check the join interesting order
            parentBlocking = true;
            break;
        case NodeConstants.Types.JOIN:
            if (node.getProperty(NodeConstants.Info.JOIN_STRATEGY) == JoinStrategyType.NESTED_LOOP || node.getProperty(NodeConstants.Info.JOIN_STRATEGY) == JoinStrategyType.NESTED_TABLE) {
                break;
            }
            /*
			 *  Look under the left and the right sources for a dup removal operation
			 *  join
			 *   [project]
			 *     source
			 *       dup remove | union not all
			 */
            parentBlocking = true;
            PlanNode toTest = node.getFirstChild();
            if (mergeSortWithDupRemovalAcrossSource(toTest)) {
                node.setProperty(NodeConstants.Info.SORT_LEFT, SortOption.SORT_DISTINCT);
                if (node.getProperty(NodeConstants.Info.SORT_RIGHT) != SortOption.SORT) {
                    node.setProperty(NodeConstants.Info.JOIN_STRATEGY, JoinStrategyType.MERGE);
                }
            }
            toTest = node.getLastChild();
            if (mergeSortWithDupRemovalAcrossSource(toTest)) {
                node.setProperty(NodeConstants.Info.SORT_RIGHT, SortOption.SORT_DISTINCT);
                if (node.getProperty(NodeConstants.Info.SORT_LEFT) != SortOption.SORT) {
                    node.setProperty(NodeConstants.Info.JOIN_STRATEGY, JoinStrategyType.MERGE);
                }
            }
            break;
        case NodeConstants.Types.SET_OP:
            // assumes the use of the merge algorithm
            if (node.getProperty(NodeConstants.Info.SET_OPERATION) != SetQuery.Operation.UNION) {
                parentBlocking = true;
            } else if (!node.hasBooleanProperty(NodeConstants.Info.USE_ALL) && !parentBlocking) {
                // do the incremental dup removal for lower latency
                node.setProperty(NodeConstants.Info.IS_DUP_REMOVAL, true);
            }
            break;
    }
    for (PlanNode child : node.getChildren()) {
        root = optimizeSorts(parentBlocking, child, root, metadata, capFinder, record, context);
    }
    return root;
}
Also used : OrderBy(org.teiid.query.sql.lang.OrderBy) ElementSymbol(org.teiid.query.sql.symbol.ElementSymbol) AggregateSymbol(org.teiid.query.sql.symbol.AggregateSymbol) PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) Expression(org.teiid.query.sql.symbol.Expression) ArrayList(java.util.ArrayList) SymbolMap(org.teiid.query.sql.util.SymbolMap) ArrayList(java.util.ArrayList) List(java.util.List)

Example 10 with SymbolMap

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

the class RulePushAggregates method updateParentAggs.

private void updateParentAggs(PlanNode groupNode, Map<AggregateSymbol, Expression> aggMap, QueryMetadataInterface metadata) throws QueryMetadataException, TeiidComponentException, QueryPlannerException {
    LinkedHashSet<AggregateSymbol> compositeAggs = new LinkedHashSet<AggregateSymbol>();
    boolean hasExpressionMapping = false;
    SymbolMap oldGroupingMap = (SymbolMap) groupNode.getProperty(Info.SYMBOL_MAP);
    /* we operate over the old group node map since the aggMap is based only
		 * upon the aggs for the target node and not all of the possible aggregates,
		 * which will cause a failure if we introduce an expression mapping
		 */
    for (Expression ex : oldGroupingMap.asMap().values()) {
        if (!(ex instanceof AggregateSymbol)) {
            continue;
        }
        Expression mappedAgg = aggMap.get(ex);
        if (mappedAgg != null) {
            if (mappedAgg instanceof AggregateSymbol) {
                compositeAggs.add((AggregateSymbol) mappedAgg);
            } else {
                compositeAggs.addAll(AggregateSymbolCollectorVisitor.getAggregates(mappedAgg, false));
                hasExpressionMapping = true;
            }
        } else {
            compositeAggs.add((AggregateSymbol) ex);
        }
    }
    if (!hasExpressionMapping) {
        // if no new expressions are created we can just modify the existing aggregates
        FrameUtil.correctSymbolMap(aggMap, groupNode);
    } else {
        // if new expressions are created we insert a view to handle the projection
        groupNode.getGroups().clear();
        GroupSymbol oldGroup = oldGroupingMap.asMap().keySet().iterator().next().getGroupSymbol();
        SymbolMap groupingMap = RelationalPlanner.buildGroupingNode(compositeAggs, (List<? extends Expression>) groupNode.getProperty(Info.GROUP_COLS), groupNode, context, idGenerator);
        ArrayList<Expression> projectCols = new ArrayList<Expression>(oldGroupingMap.asMap().size());
        SymbolMap correctedMap = new SymbolMap();
        Map<Expression, ElementSymbol> inverseMap = groupingMap.inserseMapping();
        for (Map.Entry<ElementSymbol, Expression> entry : oldGroupingMap.asMap().entrySet()) {
            Expression ses = null;
            if (entry.getValue() instanceof AggregateSymbol) {
                Expression ex = aggMap.get(entry.getValue());
                if (ex == null) {
                    ses = inverseMap.get(entry.getValue());
                } else if (ex instanceof AggregateSymbol) {
                    ses = inverseMap.get(ex);
                } else {
                    ExpressionMappingVisitor.mapExpressions(ex, inverseMap);
                    // $NON-NLS-1$
                    ses = new ExpressionSymbol("expr", ex);
                }
            } else {
                ses = inverseMap.get(entry.getValue());
            }
            ses = (Expression) ses.clone();
            projectCols.add(new AliasSymbol(Symbol.getShortName(entry.getKey()), ses));
            correctedMap.addMapping(entry.getKey(), SymbolMap.getExpression(ses));
        }
        PlanNode projectNode = groupNode.getParent();
        if (projectNode.getType() != NodeConstants.Types.PROJECT) {
            projectNode = NodeFactory.getNewNode(NodeConstants.Types.PROJECT);
            groupNode.addAsParent(projectNode);
            projectNode.setProperty(Info.PROJECT_COLS, projectCols);
            RuleDecomposeJoin.createSource(oldGroup, projectNode, correctedMap);
        } else {
            FrameUtil.convertFrame(projectNode, oldGroup, null, correctedMap.asMap(), metadata);
        }
    }
}
Also used : SymbolMap(org.teiid.query.sql.util.SymbolMap) PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) 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