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);
}
}
}
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;
}
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);
}
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);
}
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);
}
Aggregations