Search in sources :

Example 86 with PlanNode

use of org.teiid.query.optimizer.relational.plantree.PlanNode 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)

Example 87 with PlanNode

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

the class RuleCollapseSource method addDistinct.

/**
 * This functions as "RulePushDistinct", however we do not bother
 * checking to see if a parent dup removal can actually be removed
 * - which can only happen if there are sources/selects/simple projects/limits/order by
 * between the access node and the parent dup removal.
 *
 * @param metadata
 * @param capFinder
 * @param accessNode
 * @param queryCommand
 * @param capabilitiesFinder
 * @throws QueryMetadataException
 * @throws TeiidComponentException
 */
private PlanNode addDistinct(QueryMetadataInterface metadata, CapabilitiesFinder capFinder, PlanNode accessNode, PlanNode root, QueryCommand queryCommand, CapabilitiesFinder capabilitiesFinder) throws QueryMetadataException, TeiidComponentException {
    if (RuleRemoveOptionalJoins.useNonDistinctRows(accessNode.getParent())) {
        return root;
    }
    if (queryCommand instanceof Query) {
        boolean allConstants = true;
        for (Expression ex : (List<Expression>) accessNode.getProperty(Info.OUTPUT_COLS)) {
            if (!(EvaluatableVisitor.willBecomeConstant(SymbolMap.getExpression(ex)))) {
                allConstants = false;
                break;
            }
        }
        if (allConstants) {
            // distinct of all constants means just a single row
            // see also the logic in RuleAssignOutputElements for a dupremove
            Object mid = RuleRaiseAccess.getModelIDFromAccess(accessNode, metadata);
            if (!CapabilitiesUtil.supports(Capability.ROW_LIMIT, mid, metadata, capabilitiesFinder)) {
                PlanNode limit = NodeFactory.getNewNode(NodeConstants.Types.TUPLE_LIMIT);
                limit.setProperty(Info.MAX_TUPLE_LIMIT, new Constant(1));
                limit.setProperty(NodeConstants.Info.OUTPUT_COLS, accessNode.getProperty(NodeConstants.Info.OUTPUT_COLS));
                if (accessNode.getParent() != null) {
                    accessNode.addAsParent(limit);
                    return root;
                }
                limit.addFirstChild(accessNode);
                return limit;
            }
            if (queryCommand.getLimit() != null) {
                if (queryCommand.getLimit().getRowLimit() == null) {
                    queryCommand.getLimit().setRowLimit(new Constant(1));
                }
            // else could have limit 0, so it takes more logic (case statement) to set this
            } else {
                queryCommand.setLimit(new Limit(null, new Constant(1)));
            }
            return root;
        }
    }
    if (queryCommand.getLimit() != null) {
        // TODO: could create an inline view
        return root;
    }
    boolean requireDupPush = false;
    if (queryCommand.getOrderBy() == null) {
        /* 
			 * we're assuming that a pushed order by implies that the cost of the distinct operation 
			 * will be marginal - which is not always true.
			 * 
			 * TODO: we should add costing for the benefit of pushing distinct by itself
			 * cardinality without = c
			 * assume cost ~ c lg c for c' cardinality and a modification for associated bandwidth savings
			 * recompute cost of processing plan with c' and see if new cost + c lg c < original cost
			 */
        PlanNode dupRemove = NodeEditor.findParent(accessNode, NodeConstants.Types.DUP_REMOVE, NodeConstants.Types.SOURCE);
        if (dupRemove != null) {
            // TODO: what about when sort/dup remove have been combined
            PlanNode project = NodeEditor.findParent(accessNode, NodeConstants.Types.PROJECT, NodeConstants.Types.DUP_REMOVE);
            if (project != null) {
                List<Expression> projectCols = (List<Expression>) project.getProperty(Info.PROJECT_COLS);
                for (Expression ex : projectCols) {
                    ex = SymbolMap.getExpression(ex);
                    if (!(ex instanceof ElementSymbol) && !(ex instanceof Constant) && !(EvaluatableVisitor.willBecomeConstant(ex, true))) {
                        return root;
                    }
                }
                /*
					 * If we can simply move the dupremove below the projection, then we'll do that as well
					 */
                requireDupPush = true;
            }
        }
        if (accessNode.hasBooleanProperty(Info.IS_MULTI_SOURCE)) {
            if (dupRemove == null) {
                return root;
            }
            if (requireDupPush) {
                // if multi-source we still need to process above
                requireDupPush = false;
            }
        } else if (!requireDupPush) {
            return root;
        }
    }
    // ensure that all columns are comparable - they might not be if there is an intermediate project
    for (Expression ses : queryCommand.getProjectedSymbols()) {
        if (DataTypeManager.isNonComparable(DataTypeManager.getDataTypeName(ses.getType()))) {
            return root;
        }
    }
    /* 
		 * TODO: if we are under a grouping/union not-all, then we should also fully order the results 
		 * and update the processing logic (this requires that we can guarantee null ordering) to assume sorted
		 */
    if (queryCommand instanceof SetQuery) {
        ((SetQuery) queryCommand).setAll(false);
    } else if (CapabilitiesUtil.supports(Capability.QUERY_SELECT_DISTINCT, RuleRaiseAccess.getModelIDFromAccess(accessNode, metadata), metadata, capFinder)) {
        Query query = (Query) queryCommand;
        HashSet<GroupSymbol> keyPreservingGroups = new HashSet<GroupSymbol>();
        ResolverUtil.findKeyPreserved(query, keyPreservingGroups, metadata);
        if (!QueryRewriter.isDistinctWithGroupBy(query) && !NewCalculateCostUtil.usesKey(query.getSelect().getProjectedSymbols(), keyPreservingGroups, metadata, true)) {
            if (requireDupPush) {
                // remove the upper dup remove
                PlanNode dupRemove = NodeEditor.findParent(accessNode, NodeConstants.Types.DUP_REMOVE, NodeConstants.Types.SOURCE);
                if (dupRemove.getParent() == null) {
                    root = dupRemove.getFirstChild();
                    dupRemove.getFirstChild().removeFromParent();
                } else {
                    dupRemove.getParent().replaceChild(dupRemove, dupRemove.getFirstChild());
                }
            }
            ((Query) queryCommand).getSelect().setDistinct(true);
        }
    }
    return root;
}
Also used : PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) List(java.util.List) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet)

Example 88 with PlanNode

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

the class RuleCollapseSource method processLimit.

private void processLimit(PlanNode node, QueryCommand query, QueryMetadataInterface metadata) {
    Expression limit = (Expression) node.getProperty(NodeConstants.Info.MAX_TUPLE_LIMIT);
    Expression offset = (Expression) node.getProperty(NodeConstants.Info.OFFSET_TUPLE_COUNT);
    PlanNode limitNode = NodeFactory.getNewNode(NodeConstants.Types.TUPLE_LIMIT);
    Expression childLimit = null;
    Expression childOffset = null;
    if (query.getLimit() != null) {
        childLimit = query.getLimit().getRowLimit();
        childOffset = query.getLimit().getOffset();
    }
    RulePushLimit.combineLimits(limitNode, metadata, limit, offset, childLimit, childOffset);
    Limit lim = new Limit((Expression) limitNode.getProperty(NodeConstants.Info.OFFSET_TUPLE_COUNT), (Expression) limitNode.getProperty(NodeConstants.Info.MAX_TUPLE_LIMIT));
    lim.setImplicit(node.hasBooleanProperty(Info.IS_IMPLICIT_LIMIT) && (query.getLimit() == null || query.getLimit().isImplicit()));
    query.setLimit(lim);
}
Also used : PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode)

Example 89 with PlanNode

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

the class RuleCopyCriteria method visitChildern.

private boolean visitChildern(PlanNode node, Set<Criteria>[] criteriaInfo, boolean changedTree, QueryMetadataInterface metadata, boolean underAccess) {
    if (node.getChildCount() > 0) {
        underAccess |= node.getType() == NodeConstants.Types.ACCESS;
        List<PlanNode> children = node.getChildren();
        for (int i = 0; i < children.size(); i++) {
            PlanNode childNode = children.get(i);
            changedTree |= tryToCopy(childNode, i == 0 ? criteriaInfo : new Set[2], metadata, underAccess);
        }
    }
    return changedTree;
}
Also used : PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode)

Example 90 with PlanNode

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

the class RuleDecomposeJoin method buildUnion.

private PlanNode buildUnion(PlanNode unionNode, PlanNode otherSide, List<Criteria> criteria, List<int[]> matches, List<PlanNode> branches, List<PlanNode> otherBranches, JoinType joinType) {
    SymbolMap symbolMap = (SymbolMap) unionNode.getParent().getProperty(Info.SYMBOL_MAP);
    SymbolMap otherSymbolMap = (SymbolMap) otherSide.getProperty(Info.SYMBOL_MAP);
    List<PlanNode> joins = new LinkedList<PlanNode>();
    for (int i = 0; i < matches.size(); i++) {
        int[] is = matches.get(i);
        PlanNode branch = branches.get(is[0]);
        PlanNode branchSource = createSource(unionNode.getParent().getGroups().iterator().next(), branch, symbolMap);
        PlanNode otherBranch = otherBranches.get(is[1]);
        PlanNode otherBranchSource = createSource(otherSide.getGroups().iterator().next(), otherBranch, otherSymbolMap);
        PlanNode newJoinNode = NodeFactory.getNewNode(NodeConstants.Types.JOIN);
        newJoinNode.addLastChild(branchSource);
        newJoinNode.addLastChild(otherBranchSource);
        newJoinNode.setProperty(Info.JOIN_STRATEGY, JoinStrategyType.NESTED_LOOP);
        newJoinNode.setProperty(Info.JOIN_TYPE, joinType);
        newJoinNode.setProperty(Info.JOIN_CRITERIA, LanguageObject.Util.deepClone(criteria, Criteria.class));
        newJoinNode.addGroups(branchSource.getGroups());
        newJoinNode.addGroups(otherBranchSource.getGroups());
        PlanNode projectPlanNode = NodeFactory.getNewNode(NodeConstants.Types.PROJECT);
        newJoinNode.addAsParent(projectPlanNode);
        Select allSymbols = new Select(symbolMap.getKeys());
        allSymbols.addSymbols(otherSymbolMap.getKeys());
        if (i == 0) {
            QueryRewriter.makeSelectUnique(allSymbols, false);
        }
        projectPlanNode.setProperty(NodeConstants.Info.PROJECT_COLS, allSymbols.getSymbols());
        projectPlanNode.addGroups(newJoinNode.getGroups());
        joins.add(projectPlanNode);
    }
    PlanNode newUnion = RulePlanUnions.buildUnionTree(unionNode, joins);
    return newUnion;
}
Also used : PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) Select(org.teiid.query.sql.lang.Select) SymbolMap(org.teiid.query.sql.util.SymbolMap) Criteria(org.teiid.query.sql.lang.Criteria) LinkedList(java.util.LinkedList)

Aggregations

PlanNode (org.teiid.query.optimizer.relational.plantree.PlanNode)204 Expression (org.teiid.query.sql.symbol.Expression)50 GroupSymbol (org.teiid.query.sql.symbol.GroupSymbol)50 ArrayList (java.util.ArrayList)47 List (java.util.List)43 SymbolMap (org.teiid.query.sql.util.SymbolMap)42 ElementSymbol (org.teiid.query.sql.symbol.ElementSymbol)36 Criteria (org.teiid.query.sql.lang.Criteria)35 LinkedList (java.util.LinkedList)24 CompareCriteria (org.teiid.query.sql.lang.CompareCriteria)24 Test (org.junit.Test)22 HashSet (java.util.HashSet)17 JoinType (org.teiid.query.sql.lang.JoinType)17 LinkedHashSet (java.util.LinkedHashSet)16 CompoundCriteria (org.teiid.query.sql.lang.CompoundCriteria)12 DependentSetCriteria (org.teiid.query.sql.lang.DependentSetCriteria)12 QueryPlannerException (org.teiid.api.exception.query.QueryPlannerException)11 LanguageObject (org.teiid.query.sql.LanguageObject)11 OrderBy (org.teiid.query.sql.lang.OrderBy)10 IsNullCriteria (org.teiid.query.sql.lang.IsNullCriteria)9