Search in sources :

Example 96 with Expression

use of org.teiid.query.sql.symbol.Expression in project teiid by teiid.

the class RuleImplementJoinStrategy method execute.

/**
 * @see org.teiid.query.optimizer.relational.OptimizerRule#execute(org.teiid.query.optimizer.relational.plantree.PlanNode, org.teiid.query.metadata.QueryMetadataInterface, org.teiid.query.optimizer.capabilities.CapabilitiesFinder, org.teiid.query.optimizer.relational.RuleStack, org.teiid.query.analysis.AnalysisRecord, org.teiid.query.util.CommandContext)
 */
public PlanNode execute(PlanNode plan, QueryMetadataInterface metadata, CapabilitiesFinder capabilitiesFinder, RuleStack rules, AnalysisRecord analysisRecord, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
    for (PlanNode sourceNode : NodeEditor.findAllNodes(plan, NodeConstants.Types.SOURCE, NodeConstants.Types.ACCESS)) {
        SymbolMap references = (SymbolMap) sourceNode.getProperty(NodeConstants.Info.CORRELATED_REFERENCES);
        if (references != null) {
            Set<GroupSymbol> groups = GroupsUsedByElementsVisitor.getGroups(references.getValues());
            PlanNode joinNode = NodeEditor.findParent(sourceNode, NodeConstants.Types.JOIN, NodeConstants.Types.SOURCE);
            while (joinNode != null) {
                if (joinNode.getGroups().containsAll(groups)) {
                    joinNode.setProperty(NodeConstants.Info.JOIN_STRATEGY, JoinStrategyType.NESTED_TABLE);
                    Info info = Info.RIGHT_NESTED_REFERENCES;
                    if (!FrameUtil.findJoinSourceNode(joinNode.getFirstChild()).getGroups().containsAll(groups)) {
                        // $NON-NLS-1$
                        throw new AssertionError("Should not have reordered the join tree to reverse the lateral join");
                    }
                    SymbolMap map = (SymbolMap) joinNode.getProperty(info);
                    if (map == null) {
                        map = new SymbolMap();
                    }
                    joinNode.setProperty(info, map);
                    map.asUpdatableMap().putAll(references.asMap());
                    if (joinNode.getProperty(NodeConstants.Info.DEPENDENT_VALUE_SOURCE) != null) {
                        // $NON-NLS-1$
                        throw new AssertionError("Cannot use a depenedent join when the join involves a correlated nested table.");
                    }
                    break;
                }
                joinNode = NodeEditor.findParent(joinNode, NodeConstants.Types.JOIN, NodeConstants.Types.SOURCE);
            }
        }
    }
    for (PlanNode joinNode : NodeEditor.findAllNodes(plan, NodeConstants.Types.JOIN, NodeConstants.Types.ACCESS)) {
        JoinStrategyType stype = (JoinStrategyType) joinNode.getProperty(NodeConstants.Info.JOIN_STRATEGY);
        if (!JoinStrategyType.MERGE.equals(stype)) {
            continue;
        }
        List<Expression> leftExpressions = (List<Expression>) joinNode.getProperty(NodeConstants.Info.LEFT_EXPRESSIONS);
        List<Expression> rightExpressions = (List<Expression>) joinNode.getProperty(NodeConstants.Info.RIGHT_EXPRESSIONS);
        int origExpressionCount = leftExpressions.size();
        // check index information on each side
        // TODO: don't do null order compensation - in fact we should check what the order actually is, but we don't have that metadata
        Object key = null;
        boolean right = true;
        // we check the right first, since it should be larger
        if (joinNode.getLastChild().getType() == NodeConstants.Types.ACCESS && NewCalculateCostUtil.isSingleTable(joinNode.getLastChild())) {
            key = NewCalculateCostUtil.getKeyUsed(rightExpressions, null, metadata, null);
        }
        if (key == null && joinNode.getFirstChild().getType() == NodeConstants.Types.ACCESS && NewCalculateCostUtil.isSingleTable(joinNode.getFirstChild())) {
            key = NewCalculateCostUtil.getKeyUsed(leftExpressions, null, metadata, null);
            right = false;
        }
        JoinType joinType = (JoinType) joinNode.getProperty(NodeConstants.Info.JOIN_TYPE);
        /**
         * Don't push sorts for unbalanced inner joins, we prefer to use a processing time cost based decision
         */
        boolean pushLeft = true;
        boolean pushRight = true;
        if ((joinType == JoinType.JOIN_INNER || joinType == JoinType.JOIN_LEFT_OUTER) && context != null) {
            float leftCost = NewCalculateCostUtil.computeCostForTree(joinNode.getFirstChild(), metadata);
            float rightCost = NewCalculateCostUtil.computeCostForTree(joinNode.getLastChild(), metadata);
            if (leftCost != NewCalculateCostUtil.UNKNOWN_VALUE && rightCost != NewCalculateCostUtil.UNKNOWN_VALUE && (leftCost > context.getProcessorBatchSize() || rightCost > context.getProcessorBatchSize())) {
                // we use a larger constant here to ensure that we don't unwisely prevent pushdown
                pushLeft = leftCost < context.getProcessorBatchSize() || leftCost / rightCost < 8 || (key != null && !right);
                pushRight = rightCost < context.getProcessorBatchSize() || rightCost / leftCost < 8 || joinType == JoinType.JOIN_LEFT_OUTER || (key != null && right);
            }
        }
        if (key != null && joinNode.getProperty(NodeConstants.Info.DEPENDENT_VALUE_SOURCE) == null) {
            // redo the join predicates based upon the key alone
            List<Object> keyCols = metadata.getElementIDsInKey(key);
            int[] reorder = new int[keyCols.size()];
            LinkedHashSet<Integer> toCriteria = new LinkedHashSet<Integer>();
            List<Expression> keyExpressions = right ? rightExpressions : leftExpressions;
            Map<Object, Integer> indexMap = new LinkedHashMap<Object, Integer>();
            for (int i = 0; i < keyExpressions.size(); i++) {
                Expression ses = keyExpressions.get(i);
                if (!(ses instanceof ElementSymbol)) {
                    toCriteria.add(i);
                    continue;
                }
                Integer existing = indexMap.put(((ElementSymbol) ses).getMetadataID(), i);
                if (existing != null) {
                    toCriteria.add(existing);
                }
            }
            boolean found = true;
            for (int i = 0; i < keyCols.size(); i++) {
                Object id = keyCols.get(i);
                Integer index = indexMap.remove(id);
                if (index == null) {
                    found = false;
                    break;
                }
                reorder[i] = index;
            }
            if (found) {
                toCriteria.addAll(indexMap.values());
                List<Criteria> joinCriteria = (List<Criteria>) joinNode.getProperty(Info.NON_EQUI_JOIN_CRITERIA);
                for (int index : toCriteria) {
                    Expression lses = leftExpressions.get(index);
                    Expression rses = rightExpressions.get(index);
                    CompareCriteria cc = new CompareCriteria(lses, CompareCriteria.EQ, rses);
                    if (joinCriteria == null || joinCriteria.isEmpty()) {
                        joinCriteria = new ArrayList<Criteria>();
                    }
                    joinCriteria.add(cc);
                }
                joinNode.setProperty(Info.NON_EQUI_JOIN_CRITERIA, joinCriteria);
                leftExpressions = RelationalNode.projectTuple(reorder, leftExpressions);
                rightExpressions = RelationalNode.projectTuple(reorder, rightExpressions);
                joinNode.setProperty(NodeConstants.Info.LEFT_EXPRESSIONS, leftExpressions);
                joinNode.setProperty(NodeConstants.Info.RIGHT_EXPRESSIONS, rightExpressions);
            }
        }
        boolean pushedLeft = insertSort(joinNode.getFirstChild(), leftExpressions, joinNode, metadata, capabilitiesFinder, pushLeft, context);
        if (origExpressionCount == 1 && joinType == JoinType.JOIN_INNER && joinNode.getProperty(NodeConstants.Info.DEPENDENT_VALUE_SOURCE) != null && !joinNode.hasCollectionProperty(Info.NON_EQUI_JOIN_CRITERIA)) {
            Collection<Expression> output = (Collection<Expression>) joinNode.getProperty(NodeConstants.Info.OUTPUT_COLS);
            Collection<GroupSymbol> groups = GroupsUsedByElementsVisitor.getGroups(output);
            if (Collections.disjoint(groups, FrameUtil.findJoinSourceNode(joinNode.getFirstChild()).getGroups())) {
                pushRight = false;
                joinNode.setProperty(Info.IS_SEMI_DEP, Boolean.TRUE);
            }
        }
        boolean pushedRight = insertSort(joinNode.getLastChild(), rightExpressions, joinNode, metadata, capabilitiesFinder, pushRight, context);
        if ((!pushedRight || !pushedLeft) && (joinType == JoinType.JOIN_INNER || (joinType == JoinType.JOIN_LEFT_OUTER && !pushedLeft))) {
            joinNode.setProperty(NodeConstants.Info.JOIN_STRATEGY, JoinStrategyType.ENHANCED_SORT);
        }
    }
    return plan;
}
Also used : LinkedHashSet(java.util.LinkedHashSet) ElementSymbol(org.teiid.query.sql.symbol.ElementSymbol) Criteria(org.teiid.query.sql.lang.Criteria) CompareCriteria(org.teiid.query.sql.lang.CompareCriteria) LinkedHashMap(java.util.LinkedHashMap) PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) ArrayList(java.util.ArrayList) List(java.util.List) JoinStrategyType(org.teiid.query.processor.relational.JoinNode.JoinStrategyType) JoinType(org.teiid.query.sql.lang.JoinType) SymbolMap(org.teiid.query.sql.util.SymbolMap) Info(org.teiid.query.optimizer.relational.plantree.NodeConstants.Info) CompareCriteria(org.teiid.query.sql.lang.CompareCriteria) Expression(org.teiid.query.sql.symbol.Expression) GroupSymbol(org.teiid.query.sql.symbol.GroupSymbol) Collection(java.util.Collection)

Example 97 with Expression

use of org.teiid.query.sql.symbol.Expression 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 98 with Expression

use of org.teiid.query.sql.symbol.Expression 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 99 with Expression

use of org.teiid.query.sql.symbol.Expression in project teiid by teiid.

the class RuleMergeVirtual method checkProjectedSymbols.

/**
 * Check to ensure that we are not projecting a subquery or null dependent expressions
 * @param parentProject2
 */
private static boolean checkProjectedSymbols(PlanNode projectNode, GroupSymbol virtualGroup, PlanNode parentJoin, QueryMetadataInterface metadata, List<PlanNode> sources, boolean checkForNullDependent, PlanNode parentProject) {
    if (projectNode.hasBooleanProperty(Info.HAS_WINDOW_FUNCTIONS)) {
        boolean allow = false;
        PlanNode source = NodeEditor.findParent(parentProject, NodeConstants.Types.SOURCE);
        if (source != null) {
            PlanNode grandparentProject = NodeEditor.findParent(source, NodeConstants.Types.PROJECT);
            if (grandparentProject != null && grandparentProject.hasProperty(Info.INTO_GROUP)) {
                allow = true;
            }
        }
        if (!allow) {
            return false;
        }
    }
    List<Expression> selectSymbols = (List<Expression>) projectNode.getProperty(NodeConstants.Info.PROJECT_COLS);
    HashSet<GroupSymbol> groups = new HashSet<GroupSymbol>();
    for (PlanNode sourceNode : sources) {
        groups.addAll(sourceNode.getGroups());
    }
    return checkProjectedSymbols(virtualGroup, parentJoin, metadata, selectSymbols, groups, checkForNullDependent);
}
Also used : PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) Expression(org.teiid.query.sql.symbol.Expression) GroupSymbol(org.teiid.query.sql.symbol.GroupSymbol) ArrayList(java.util.ArrayList) LinkedList(java.util.LinkedList) List(java.util.List) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet)

Example 100 with Expression

use of org.teiid.query.sql.symbol.Expression 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)

Aggregations

Expression (org.teiid.query.sql.symbol.Expression)257 ElementSymbol (org.teiid.query.sql.symbol.ElementSymbol)104 ArrayList (java.util.ArrayList)74 GroupSymbol (org.teiid.query.sql.symbol.GroupSymbol)54 Test (org.junit.Test)53 PlanNode (org.teiid.query.optimizer.relational.plantree.PlanNode)50 List (java.util.List)49 Constant (org.teiid.query.sql.symbol.Constant)38 SymbolMap (org.teiid.query.sql.util.SymbolMap)37 HashMap (java.util.HashMap)22 LinkedList (java.util.LinkedList)22 Criteria (org.teiid.query.sql.lang.Criteria)22 Map (java.util.Map)21 ClobType (org.teiid.core.types.ClobType)16 CompareCriteria (org.teiid.query.sql.lang.CompareCriteria)16 HashSet (java.util.HashSet)13 LinkedHashSet (java.util.LinkedHashSet)13 AliasSymbol (org.teiid.query.sql.symbol.AliasSymbol)13 SearchedCaseExpression (org.teiid.query.sql.symbol.SearchedCaseExpression)13 OrderBy (org.teiid.query.sql.lang.OrderBy)12