Search in sources :

Example 11 with JoinType

use of org.teiid.query.sql.lang.JoinType in project teiid by teiid.

the class RuleMergeVirtual method checkJoinCriteria.

/**
 * check to see if criteria is used in a full outer join or has no groups and is on the inner side of an outer join. if this
 * is the case then the layers cannot be merged, since merging would possibly force the criteria to change it's position (into
 * the on clause or above the join).
 */
static boolean checkJoinCriteria(PlanNode frameRoot, GroupSymbol virtualGroup, PlanNode parentJoin) {
    if (parentJoin != null) {
        List<PlanNode> selectNodes = NodeEditor.findAllNodes(frameRoot, NodeConstants.Types.SELECT, NodeConstants.Types.SOURCE);
        Set<GroupSymbol> groups = new HashSet<GroupSymbol>();
        groups.add(virtualGroup);
        for (PlanNode selectNode : selectNodes) {
            if (selectNode.hasBooleanProperty(NodeConstants.Info.IS_PHANTOM)) {
                continue;
            }
            JoinType jt = JoinUtil.getJoinTypePreventingCriteriaOptimization(parentJoin, groups);
            if (jt != null && (jt == JoinType.JOIN_FULL_OUTER || selectNode.getGroups().size() == 0)) {
                return false;
            }
        }
    }
    return true;
}
Also used : PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) GroupSymbol(org.teiid.query.sql.symbol.GroupSymbol) JoinType(org.teiid.query.sql.lang.JoinType) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet)

Example 12 with JoinType

use of org.teiid.query.sql.lang.JoinType in project teiid by teiid.

the class RuleMergeVirtual method checkProjectedSymbols.

static boolean checkProjectedSymbols(GroupSymbol virtualGroup, PlanNode parentJoin, QueryMetadataInterface metadata, List<? extends Expression> selectSymbols, Set<GroupSymbol> groups, boolean checkForNullDependent) {
    if (checkForNullDependent) {
        checkForNullDependent = false;
        // check to see if there are projected literal on the inner side of an outer join that needs to be preserved
        if (parentJoin != null) {
            PlanNode joinToTest = parentJoin;
            while (joinToTest != null) {
                JoinType joinType = (JoinType) joinToTest.getProperty(NodeConstants.Info.JOIN_TYPE);
                if (joinType == JoinType.JOIN_FULL_OUTER) {
                    checkForNullDependent = true;
                    break;
                } else if (joinType == JoinType.JOIN_LEFT_OUTER && FrameUtil.findJoinSourceNode(joinToTest.getLastChild()).getGroups().contains(virtualGroup)) {
                    checkForNullDependent = true;
                    break;
                }
                joinToTest = NodeEditor.findParent(joinToTest.getParent(), NodeConstants.Types.JOIN, NodeConstants.Types.SOURCE);
            }
        }
    }
    for (int i = 0; i < selectSymbols.size(); i++) {
        Expression symbol = selectSymbols.get(i);
        Collection scalarSubqueries = ValueIteratorProviderCollectorVisitor.getValueIteratorProviders(symbol);
        if (!scalarSubqueries.isEmpty()) {
            return false;
        }
        if (checkForNullDependent && JoinUtil.isNullDependent(metadata, groups, SymbolMap.getExpression(symbol))) {
            return false;
        }
        // TEIID-16: We do not want to merge a non-deterministic scalar function
        if (FunctionCollectorVisitor.isNonDeterministic(symbol)) {
            return false;
        }
    }
    return true;
}
Also used : PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) Expression(org.teiid.query.sql.symbol.Expression) JoinType(org.teiid.query.sql.lang.JoinType) Collection(java.util.Collection)

Example 13 with JoinType

use of org.teiid.query.sql.lang.JoinType in project teiid by teiid.

the class RulePlanJoins method groupJoinsForPushing.

/**
 * This is a heuristic that checks for joins that may be pushed so they can be removed
 * before considering the joins that must be evaluated in MetaMatrix.
 *
 * By running this, we eliminate the need for running RuleRaiseAccess during join ordering
 *
 * @param metadata
 * @param joinRegion
 * @throws QueryMetadataException
 * @throws TeiidComponentException
 * @throws QueryPlannerException
 */
private void groupJoinsForPushing(QueryMetadataInterface metadata, CapabilitiesFinder capFinder, JoinRegion joinRegion, CommandContext context) throws QueryMetadataException, TeiidComponentException, QueryPlannerException {
    // TODO: consider moving select criteria if it is preventing a join from being pushed down
    // TODO: make the criteria checks based upon a guess at selectivity
    Map accessMap = getAccessMap(metadata, capFinder, joinRegion);
    boolean structureChanged = false;
    // search for combinations of join sources that should be pushed down
    for (Iterator accessNodeIter = accessMap.entrySet().iterator(); accessNodeIter.hasNext(); ) {
        Map.Entry entry = (Map.Entry) accessNodeIter.next();
        List<PlanNode> accessNodes = (List) entry.getValue();
        if (accessNodes.size() < 2) {
            continue;
        }
        int secondPass = -1;
        for (int i = accessNodes.size() - 1; i >= 0; i--) {
            PlanNode accessNode1 = accessNodes.get(i);
            Object modelId = RuleRaiseAccess.getModelIDFromAccess(accessNode1, metadata);
            SupportedJoinCriteria sjc = CapabilitiesUtil.getSupportedJoinCriteria(modelId, metadata, capFinder);
            int discoveredJoin = -1;
            for (int k = (secondPass == -1 ? accessNodes.size() - 1 : secondPass); k >= 0; k--) {
                if (k == i) {
                    continue;
                }
                PlanNode accessNode2 = accessNodes.get(k);
                List<PlanNode> criteriaNodes = joinRegion.getCriteriaNodes();
                List<PlanNode> joinCriteriaNodes = new LinkedList<PlanNode>();
                /* hasJoinCriteria will be true if
                     *  1. there is criteria between accessNode1 and accessNode2 exclusively
                     *  2. there is criteria between some other source (not the same logical connector) and accessNode1 or accessNode2
                     *  
                     *  Ideally we should be a little smarter in case 2 
                     *    - pushing down a same source cross join can be done if we know that a dependent join will be performed 
                     */
                boolean hasJoinCriteria = false;
                LinkedList<Criteria> joinCriteria = new LinkedList<Criteria>();
                for (PlanNode critNode : criteriaNodes) {
                    Set<PlanNode> sources = joinRegion.getCritieriaToSourceMap().get(critNode);
                    if (sources == null) {
                        continue;
                    }
                    if (sources.contains(accessNode1)) {
                        if (sources.contains(accessNode2) && sources.size() == 2) {
                            Criteria crit = (Criteria) critNode.getProperty(NodeConstants.Info.SELECT_CRITERIA);
                            if (RuleRaiseAccess.isSupportedJoinCriteria(sjc, crit, modelId, metadata, capFinder, null)) {
                                joinCriteriaNodes.add(critNode);
                                joinCriteria.add(crit);
                            }
                        } else if (!accessNodes.containsAll(sources)) {
                            hasJoinCriteria = true;
                        }
                    } else if (sources.contains(accessNode2) && !accessNodes.containsAll(sources)) {
                        hasJoinCriteria = true;
                    }
                }
                /*
                     * If we failed to find direct criteria, a cross join may still be acceptable
                     */
                if (joinCriteriaNodes.isEmpty() && (hasJoinCriteria || !canPushCrossJoin(metadata, accessNode1, accessNode2))) {
                    continue;
                }
                List<PlanNode> toTest = Arrays.asList(accessNode1, accessNode2);
                JoinType joinType = joinCriteria.isEmpty() ? JoinType.JOIN_CROSS : JoinType.JOIN_INNER;
                /*
                     * We need to limit the heuristic grouping as we don't want to create larger source queries than necessary
                     */
                boolean shouldPush = true;
                int sourceCount = NodeEditor.findAllNodes(accessNode1, NodeConstants.Types.SOURCE, NodeConstants.Types.SOURCE).size();
                sourceCount += NodeEditor.findAllNodes(accessNode2, NodeConstants.Types.SOURCE, NodeConstants.Types.SOURCE).size();
                if (!context.getOptions().isAggressiveJoinGrouping() && accessMap.size() > 1 && joinType == JoinType.JOIN_INNER && (sourceCount > 2 && (accessNode1.hasProperty(Info.MAKE_DEP) || accessNode2.hasProperty(Info.MAKE_DEP)) || sourceCount > 3) && !canPushCrossJoin(metadata, accessNode1, accessNode2)) {
                    Collection<GroupSymbol> leftGroups = accessNode1.getGroups();
                    Collection<GroupSymbol> rightGroups = accessNode2.getGroups();
                    List<Expression> leftExpressions = new ArrayList<Expression>();
                    List<Expression> rightExpressions = new ArrayList<Expression>();
                    List<Criteria> nonEquiJoinCriteria = new ArrayList<Criteria>();
                    RuleChooseJoinStrategy.separateCriteria(leftGroups, rightGroups, leftExpressions, rightExpressions, joinCriteria, nonEquiJoinCriteria);
                    // allow a 1-1 join
                    if (!NewCalculateCostUtil.usesKey(accessNode1, leftExpressions, metadata) || !NewCalculateCostUtil.usesKey(accessNode2, rightExpressions, metadata)) {
                        // don't push heuristically
                        shouldPush = false;
                    }
                }
                // try to push to the source
                if (!shouldPush || RuleRaiseAccess.canRaiseOverJoin(toTest, metadata, capFinder, joinCriteria, joinType, null, context, secondPass != -1, false) == null) {
                    if (secondPass == -1 && sjc != SupportedJoinCriteria.KEY && discoveredJoin == -1) {
                        for (Criteria criteria : joinCriteria) {
                            if (criteria instanceof CompareCriteria && ((CompareCriteria) criteria).isOptional()) {
                                discoveredJoin = k;
                            }
                        }
                    }
                    continue;
                }
                secondPass = -1;
                discoveredJoin = -1;
                structureChanged = true;
                // remove the information that is no longer relevant to the join region
                joinRegion.getCritieriaToSourceMap().keySet().removeAll(joinCriteriaNodes);
                joinRegion.getCriteriaNodes().removeAll(joinCriteriaNodes);
                joinRegion.getJoinSourceNodes().remove(accessNode1);
                joinRegion.getJoinSourceNodes().remove(accessNode2);
                accessNodes.remove(i);
                accessNodes.remove(k < i ? k : k - 1);
                // build a new join node
                PlanNode joinNode = createJoinNode(accessNode1, accessNode2, joinCriteria, joinType);
                PlanNode newAccess = RuleRaiseAccess.raiseAccessOverJoin(joinNode, joinNode.getFirstChild(), entry.getKey(), capFinder, metadata, false);
                for (PlanNode critNode : joinCriteriaNodes) {
                    critNode.removeFromParent();
                    critNode.removeAllChildren();
                }
                for (Set<PlanNode> source : joinRegion.getCritieriaToSourceMap().values()) {
                    if (source.remove(accessNode1) || source.remove(accessNode2)) {
                        source.add(newAccess);
                    }
                }
                joinRegion.getJoinSourceNodes().put(newAccess, newAccess);
                accessNodes.add(newAccess);
                i = accessNodes.size();
                k = accessNodes.size();
                break;
            }
            if (discoveredJoin != -1) {
                // rerun with the discoveredJoin criteria
                i++;
                secondPass = discoveredJoin;
            }
        }
    }
    if (structureChanged) {
        joinRegion.reconstructJoinRegoin();
    }
}
Also used : JoinType(org.teiid.query.sql.lang.JoinType) Criteria(org.teiid.query.sql.lang.Criteria) CompareCriteria(org.teiid.query.sql.lang.CompareCriteria) SupportedJoinCriteria(org.teiid.translator.ExecutionFactory.SupportedJoinCriteria) CompareCriteria(org.teiid.query.sql.lang.CompareCriteria) PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) SupportedJoinCriteria(org.teiid.translator.ExecutionFactory.SupportedJoinCriteria) Expression(org.teiid.query.sql.symbol.Expression) GroupSymbol(org.teiid.query.sql.symbol.GroupSymbol) SymbolMap(org.teiid.query.sql.util.SymbolMap)

Example 14 with JoinType

use of org.teiid.query.sql.lang.JoinType in project teiid by teiid.

the class RulePushAggregates method canPush.

/**
 * Ensures that we are only pushing through inner equi joins or cross joins.  Also collects the necessary staged grouping symbols
 * @param aggregates
 * @param metadata
 * @return null if we cannot push otherwise the target join node
 */
private PlanNode canPush(PlanNode groupNode, Set<Expression> stagedGroupingSymbols, PlanNode planNode, Collection<AggregateSymbol> aggregates, QueryMetadataInterface metadata) {
    PlanNode parentJoin = planNode.getParent();
    Set<GroupSymbol> groups = FrameUtil.findJoinSourceNode(planNode).getGroups();
    PlanNode result = planNode;
    while (parentJoin != groupNode) {
        if (parentJoin.getType() != NodeConstants.Types.JOIN) {
            return null;
        }
        JoinType joinType = (JoinType) parentJoin.getProperty(NodeConstants.Info.JOIN_TYPE);
        if (joinType.isOuter() && aggregates != null) {
            for (AggregateSymbol as : aggregates) {
                if (as.getArgs().length != 1) {
                    continue;
                }
                Collection<GroupSymbol> expressionGroups = GroupsUsedByElementsVisitor.getGroups(as.getArg(0));
                Collection<GroupSymbol> innerGroups = null;
                if (joinType == JoinType.JOIN_LEFT_OUTER) {
                    innerGroups = FrameUtil.findJoinSourceNode(parentJoin.getLastChild()).getGroups();
                } else {
                    // full outer
                    innerGroups = parentJoin.getGroups();
                }
                if (Collections.disjoint(expressionGroups, innerGroups)) {
                    continue;
                }
                if (as.getFunctionDescriptor() != null && as.getFunctionDescriptor().isNullDependent()) {
                    return null;
                }
                if (as.getArgs().length == 1 && JoinUtil.isNullDependent(metadata, innerGroups, as.getArg(0))) {
                    return null;
                }
            }
        }
        // check for sideways correlation
        PlanNode other = null;
        if (planNode == parentJoin.getFirstChild()) {
            other = parentJoin.getLastChild();
        } else {
            other = parentJoin.getFirstChild();
        }
        SymbolMap map = (SymbolMap) other.getProperty(NodeConstants.Info.CORRELATED_REFERENCES);
        if (map != null) {
            return null;
        // TODO: handle this case. the logic would look something like below,
        // but we would need to handle the updating of the symbol maps in addGroupBy
        /*filterExpressions(stagedGroupingSymbols, groups, map.getKeys(), true);
        		for (ElementSymbol ex : map.getKeys()) {
    				if (DataTypeManager.isNonComparable(DataTypeManager.getDataTypeName(ex.getType()))) {
    					return null;
    				}
	        	}*/
        }
        if (!parentJoin.hasCollectionProperty(NodeConstants.Info.LEFT_EXPRESSIONS) || !parentJoin.hasCollectionProperty(NodeConstants.Info.RIGHT_EXPRESSIONS)) {
            List<Criteria> criteria = (List<Criteria>) parentJoin.getProperty(Info.JOIN_CRITERIA);
            if (!findStagedGroupingExpressions(groups, criteria, stagedGroupingSymbols)) {
                return null;
            }
        } else {
            List<Criteria> criteria = (List<Criteria>) parentJoin.getProperty(Info.NON_EQUI_JOIN_CRITERIA);
            if (!findStagedGroupingExpressions(groups, criteria, stagedGroupingSymbols)) {
                return null;
            }
            // we move the target up if the filtered expressions introduce outside groups
            if (planNode == parentJoin.getFirstChild()) {
                if (filterExpressions(stagedGroupingSymbols, groups, (List<Expression>) parentJoin.getProperty(NodeConstants.Info.LEFT_EXPRESSIONS), true)) {
                    result = parentJoin;
                    groups = result.getGroups();
                }
            } else {
                if (filterExpressions(stagedGroupingSymbols, groups, (List<Expression>) parentJoin.getProperty(NodeConstants.Info.RIGHT_EXPRESSIONS), true)) {
                    result = parentJoin;
                    groups = result.getGroups();
                }
            }
        }
        planNode = parentJoin;
        parentJoin = parentJoin.getParent();
    }
    if (result.getParent() == groupNode) {
        // can't be pushed as we are already at the direct child
        return null;
    }
    return result;
}
Also used : PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) JoinType(org.teiid.query.sql.lang.JoinType) SymbolMap(org.teiid.query.sql.util.SymbolMap) IsNullCriteria(org.teiid.query.sql.lang.IsNullCriteria) Criteria(org.teiid.query.sql.lang.Criteria) CompareCriteria(org.teiid.query.sql.lang.CompareCriteria)

Example 15 with JoinType

use of org.teiid.query.sql.lang.JoinType in project teiid by teiid.

the class RuleChooseDependent method markDependent.

/**
 * Mark the specified access node to be made dependent
 * @param sourceNode Node to make dependent
 * @param dca
 * @param rules
 * @param analysisRecord
 * @param commandContext
 * @param capFinder
 * @throws TeiidComponentException
 * @throws QueryMetadataException
 * @throws QueryPlannerException
 */
boolean markDependent(PlanNode sourceNode, PlanNode joinNode, QueryMetadataInterface metadata, DependentCostAnalysis dca, Boolean bound, CapabilitiesFinder capabilitiesFinder, CommandContext context, RuleStack rules, AnalysisRecord analysisRecord) throws QueryMetadataException, TeiidComponentException, QueryPlannerException {
    boolean isLeft = joinNode.getFirstChild() == sourceNode;
    // Get new access join node properties based on join criteria
    List independentExpressions = (List) (isLeft ? joinNode.getProperty(NodeConstants.Info.RIGHT_EXPRESSIONS) : joinNode.getProperty(NodeConstants.Info.LEFT_EXPRESSIONS));
    List dependentExpressions = (List) (isLeft ? joinNode.getProperty(NodeConstants.Info.LEFT_EXPRESSIONS) : joinNode.getProperty(NodeConstants.Info.RIGHT_EXPRESSIONS));
    if (independentExpressions == null || independentExpressions.isEmpty()) {
        return false;
    }
    PlanNode indNode = isLeft ? joinNode.getLastChild() : joinNode.getFirstChild();
    if (bound == null) {
        List<PlanNode> sources = NodeEditor.findAllNodes(indNode, NodeConstants.Types.SOURCE);
        for (PlanNode planNode : sources) {
            for (GroupSymbol gs : planNode.getGroups()) {
                if (gs.isTempTable() && metadata.getCardinality(gs.getMetadataID()) == QueryMetadataInterface.UNKNOWN_CARDINALITY) {
                    bound = true;
                    break;
                }
            }
        }
        if (bound == null) {
            bound = false;
        }
    }
    MakeDep makeDep = (MakeDep) sourceNode.getProperty(Info.MAKE_DEP);
    if (fullPushOnly) {
        fullyPush(sourceNode, joinNode, metadata, capabilitiesFinder, context, indNode, rules, makeDep, analysisRecord, independentExpressions);
        return false;
    }
    // Check that for a outer join the dependent side must be the inner
    JoinType jtype = (JoinType) joinNode.getProperty(NodeConstants.Info.JOIN_TYPE);
    if (jtype == JoinType.JOIN_FULL_OUTER || (jtype.isOuter() && JoinUtil.getInnerSideJoinNodes(joinNode)[0] != sourceNode)) {
        // $NON-NLS-1$ //$NON-NLS-2$
        sourceNode.recordDebugAnnotation("node is on outer side of the join", null, "Rejecting dependent join", analysisRecord, null);
        return false;
    }
    String id = nextId();
    // Create DependentValueSource and set on the independent side as this will feed the values
    joinNode.setProperty(NodeConstants.Info.DEPENDENT_VALUE_SOURCE, id);
    PlanNode depNode = isLeft ? joinNode.getFirstChild() : joinNode.getLastChild();
    depNode = FrameUtil.findJoinSourceNode(depNode);
    if (!depNode.hasCollectionProperty(Info.ACCESS_PATTERNS)) {
        // in some situations a federated join will span multiple tables using the same key
        handleDuplicate(joinNode, isLeft, independentExpressions, dependentExpressions);
        handleDuplicate(joinNode, !isLeft, dependentExpressions, independentExpressions);
    }
    PlanNode crit = getDependentCriteriaNode(id, independentExpressions, dependentExpressions, indNode, metadata, dca, bound, makeDep);
    sourceNode.addAsParent(crit);
    if (isLeft) {
        JoinUtil.swapJoinChildren(joinNode);
    }
    return true;
}
Also used : PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) GroupSymbol(org.teiid.query.sql.symbol.GroupSymbol) MakeDep(org.teiid.query.sql.lang.Option.MakeDep) JoinType(org.teiid.query.sql.lang.JoinType) ArrayList(java.util.ArrayList) List(java.util.List)

Aggregations

JoinType (org.teiid.query.sql.lang.JoinType)21 PlanNode (org.teiid.query.optimizer.relational.plantree.PlanNode)17 List (java.util.List)11 Criteria (org.teiid.query.sql.lang.Criteria)10 GroupSymbol (org.teiid.query.sql.symbol.GroupSymbol)10 ArrayList (java.util.ArrayList)9 Expression (org.teiid.query.sql.symbol.Expression)8 CompareCriteria (org.teiid.query.sql.lang.CompareCriteria)6 LinkedList (java.util.LinkedList)5 SymbolMap (org.teiid.query.sql.util.SymbolMap)5 ElementSymbol (org.teiid.query.sql.symbol.ElementSymbol)4 LinkedHashSet (java.util.LinkedHashSet)3 IsNullCriteria (org.teiid.query.sql.lang.IsNullCriteria)3 Collection (java.util.Collection)2 HashMap (java.util.HashMap)2 HashSet (java.util.HashSet)2 Iterator (java.util.Iterator)2 LinkedHashMap (java.util.LinkedHashMap)2 Set (java.util.Set)2 OrderBy (org.teiid.query.sql.lang.OrderBy)2