Search in sources :

Example 41 with PlanNode

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

the class RulePushAggregates method updateParentAggs.

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

Example 42 with PlanNode

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

the class RulePushAggregates method createNodeMapping.

private <T extends Expression> Map<PlanNode, List<T>> createNodeMapping(PlanNode groupNode, Collection<T> expressions, boolean aggs) {
    Map<PlanNode, List<T>> result = new LinkedHashMap<PlanNode, List<T>>();
    if (expressions == null) {
        return result;
    }
    for (T aggregateSymbol : expressions) {
        boolean countStar = false;
        if (aggs) {
            AggregateSymbol as = (AggregateSymbol) aggregateSymbol;
            if ((!as.canStage() && as.isCardinalityDependent())) {
                return null;
            }
            countStar = isCountStar(as);
        }
        PlanNode originatingNode = null;
        Set<GroupSymbol> groups = null;
        if (countStar) {
            // TODO make a better choice as to the side
            PlanNode joinNode = NodeEditor.findAllNodes(groupNode, NodeConstants.Types.JOIN).get(0);
            float left = joinNode.getFirstChild().getCardinality();
            float right = joinNode.getLastChild().getCardinality();
            boolean useLeft = true;
            if (left != -1 && right != -1 && right > left) {
                useLeft = false;
            }
            groups = (useLeft ? joinNode.getFirstChild() : joinNode.getLastChild()).getGroups();
        } else {
            groups = GroupsUsedByElementsVisitor.getGroups(aggregateSymbol);
            if (groups.isEmpty()) {
                continue;
            }
        }
        originatingNode = FrameUtil.findOriginatingNode(groupNode.getFirstChild(), groups);
        if (originatingNode == null) {
            if (aggs) {
                // should never happen
                return null;
            }
            continue;
        }
        PlanNode parentAccess = NodeEditor.findParent(originatingNode, NodeConstants.Types.ACCESS, NodeConstants.Types.GROUP);
        if (parentAccess != null) {
            if (!NodeEditor.findAllNodes(parentAccess, NodeConstants.Types.GROUP, NodeConstants.Types.SOURCE).isEmpty()) {
                // already did a decomposition
                continue;
            }
            while (parentAccess.getType() == NodeConstants.Types.SELECT) {
                parentAccess = parentAccess.getParent();
            }
            originatingNode = parentAccess;
        }
        if (originatingNode.getParent() == groupNode || originatingNode.getType() != NodeConstants.Types.ACCESS) {
            // dependent upon the cardinality prevents us from optimizing.
            if (aggs && ((AggregateSymbol) aggregateSymbol).isCardinalityDependent()) {
                return null;
            }
            // don't perform intermediate grouping either
            continue;
        }
        if (aggs && ((AggregateSymbol) aggregateSymbol).isDistinct()) {
            // TODO: support distinct
            continue;
        }
        List<T> symbols = result.get(originatingNode);
        if (symbols == null) {
            symbols = new LinkedList<T>();
            result.put(originatingNode, symbols);
        }
        symbols.add(aggregateSymbol);
    }
    return result;
}
Also used : PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode)

Example 43 with PlanNode

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

the class RulePushAggregates method addEmptyFilter.

private void addEmptyFilter(Collection<AggregateSymbol> aggregates, PlanNode stageGroup, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, Object modelId) throws QueryMetadataException, TeiidComponentException {
    PlanNode selectNode = NodeFactory.getNewNode(NodeConstants.Types.SELECT);
    AggregateSymbol count = new AggregateSymbol(NonReserved.COUNT, false, null);
    // consider the count aggregate for the push down call below
    aggregates.add(count);
    Criteria crit = new CompareCriteria(count, CompareCriteria.GT, new Constant(new Integer(0)));
    selectNode.setProperty(NodeConstants.Info.SELECT_CRITERIA, crit);
    selectNode.setProperty(NodeConstants.Info.IS_HAVING, Boolean.TRUE);
    stageGroup.addAsParent(selectNode);
}
Also used : PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) IsNullCriteria(org.teiid.query.sql.lang.IsNullCriteria) Criteria(org.teiid.query.sql.lang.Criteria) CompareCriteria(org.teiid.query.sql.lang.CompareCriteria) CompareCriteria(org.teiid.query.sql.lang.CompareCriteria)

Example 44 with PlanNode

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

the class RulePushAggregates method pushGroupNodeOverUnion.

/**
 * The plan tree looks like:
 * group [agg(x), {a, b}]
 *   source
 *     set op
 *       child 1
 *       ...
 *
 * we need to make it into
 *
 * group [agg(agg(x)), {a, b}]
 *   source
 *     set op
 *       project
 *         [select]
 *           group [agg(x), {a, b}]
 *             source
 *               child 1
 *       ...
 *
 * Or if the child does not support pushdown we add dummy aggregate projection
 * count(*) = 1, count(x) = case x is null then 0 else 1 end, avg(x) = x, etc.
 */
private void pushGroupNodeOverUnion(QueryMetadataInterface metadata, CapabilitiesFinder capFinder, PlanNode groupNode, PlanNode unionSourceParent, List<Expression> groupingExpressions, PlanNode setOp, AnalysisRecord record) throws TeiidComponentException, QueryMetadataException, QueryPlannerException, QueryResolverException {
    if (setOp == null || setOp.getProperty(NodeConstants.Info.SET_OPERATION) != Operation.UNION) {
        return;
    }
    LinkedHashSet<AggregateSymbol> aggregates = collectAggregates(groupNode);
    Map<ElementSymbol, List<Set<Constant>>> partitionInfo = (Map<ElementSymbol, List<Set<Constant>>>) unionSourceParent.getProperty(Info.PARTITION_INFO);
    // check to see if any aggregate is dependent upon cardinality
    boolean cardinalityDependent = AggregateSymbol.areAggregatesCardinalityDependent(aggregates);
    LinkedList<PlanNode> unionChildren = new LinkedList<PlanNode>();
    findUnionChildren(unionChildren, cardinalityDependent, setOp);
    SymbolMap parentMap = (SymbolMap) unionSourceParent.getProperty(NodeConstants.Info.SYMBOL_MAP);
    // partitioned union
    if (partitionInfo != null && !Collections.disjoint(partitionInfo.keySet(), groupingExpressions)) {
        decomposeGroupBy(groupNode, unionSourceParent, groupingExpressions, aggregates, unionChildren, parentMap, metadata, capFinder);
        return;
    }
    /*
		 * if there are no aggregates, this is just duplicate removal
		 * mark the union as not all, which should be removed later but
		 * serves as a hint to distribute a distinct to the union queries
		 */
    if (aggregates.isEmpty()) {
        if (!groupingExpressions.isEmpty()) {
            Set<Expression> expressions = new HashSet<Expression>();
            boolean allCols = true;
            for (Expression ex : groupingExpressions) {
                if (!(ex instanceof ElementSymbol)) {
                    allCols = false;
                    break;
                }
                Expression mapped = parentMap.getMappedExpression((ElementSymbol) ex);
                expressions.add(mapped);
            }
            if (allCols) {
                PlanNode project = NodeEditor.findNodePreOrder(unionSourceParent, NodeConstants.Types.PROJECT);
                boolean projectsGrouping = true;
                for (Expression ex : (List<Expression>) project.getProperty(Info.PROJECT_COLS)) {
                    if (!expressions.contains(SymbolMap.getExpression(ex))) {
                        projectsGrouping = false;
                        break;
                    }
                }
                if (projectsGrouping) {
                    // since there are no expressions in the grouping cols, we know the grouping node is now not needed.
                    RuleAssignOutputElements.removeGroupBy(groupNode, metadata);
                    setOp.setProperty(NodeConstants.Info.USE_ALL, Boolean.FALSE);
                }
            }
        }
        return;
    }
    for (AggregateSymbol agg : aggregates) {
        if (!agg.canStage()) {
            return;
        }
    }
    if (unionChildren.size() < 2) {
        return;
    }
    List<AggregateSymbol> copy = new ArrayList<AggregateSymbol>(aggregates);
    aggregates.clear();
    Map<AggregateSymbol, Expression> aggMap = buildAggregateMap(copy, metadata, aggregates, false);
    boolean shouldPushdown = false;
    List<Boolean> pushdownList = new ArrayList<Boolean>(unionChildren.size());
    for (PlanNode planNode : unionChildren) {
        boolean pushdown = canPushGroupByToUnionChild(metadata, capFinder, groupingExpressions, aggregates, planNode, record, groupNode);
        pushdownList.add(pushdown);
        shouldPushdown |= pushdown;
    }
    if (!shouldPushdown) {
        return;
    }
    GroupSymbol group = unionSourceParent.getGroups().iterator().next().clone();
    Iterator<Boolean> pushdownIterator = pushdownList.iterator();
    boolean first = true;
    for (PlanNode planNode : unionChildren) {
        addUnionGroupBy(groupingExpressions, aggregates, parentMap, metadata, capFinder, group, first, planNode, !pushdownIterator.next(), false);
        first = false;
    }
    updateParentAggs(groupNode, aggMap, metadata);
    List<Expression> symbols = (List<Expression>) NodeEditor.findNodePreOrder(unionSourceParent, NodeConstants.Types.PROJECT).getProperty(Info.PROJECT_COLS);
    GroupSymbol modifiedGroup = group.clone();
    SymbolMap symbolMap = createSymbolMap(modifiedGroup, symbols, unionSourceParent, metadata);
    unionSourceParent.setProperty(Info.SYMBOL_MAP, symbolMap);
    // correct the parent frame
    Map<Expression, ElementSymbol> mapping = new HashMap<Expression, ElementSymbol>();
    Iterator<ElementSymbol> elemIter = symbolMap.getKeys().iterator();
    for (Expression expr : groupingExpressions) {
        mapping.put(expr, elemIter.next());
    }
    for (AggregateSymbol agg : aggregates) {
        mapping.put(agg, elemIter.next());
    }
    PlanNode node = unionSourceParent;
    while (node != groupNode.getParent()) {
        FrameUtil.convertNode(node, null, null, mapping, metadata, false);
        node = node.getParent();
    }
    removeUnnecessaryViews(unionSourceParent, metadata, capFinder);
}
Also used : PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) SymbolMap(org.teiid.query.sql.util.SymbolMap) SymbolMap(org.teiid.query.sql.util.SymbolMap)

Example 45 with PlanNode

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

the class RulePushAggregates method decomposeGroupBy.

/* if partitioned, then we don't need decomposition or the top level group by
     * 
	 *   source
	 *     set op
	 *       project
	 *           group [agg(x), {a, b}]
	 *             source
	 *               child 1
	 *       ...
     * 
     */
private void decomposeGroupBy(PlanNode groupNode, PlanNode sourceNode, List<Expression> groupingExpressions, LinkedHashSet<AggregateSymbol> aggregates, LinkedList<PlanNode> unionChildren, SymbolMap parentMap, QueryMetadataInterface metadata, CapabilitiesFinder capFinder) throws QueryPlannerException, QueryMetadataException, TeiidComponentException, QueryResolverException {
    // remove the group node
    groupNode.getParent().replaceChild(groupNode, groupNode.getFirstChild());
    GroupSymbol group = sourceNode.getGroups().iterator().next().clone();
    boolean first = true;
    for (PlanNode planNode : unionChildren) {
        addUnionGroupBy(groupingExpressions, aggregates, parentMap, metadata, capFinder, group, first, planNode, false, true);
        first = false;
    }
    List<Expression> symbols = (List<Expression>) NodeEditor.findNodePreOrder(sourceNode, NodeConstants.Types.PROJECT).getProperty(Info.PROJECT_COLS);
    GroupSymbol modifiedGroup = group.clone();
    SymbolMap symbolMap = createSymbolMap(modifiedGroup, symbols, sourceNode, metadata);
    sourceNode.setProperty(Info.SYMBOL_MAP, symbolMap);
    // map from the anon group to the updated inline view group
    SymbolMap map = (SymbolMap) groupNode.getProperty(Info.SYMBOL_MAP);
    Map<Expression, ElementSymbol> inverse = map.inserseMapping();
    SymbolMap newMapping = (SymbolMap) NodeEditor.findNodePreOrder(sourceNode, NodeConstants.Types.GROUP).getProperty(Info.SYMBOL_MAP);
    GroupSymbol oldGroup = null;
    Map<ElementSymbol, ElementSymbol> updatedMapping = new HashMap<ElementSymbol, ElementSymbol>();
    for (Map.Entry<ElementSymbol, Expression> entry : symbolMap.asMap().entrySet()) {
        Expression ex = newMapping.getMappedExpression((ElementSymbol) entry.getValue());
        ElementSymbol orig = inverse.get(ex);
        oldGroup = orig.getGroupSymbol();
        updatedMapping.put(orig, entry.getKey());
    }
    FrameUtil.convertFrame(sourceNode, oldGroup, Collections.singleton(modifiedGroup), updatedMapping, metadata);
    removeUnnecessaryViews(sourceNode, metadata, capFinder);
}
Also used : SymbolMap(org.teiid.query.sql.util.SymbolMap) PlanNode(org.teiid.query.optimizer.relational.plantree.PlanNode) SymbolMap(org.teiid.query.sql.util.SymbolMap)

Aggregations

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