Search in sources :

Example 1 with Evaluatable

use of org.teiid.query.sql.lang.SubqueryContainer.Evaluatable in project teiid by teiid.

the class RuleRaiseAccess method canRaiseOverJoin.

static Object canRaiseOverJoin(List<PlanNode> children, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, List<Criteria> crits, JoinType type, AnalysisRecord record, CommandContext context, boolean considerOptional, boolean considerLateral) throws QueryMetadataException, TeiidComponentException {
    // we only want to consider binary joins
    if (children.size() != 2) {
        return null;
    }
    Object modelID = null;
    boolean multiSource = false;
    Set<Object> groupIDs = new HashSet<Object>();
    int groupCount = 0;
    LinkedList<CompareCriteria> thetaCriteria = new LinkedList<CompareCriteria>();
    SupportedJoinCriteria sjc = null;
    for (PlanNode childNode : children) {
        boolean lateral = false;
        boolean procedure = false;
        if (considerLateral && childNode.getType() == NodeConstants.Types.SOURCE && childNode.getFirstChild() != null && childNode.getProperty(Info.CORRELATED_REFERENCES) != null) {
            if (FrameUtil.getNestedPlan(childNode.getFirstChild()) != null) {
                return null;
            }
            Command command = FrameUtil.getNonQueryCommand(childNode.getFirstChild());
            if (command instanceof StoredProcedure) {
                procedure = true;
                if (!CapabilitiesUtil.supports(Capability.QUERY_FROM_PROCEDURE_TABLE, modelID, metadata, capFinder)) {
                    return null;
                }
                // this should look like source/project/access - if not, then back out
                if (childNode.getFirstChild().getType() == NodeConstants.Types.PROJECT) {
                    childNode = childNode.getFirstChild();
                }
            }
            if (childNode.getFirstChild().getType() == NodeConstants.Types.ACCESS) {
                childNode = childNode.getFirstChild();
            } else {
                return null;
            }
            lateral = true;
        }
        if (childNode.getType() != NodeConstants.Types.ACCESS) {
            return null;
        }
        if (childNode.hasCollectionProperty(NodeConstants.Info.ACCESS_PATTERNS)) {
            // $NON-NLS-1$ //$NON-NLS-2$
            childNode.recordDebugAnnotation("access pattern not satisfied by join", modelID, "not pushing parent join", record, metadata);
            return null;
        }
        Object accessModelID = getModelIDFromAccess(childNode, metadata);
        if (accessModelID == null) {
            return null;
        }
        groupCount += childNode.getGroups().size();
        // Add all group metadata IDs to the list but check before each to make
        // sure group hasn't already been seen - if so, bail out - this is a self join
        // Unless model supports self joins, in which case, don't bail out.
        boolean supportsSelfJoins = CapabilitiesUtil.supportsSelfJoins(accessModelID, metadata, capFinder);
        if (!supportsSelfJoins) {
            for (GroupSymbol groupSymbol : childNode.getGroups()) {
                Object groupID = groupSymbol.getMetadataID();
                if (!groupIDs.add(groupID)) {
                    // Already seen group - can't raise access over self join
                    return null;
                }
            }
        }
        // check the join criteria now that we know the model
        if (modelID == null) {
            if (!CapabilitiesUtil.supportsJoin(accessModelID, type, metadata, capFinder)) {
                return null;
            }
            sjc = CapabilitiesUtil.getSupportedJoinCriteria(accessModelID, metadata, capFinder);
            // see if we can emulate the inner join using an outer
            if (!type.isOuter() && !CapabilitiesUtil.supports(Capability.QUERY_FROM_JOIN_INNER, accessModelID, metadata, capFinder) && (crits != null) && !crits.isEmpty()) {
                // TODO: the IS NOT NULL check is not strictly needed as we could check predicates to see if we are already null filtering
                if (!CapabilitiesUtil.supports(Capability.CRITERIA_ISNULL, accessModelID, metadata, capFinder) || !CapabilitiesUtil.supports(Capability.CRITERIA_NOT, accessModelID, metadata, capFinder)) {
                    return null;
                }
                if (sjc == SupportedJoinCriteria.ANY) {
                    // quick check to see if we can find an element to be nullable
                    boolean valid = false;
                    for (Criteria crit : crits) {
                        if (!(crit instanceof CompareCriteria)) {
                            continue;
                        }
                        CompareCriteria cc = (CompareCriteria) crit;
                        if ((cc.getLeftExpression() instanceof ElementSymbol) || (cc.getRightExpression() instanceof ElementSymbol)) {
                            valid = true;
                        }
                    }
                    if (!valid) {
                        // TODO: check if any of the already pushed predicates can satisfy
                        return null;
                    }
                }
            }
            if (crits != null && !crits.isEmpty()) {
                for (Criteria crit : crits) {
                    if (!isSupportedJoinCriteria(sjc, crit, accessModelID, metadata, capFinder, record)) {
                        if (crit instanceof CompareCriteria) {
                            CompareCriteria cc = (CompareCriteria) crit;
                            if (cc.isOptional()) {
                                cc.setOptional(true);
                                continue;
                            }
                        }
                        // TODO: plan based upon a predicate subset when possible
                        return null;
                    } else if (crit instanceof CompareCriteria) {
                        thetaCriteria.add((CompareCriteria) crit);
                    }
                }
                if (sjc == SupportedJoinCriteria.KEY) {
                    PlanNode left = children.get(0);
                    PlanNode right = children.get(1);
                    if (left.getGroups().size() != 1) {
                        if (right.getGroups().size() != 1) {
                            // require the simple case of 1 side being a single group
                            return null;
                        }
                        if (type != JoinType.JOIN_INNER) {
                            return null;
                        }
                        left = children.get(1);
                        right = children.get(0);
                    }
                    LinkedList<Expression> leftExpressions = new LinkedList<Expression>();
                    LinkedList<Expression> rightExpressions = new LinkedList<Expression>();
                    RuleChooseJoinStrategy.separateCriteria(left.getGroups(), right.getGroups(), leftExpressions, rightExpressions, crits, new LinkedList<Criteria>());
                    ArrayList<Object> leftIds = new ArrayList<Object>(leftExpressions.size());
                    ArrayList<Object> rightIds = new ArrayList<Object>(rightExpressions.size());
                    for (Expression expr : leftExpressions) {
                        if (expr instanceof ElementSymbol) {
                            leftIds.add(((ElementSymbol) expr).getMetadataID());
                        }
                    }
                    GroupSymbol rightGroup = null;
                    for (Expression expr : rightExpressions) {
                        if (expr instanceof ElementSymbol) {
                            ElementSymbol es = (ElementSymbol) expr;
                            if (rightGroup == null) {
                                rightGroup = es.getGroupSymbol();
                            } else if (!rightGroup.equals(es.getGroupSymbol())) {
                                return null;
                            }
                            rightIds.add(es.getMetadataID());
                        }
                    }
                    if (rightGroup == null) {
                        return null;
                    }
                    if (!matchesForeignKey(metadata, leftIds, rightIds, left.getGroups().iterator().next(), true, !type.isOuter() || type == JoinType.JOIN_LEFT_OUTER) && !matchesForeignKey(metadata, rightIds, leftIds, rightGroup, true, !type.isOuter())) {
                        return null;
                    }
                }
            }
            if (sjc != SupportedJoinCriteria.ANY && thetaCriteria.isEmpty()) {
                // cross join not supported
                return null;
            }
            if (type == JoinType.JOIN_LEFT_OUTER && !CapabilitiesUtil.supports(Capability.CRITERIA_ON_SUBQUERY, accessModelID, metadata, capFinder)) {
                PlanNode right = children.get(1);
                for (PlanNode node : NodeEditor.findAllNodes(right, NodeConstants.Types.SELECT, NodeConstants.Types.SOURCE)) {
                    for (SubqueryContainer<?> subqueryContainer : ValueIteratorProviderCollectorVisitor.getValueIteratorProviders((Criteria) node.getProperty(NodeConstants.Info.SELECT_CRITERIA))) {
                        if (!(subqueryContainer instanceof Evaluatable) || subqueryContainer.getCommand().getCorrelatedReferences() != null) {
                            return null;
                        }
                    }
                }
            }
            modelID = accessModelID;
            multiSource = childNode.hasBooleanProperty(Info.IS_MULTI_SOURCE);
        } else if (!CapabilitiesUtil.isSameConnector(modelID, accessModelID, metadata, capFinder) && !isConformed(metadata, capFinder, (Set<Object>) childNode.getProperty(Info.CONFORMED_SOURCES), modelID, (Set<Object>) children.get(0).getProperty(Info.CONFORMED_SOURCES), accessModelID)) {
            return null;
        } else if ((multiSource || childNode.hasBooleanProperty(Info.IS_MULTI_SOURCE)) && !context.getOptions().isImplicitMultiSourceJoin()) {
            // only allow raise if partitioned
            boolean multiSourceOther = childNode.hasBooleanProperty(Info.IS_MULTI_SOURCE);
            if (multiSource && multiSourceOther && (type == JoinType.JOIN_ANTI_SEMI || type == JoinType.JOIN_CROSS)) {
                return null;
            }
            ArrayList<Expression> leftExpressions = new ArrayList<Expression>();
            ArrayList<Expression> rightExpressions = new ArrayList<Expression>();
            RuleChooseJoinStrategy.separateCriteria(children.get(0).getGroups(), children.get(1).getGroups(), leftExpressions, rightExpressions, crits, new LinkedList<Criteria>());
            boolean needsOtherCrit = sjc != SupportedJoinCriteria.ANY;
            boolean partitioned = !multiSource || !multiSourceOther;
            for (int i = 0; i < leftExpressions.size() && (!partitioned || needsOtherCrit); i++) {
                boolean multi = isMultiSourceColumn(metadata, leftExpressions.get(i), children.get(0)) && isMultiSourceColumn(metadata, rightExpressions.get(i), children.get(1));
                if (multi) {
                    partitioned = true;
                } else {
                    needsOtherCrit = false;
                }
            }
            if (needsOtherCrit || !partitioned) {
                return null;
            }
        }
        if (lateral) {
            if ((!CapabilitiesUtil.supports(Capability.QUERY_FROM_JOIN_LATERAL, modelID, metadata, capFinder) || (crits != null && !crits.isEmpty() && !CapabilitiesUtil.supports(Capability.QUERY_FROM_JOIN_LATERAL_CONDITION, accessModelID, metadata, capFinder)))) {
                return null;
            }
            if (!procedure && CapabilitiesUtil.supports(Capability.QUERY_ONLY_FROM_JOIN_LATERAL_PROCEDURE, accessModelID, metadata, capFinder)) {
                return null;
            }
        }
    }
    // end walking through join node's children
    int maxGroups = CapabilitiesUtil.getMaxFromGroups(modelID, metadata, capFinder);
    if (maxGroups != -1 && maxGroups < groupCount) {
        return null;
    }
    if (crits != null && !crits.isEmpty()) {
        if (considerOptional) {
            for (CompareCriteria criteria : thetaCriteria) {
                criteria.setOptional(false);
            }
        } else {
            boolean hasCriteria = false;
            for (CompareCriteria criteria : thetaCriteria) {
                if (criteria.getIsOptional() == null || !criteria.isOptional()) {
                    hasCriteria = true;
                    break;
                }
            }
            if (!hasCriteria) {
                return null;
            }
        }
    }
    return modelID;
}
Also used : ElementSymbol(org.teiid.query.sql.symbol.ElementSymbol) HashSet(java.util.HashSet) Set(java.util.Set) ArrayList(java.util.ArrayList) SupportedJoinCriteria(org.teiid.translator.ExecutionFactory.SupportedJoinCriteria) LinkedList(java.util.LinkedList) 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) Evaluatable(org.teiid.query.sql.lang.SubqueryContainer.Evaluatable) HashSet(java.util.HashSet)

Example 2 with Evaluatable

use of org.teiid.query.sql.lang.SubqueryContainer.Evaluatable in project teiid by teiid.

the class RuleCollapseSource method buildQuery.

void buildQuery(PlanNode accessRoot, PlanNode node, Query query, CommandContext context, CapabilitiesFinder capFinder) throws QueryMetadataException, TeiidComponentException, QueryPlannerException {
    QueryMetadataInterface metadata = context.getMetadata();
    // visit source and join nodes as they appear
    Object modelID = RuleRaiseAccess.getModelIDFromAccess(accessRoot, metadata);
    switch(node.getType()) {
        case NodeConstants.Types.JOIN:
            {
                prepareSubqueries(node.getSubqueryContainers());
                JoinType joinType = (JoinType) node.getProperty(NodeConstants.Info.JOIN_TYPE);
                List<Criteria> crits = (List<Criteria>) node.getProperty(NodeConstants.Info.JOIN_CRITERIA);
                if (crits == null || crits.isEmpty()) {
                    crits = new ArrayList<Criteria>();
                } else {
                    RuleChooseJoinStrategy.filterOptionalCriteria(crits, false);
                    if (crits.isEmpty() && joinType == JoinType.JOIN_INNER) {
                        joinType = JoinType.JOIN_CROSS;
                    }
                }
                PlanNode left = node.getFirstChild();
                PlanNode right = node.getLastChild();
                /* special handling is needed to determine criteria placement.
                 * 
                 * if the join is a left outer join, criteria from the right side will be added to the on clause
                 */
                Criteria savedCriteria = null;
                buildQuery(accessRoot, left, query, context, capFinder);
                if (joinType == JoinType.JOIN_LEFT_OUTER) {
                    savedCriteria = query.getCriteria();
                    query.setCriteria(null);
                }
                buildQuery(accessRoot, right, query, context, capFinder);
                if (joinType == JoinType.JOIN_LEFT_OUTER) {
                    moveWhereClauseIntoOnClause(query, crits);
                    query.setCriteria(savedCriteria);
                }
                if (joinType == JoinType.JOIN_LEFT_OUTER || joinType == JoinType.JOIN_FULL_OUTER) {
                    boolean subqueryOn = CapabilitiesUtil.supports(Capability.CRITERIA_ON_SUBQUERY, modelID, metadata, capFinder);
                    if (!subqueryOn) {
                        for (SubqueryContainer<?> subqueryContainer : ValueIteratorProviderCollectorVisitor.getValueIteratorProviders(crits)) {
                            if (subqueryContainer instanceof Evaluatable && subqueryContainer.getCommand().getCorrelatedReferences() == null) {
                                ((Evaluatable) subqueryContainer).setShouldEvaluate(true);
                            } else {
                                // $NON-NLS-1$
                                throw new AssertionError("On clause not expected to contain non-evaluatable subqueries");
                            }
                        }
                    }
                }
                // Get last two clauses added to the FROM and combine them into a JoinPredicate
                From from = query.getFrom();
                List<FromClause> clauses = from.getClauses();
                int lastClause = clauses.size() - 1;
                FromClause clause1 = clauses.get(lastClause - 1);
                FromClause clause2 = clauses.get(lastClause);
                // so this may not be needed moving forward
                if (!joinType.isOuter() && !CapabilitiesUtil.supports(Capability.QUERY_FROM_JOIN_INNER, modelID, metadata, capFinder)) {
                    joinType = JoinType.JOIN_LEFT_OUTER;
                    if (!crits.isEmpty()) {
                        if (!useLeftOuterJoin(query, metadata, crits, right.getGroups())) {
                            if (!useLeftOuterJoin(query, metadata, crits, left.getGroups())) {
                                // $NON-NLS-1$
                                throw new AssertionError("Could not convert inner to outer join.");
                            }
                            FromClause temp = clause1;
                            clause1 = clause2;
                            clause2 = temp;
                        }
                    }
                }
                // correct the criteria or the join type if necessary
                if (joinType != JoinType.JOIN_CROSS && crits.isEmpty()) {
                    crits.add(QueryRewriter.TRUE_CRITERIA);
                } else if (joinType == JoinType.JOIN_CROSS && !crits.isEmpty()) {
                    joinType = JoinType.JOIN_INNER;
                }
                JoinPredicate jp = new JoinPredicate(clause1, clause2, joinType, crits);
                // Replace last two clauses with new predicate
                clauses.remove(lastClause);
                clauses.set(lastClause - 1, jp);
                return;
            }
        case NodeConstants.Types.SOURCE:
            {
                boolean pushedTableProcedure = false;
                GroupSymbol symbol = node.getGroups().iterator().next();
                if (node.hasBooleanProperty(Info.INLINE_VIEW)) {
                    PlanNode child = node.getFirstChild();
                    QueryCommand newQuery = createQuery(context, capFinder, accessRoot, child);
                    // ensure that the group is consistent
                    SubqueryFromClause sfc = new SubqueryFromClause(symbol, newQuery);
                    SymbolMap map = (SymbolMap) node.getProperty(NodeConstants.Info.CORRELATED_REFERENCES);
                    if (map != null) {
                        ExpressionMappingVisitor visitor = new RuleMergeCriteria.ReferenceReplacementVisitor(map);
                        DeepPostOrderNavigator.doVisit(newQuery, visitor);
                        sfc.setLateral(true);
                    }
                    query.getFrom().addClause(sfc);
                    // ensure that the column names are consistent
                    Query q = newQuery.getProjectedQuery();
                    List<Expression> expressions = q.getSelect().getSymbols();
                    List<Expression> outputCols = (List<Expression>) node.getProperty(NodeConstants.Info.OUTPUT_COLS);
                    Map<Expression, String> corrected = null;
                    for (int i = 0; i < outputCols.size(); i++) {
                        Expression ex = expressions.get(i);
                        Expression expected = outputCols.get(i);
                        String name = Symbol.getShortName(expected);
                        if (!name.equals(Symbol.getShortName(ex))) {
                            expressions.set(i, new AliasSymbol(name, SymbolMap.getExpression(ex)));
                            corrected = new HashMap<Expression, String>();
                            corrected.put(ex, name);
                        }
                    }
                    if (corrected != null && newQuery.getOrderBy() != null) {
                        for (OrderByItem item : newQuery.getOrderBy().getOrderByItems()) {
                            String name = corrected.get(item.getSymbol());
                            if (name != null) {
                                item.setSymbol(new AliasSymbol(name, SymbolMap.getExpression(item.getSymbol())));
                            }
                        }
                    }
                    // so we'll unwrap that here
                    if (newQuery instanceof Query) {
                        q = (Query) newQuery;
                        if (q.getFrom() != null && q.getFrom().getClauses().size() == 1 && q.getFrom().getClauses().get(0) instanceof SubqueryFromClause) {
                            SubqueryFromClause nested = (SubqueryFromClause) q.getFrom().getClauses().get(0);
                            if (nested.getCommand() instanceof StoredProcedure) {
                                sfc.setCommand(nested.getCommand());
                            }
                        }
                    }
                    return;
                }
                // handle lateral join of a procedure
                Command command = (Command) node.getProperty(NodeConstants.Info.VIRTUAL_COMMAND);
                if (command instanceof StoredProcedure) {
                    StoredProcedure storedProcedure = (StoredProcedure) command;
                    storedProcedure.setPushedInQuery(true);
                    SubqueryFromClause subqueryFromClause = new SubqueryFromClause(symbol, storedProcedure);
                    // TODO: it would be better to directly add
                    query.getFrom().addClause(subqueryFromClause);
                    pushedTableProcedure = true;
                }
                PlanNode subPlan = (PlanNode) node.getProperty(Info.SUB_PLAN);
                if (subPlan != null) {
                    Map<GroupSymbol, PlanNode> subPlans = (Map<GroupSymbol, PlanNode>) accessRoot.getProperty(Info.SUB_PLANS);
                    if (subPlans == null) {
                        subPlans = new HashMap<GroupSymbol, PlanNode>();
                        accessRoot.setProperty(Info.SUB_PLANS, subPlans);
                    }
                    subPlans.put(symbol, subPlan);
                }
                if (!pushedTableProcedure) {
                    query.getFrom().addGroup(symbol);
                }
                break;
            }
    }
    for (PlanNode childNode : node.getChildren()) {
        buildQuery(accessRoot, childNode, query, context, capFinder);
    }
    switch(node.getType()) {
        case NodeConstants.Types.SELECT:
            {
                Criteria crit = (Criteria) node.getProperty(NodeConstants.Info.SELECT_CRITERIA);
                prepareSubqueries(node.getSubqueryContainers());
                if (!node.hasBooleanProperty(NodeConstants.Info.IS_HAVING)) {
                    query.setCriteria(CompoundCriteria.combineCriteria(query.getCriteria(), crit));
                } else {
                    query.setHaving(CompoundCriteria.combineCriteria(query.getHaving(), crit));
                }
                break;
            }
        case NodeConstants.Types.SORT:
            {
                prepareSubqueries(node.getSubqueryContainers());
                processOrderBy(node, query, modelID, context, capFinder);
                break;
            }
        case NodeConstants.Types.DUP_REMOVE:
            {
                boolean distinct = true;
                PlanNode grouping = NodeEditor.findNodePreOrder(node.getFirstChild(), NodeConstants.Types.GROUP, NodeConstants.Types.SOURCE);
                if (grouping != null) {
                    List groups = (List) grouping.getProperty(NodeConstants.Info.GROUP_COLS);
                    if (groups == null || groups.isEmpty()) {
                        distinct = false;
                    }
                }
                query.getSelect().setDistinct(distinct);
                break;
            }
        case NodeConstants.Types.GROUP:
            {
                List groups = (List) node.getProperty(NodeConstants.Info.GROUP_COLS);
                if (groups != null && !groups.isEmpty()) {
                    query.setGroupBy(new GroupBy(groups));
                    if (node.hasBooleanProperty(Info.ROLLUP)) {
                        query.getGroupBy().setRollup(true);
                    }
                }
                break;
            }
        case NodeConstants.Types.TUPLE_LIMIT:
            {
                processLimit(node, query, metadata);
                break;
            }
    }
}
Also used : HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) List(java.util.List) ArrayList(java.util.ArrayList) QueryMetadataInterface(org.teiid.query.metadata.QueryMetadataInterface) SymbolMap(org.teiid.query.sql.util.SymbolMap) ExpressionMappingVisitor(org.teiid.query.sql.visitor.ExpressionMappingVisitor) Evaluatable(org.teiid.query.sql.lang.SubqueryContainer.Evaluatable) Map(java.util.Map) HashMap(java.util.HashMap) SymbolMap(org.teiid.query.sql.util.SymbolMap)

Aggregations

ArrayList (java.util.ArrayList)2 PlanNode (org.teiid.query.optimizer.relational.plantree.PlanNode)2 Evaluatable (org.teiid.query.sql.lang.SubqueryContainer.Evaluatable)2 HashMap (java.util.HashMap)1 HashSet (java.util.HashSet)1 LinkedList (java.util.LinkedList)1 List (java.util.List)1 Map (java.util.Map)1 Set (java.util.Set)1 QueryMetadataInterface (org.teiid.query.metadata.QueryMetadataInterface)1 ElementSymbol (org.teiid.query.sql.symbol.ElementSymbol)1 Expression (org.teiid.query.sql.symbol.Expression)1 GroupSymbol (org.teiid.query.sql.symbol.GroupSymbol)1 SymbolMap (org.teiid.query.sql.util.SymbolMap)1 ExpressionMappingVisitor (org.teiid.query.sql.visitor.ExpressionMappingVisitor)1 SupportedJoinCriteria (org.teiid.translator.ExecutionFactory.SupportedJoinCriteria)1