* @return null if the raising should not continue, else the newRoot
*/
PlanNode raiseNullNode(PlanNode rootNode, List<PlanNode> nodes, PlanNode nullNode, QueryMetadataInterface metadata, CapabilitiesFinder capFinder)
throws QueryPlannerException, QueryMetadataException, TeiidComponentException {
PlanNode parentNode = nullNode.getParent();
switch(parentNode.getType()) {
case NodeConstants.Types.JOIN:
{
JoinType jt = (JoinType)parentNode.getProperty(NodeConstants.Info.JOIN_TYPE);
if (jt == JoinType.JOIN_CROSS || jt == JoinType.JOIN_INNER) {
return raiseNullNode(rootNode, parentNode, nullNode, nodes);
}
//for outer joins if the null node is on the outer side, then the join itself is null
//if the null node is on the inner side, then the join can be removed but the null values
//coming from the inner side will need to be placed into the frame
if (jt == JoinType.JOIN_LEFT_OUTER) {
if (nullNode == parentNode.getFirstChild()) {
return raiseNullNode(rootNode, parentNode, nullNode, nodes);
}
raiseNullThroughJoin(metadata, parentNode, parentNode.getLastChild());
return null;
}
if (jt == JoinType.JOIN_RIGHT_OUTER) {
if (nullNode == parentNode.getLastChild()) {
return raiseNullNode(rootNode, parentNode, nullNode, nodes);
}
raiseNullThroughJoin(metadata, parentNode, parentNode.getFirstChild());
return null;
}
if (jt == JoinType.JOIN_FULL_OUTER) {
if (nullNode == parentNode.getLastChild()) {
raiseNullThroughJoin(metadata, parentNode, parentNode.getLastChild());
} else {
raiseNullThroughJoin(metadata, parentNode, parentNode.getFirstChild());
}
return null;
}
break;
}
case NodeConstants.Types.SET_OP:
{
boolean isLeftChild = parentNode.getFirstChild() == nullNode;
SetQuery.Operation operation = (SetQuery.Operation)parentNode.getProperty(NodeConstants.Info.SET_OPERATION);
boolean raiseOverSetOp = (operation == SetQuery.Operation.INTERSECT || (operation == SetQuery.Operation.EXCEPT && isLeftChild));
if (raiseOverSetOp) {
return raiseNullNode(rootNode, parentNode, nullNode, nodes);
}
boolean isAll = parentNode.hasBooleanProperty(NodeConstants.Info.USE_ALL);
if (isLeftChild) {
PlanNode firstProject = NodeEditor.findNodePreOrder(parentNode, NodeConstants.Types.PROJECT);
if (firstProject == null) { // will only happen if the other branch has only null nodes
return raiseNullNode(rootNode, parentNode, nullNode, nodes);
}
List<SingleElementSymbol> newProjectSymbols = (List<SingleElementSymbol>)firstProject.getProperty(NodeConstants.Info.PROJECT_COLS);
List<SingleElementSymbol> oldProjectSymbols = (List<SingleElementSymbol>)nullNode.getProperty(NodeConstants.Info.PROJECT_COLS);
for (int i = 0; i < newProjectSymbols.size(); i++) {
SingleElementSymbol newSes = newProjectSymbols.get(i);
SingleElementSymbol oldSes = oldProjectSymbols.get(i);
if (newSes instanceof ExpressionSymbol || !newSes.getShortCanonicalName().equals(oldSes.getShortCanonicalName())) {
if (newSes instanceof AliasSymbol) {
newSes = ((AliasSymbol)newSes).getSymbol();
}
newProjectSymbols.set(i, new AliasSymbol(oldSes.getShortName(), newSes));
}
}
PlanNode sort = NodeEditor.findParent(firstProject, NodeConstants.Types.SORT, NodeConstants.Types.SOURCE);
if (sort != null) { //correct the sort to the new columns as well
OrderBy sortOrder = (OrderBy)sort.getProperty(NodeConstants.Info.SORT_ORDER);
for (OrderByItem item : sortOrder.getOrderByItems()) {
SingleElementSymbol sortElement = item.getSymbol();
sortElement = newProjectSymbols.get(oldProjectSymbols.indexOf(sortElement));
item.setSymbol(sortElement);
}
}
PlanNode sourceNode = NodeEditor.findParent(parentNode, NodeConstants.Types.SOURCE);
if (sourceNode != null && NodeEditor.findNodePreOrder(sourceNode, NodeConstants.Types.PROJECT) == firstProject) {
SymbolMap symbolMap = (SymbolMap)sourceNode.getProperty(NodeConstants.Info.SYMBOL_MAP);
symbolMap = SymbolMap.createSymbolMap(symbolMap.getKeys(), newProjectSymbols);
sourceNode.setProperty(NodeConstants.Info.SYMBOL_MAP, symbolMap);
}
}
NodeEditor.removeChildNode(parentNode, nullNode);
PlanNode grandParent = parentNode.getParent();
if (!isAll) { //ensure that the new child is distinct
PlanNode nestedSetOp = NodeEditor.findNodePreOrder(parentNode.getFirstChild(), NodeConstants.Types.SET_OP, NodeConstants.Types.SOURCE);
if (nestedSetOp != null) {
nestedSetOp.setProperty(NodeConstants.Info.USE_ALL, false);
} else if (NodeEditor.findNodePreOrder(parentNode.getFirstChild(), NodeConstants.Types.DUP_REMOVE, NodeConstants.Types.SOURCE) == null) {
parentNode.getFirstChild().addAsParent(NodeFactory.getNewNode(NodeConstants.Types.DUP_REMOVE));
}
}
if (grandParent == null) {
PlanNode newRoot = parentNode.getFirstChild();
parentNode.removeChild(newRoot);
return newRoot;
}
//remove the set op
NodeEditor.removeChildNode(grandParent, parentNode);
PlanNode sourceNode = NodeEditor.findParent(grandParent.getFirstChild(), NodeConstants.Types.SOURCE, NodeConstants.Types.SET_OP);
if (sourceNode != null) {
return RuleMergeVirtual.doMerge(sourceNode, rootNode, false, metadata);
}
return null;
}
case NodeConstants.Types.GROUP:
{
//if there are grouping columns, then we can raise
if (parentNode.hasCollectionProperty(NodeConstants.Info.GROUP_COLS)) {
return raiseNullNode(rootNode, parentNode, nullNode, nodes);
}
break; //- the else case could be implemented, but it's a lot of work for little gain, since the null node can't raise higher
}
case NodeConstants.Types.PROJECT:
{
// check for project into
PlanNode upperProject = NodeEditor.findParent(parentNode.getParent(), NodeConstants.Types.PROJECT);
if (upperProject == null
|| upperProject.getProperty(NodeConstants.Info.INTO_GROUP) == null) {
return raiseNullNode(rootNode, parentNode, nullNode, nodes);
}
break;
}
default: