boolean cardinalityDependent = AggregateSymbol.areAggregatesCardinalityDependent(aggregates);
LinkedList<PlanNode> unionChildren = new LinkedList<PlanNode>();
findUnionChildren(unionChildren, cardinalityDependent, setOp);
SymbolMap parentMap = (SymbolMap)child.getProperty(NodeConstants.Info.SYMBOL_MAP);
//partitioned union
if (partitionInfo != null && !Collections.disjoint(partitionInfo.keySet(), groupingExpressions)) {
decomposeGroupBy(groupNode, child, groupingExpressions, aggregates, unionChildren, parentMap, context, 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 != null && !groupingExpressions.isEmpty()) {
setOp.setProperty(NodeConstants.Info.USE_ALL, Boolean.FALSE);
}
return;
}
//TODO: merge virtual, plan unions, raise null - change the partition information
if (unionChildren.size() < 2) {
return;
}
List<ElementSymbol> virtualElements = parentMap.getKeys();
List<SingleElementSymbol> copy = new ArrayList<SingleElementSymbol>(aggregates);
aggregates.clear();
Map<AggregateSymbol, Expression> aggMap = buildAggregateMap(copy, metadata, aggregates);
boolean shouldPushdown = false;
List<Boolean> pushdownList = new ArrayList<Boolean>(unionChildren.size());
for (PlanNode planNode : unionChildren) {
boolean pushdown = canPushGroupByToUnionChild(metadata, capFinder, groupingExpressions, aggregates, planNode);
pushdownList.add(pushdown);
shouldPushdown |= pushdown;
}
if (!shouldPushdown) {
return;
}
Iterator<Boolean> pushdownIterator = pushdownList.iterator();
for (PlanNode planNode : unionChildren) {
addView(plan, planNode, pushdownIterator.next(), groupingExpressions, aggregates, virtualElements, metadata, capFinder);
}
//update the parent plan with the staged aggregates and the new projected symbols
List<SingleElementSymbol> projectedViewSymbols = (List<SingleElementSymbol>)NodeEditor.findNodePreOrder(child, NodeConstants.Types.PROJECT).getProperty(NodeConstants.Info.PROJECT_COLS);
List<ElementSymbol> updatedVirturalElement = new ArrayList<ElementSymbol>(virtualElements);
//hack to introduce aggregate symbols to the parent view TODO: this should change the metadata properly.
GroupSymbol virtualGroup = child.getGroups().iterator().next();
for (int i = updatedVirturalElement.size(); i < projectedViewSymbols.size(); i++) {
SingleElementSymbol symbol = projectedViewSymbols.get(i);
String name = symbol.getShortName();
String virtualElementName = virtualGroup.getCanonicalName() + ElementSymbol.SEPARATOR + name;
ElementSymbol virtualElement = new ElementSymbol(virtualElementName);
virtualElement.setGroupSymbol(virtualGroup);
virtualElement.setType(symbol.getType());
virtualElement.setMetadataID(new TempMetadataID(virtualElementName, symbol.getType()));
updatedVirturalElement.add(virtualElement);
}
SymbolMap newParentMap = SymbolMap.createSymbolMap(updatedVirturalElement, projectedViewSymbols);
child.setProperty(NodeConstants.Info.SYMBOL_MAP, newParentMap);
Map<AggregateSymbol, ElementSymbol> projectedMap = new HashMap<AggregateSymbol, ElementSymbol>();
Iterator<AggregateSymbol> aggIter = aggregates.iterator();
for (ElementSymbol projectedViewSymbol : newParentMap.getKeys().subList(projectedViewSymbols.size() - aggregates.size(), projectedViewSymbols.size())) {
projectedMap.put(aggIter.next(), projectedViewSymbol);
}
for (Expression expr : aggMap.values()) {
ExpressionMappingVisitor.mapExpressions(expr, projectedMap);
}