for (PlanNode sourceNode : NodeEditor.findAllNodes(plan, NodeConstants.Types.SOURCE, NodeConstants.Types.ACCESS)) {
SymbolMap references = (SymbolMap)sourceNode.getProperty(NodeConstants.Info.CORRELATED_REFERENCES);
if (references != null) {
Set<GroupSymbol> groups = GroupsUsedByElementsVisitor.getGroups(references.getValues());
PlanNode joinNode = NodeEditor.findParent(sourceNode, NodeConstants.Types.JOIN, NodeConstants.Types.SOURCE);
while (joinNode != null) {
joinNode.setProperty(NodeConstants.Info.JOIN_STRATEGY, JoinStrategyType.NESTED_TABLE);
if (joinNode.getProperty(NodeConstants.Info.DEPENDENT_VALUE_SOURCE) != null) {
//sanity check
throw new AssertionError("Cannot use a depenedent join when the join involves a correlated nested table."); //$NON-NLS-1$
}
if (joinNode.getGroups().containsAll(groups)) {
break;
}
joinNode = NodeEditor.findParent(joinNode, NodeConstants.Types.JOIN, NodeConstants.Types.SOURCE);
}
}
}
for (PlanNode joinNode : NodeEditor.findAllNodes(plan, NodeConstants.Types.JOIN, NodeConstants.Types.ACCESS)) {
JoinStrategyType stype = (JoinStrategyType) joinNode.getProperty(NodeConstants.Info.JOIN_STRATEGY);
if (!JoinStrategyType.MERGE.equals(stype)) {
continue;
}
/**
* Don't push sorts for unbalanced inner joins, we prefer to use a processing time cost based decision
*/
boolean pushLeft = true;
boolean pushRight = true;
if (joinNode.getProperty(NodeConstants.Info.JOIN_TYPE) == JoinType.JOIN_INNER && context != null) {
float leftCost = NewCalculateCostUtil.computeCostForTree(joinNode.getFirstChild(), metadata);
float rightCost = NewCalculateCostUtil.computeCostForTree(joinNode.getLastChild(), metadata);
if (leftCost != NewCalculateCostUtil.UNKNOWN_VALUE && rightCost != NewCalculateCostUtil.UNKNOWN_VALUE
&& (leftCost > context.getProcessorBatchSize() || rightCost > context.getProcessorBatchSize())) {
//we use a larger constant here to ensure that we don't unwisely prevent pushdown
pushLeft = leftCost < context.getProcessorBatchSize() || leftCost / rightCost < 8;
pushRight = rightCost < context.getProcessorBatchSize() || rightCost / leftCost < 8 || joinNode.getProperty(NodeConstants.Info.DEPENDENT_VALUE_SOURCE) != null;
}
}
List<SingleElementSymbol> leftExpressions = (List<SingleElementSymbol>) joinNode.getProperty(NodeConstants.Info.LEFT_EXPRESSIONS);
List<SingleElementSymbol> rightExpressions = (List<SingleElementSymbol>) joinNode.getProperty(NodeConstants.Info.RIGHT_EXPRESSIONS);
//check index information on each side
//TODO: don't do null order compensation - in fact we should check what the order actually is, but we don't have that metadata
Object key = null;
boolean right = true;
//we check the right first, since it should be larger
if (joinNode.getLastChild().getType() == NodeConstants.Types.ACCESS && NewCalculateCostUtil.isSingleTable(joinNode.getLastChild())) {
key = NewCalculateCostUtil.getKeyUsed(rightExpressions, null, metadata, null);
}
if (key == null && joinNode.getFirstChild().getType() == NodeConstants.Types.ACCESS && NewCalculateCostUtil.isSingleTable(joinNode.getFirstChild())) {
key = NewCalculateCostUtil.getKeyUsed(leftExpressions, null, metadata, null);
right = false;
}
if (key != null) {
//redo the join predicates based upon the key alone
List<Object> keyCols = metadata.getElementIDsInKey(key);
int[] reorder = new int[keyCols.size()];
List<Integer> toCriteria = new ArrayList<Integer>(rightExpressions.size() - keyCols.size());
List<SingleElementSymbol> keyExpressions = right?rightExpressions:leftExpressions;
for (int j = 0; j < keyExpressions.size(); j++) {
SingleElementSymbol ses = keyExpressions.get(j);
if (!(ses instanceof ElementSymbol)) {
continue;
}
ElementSymbol es = (ElementSymbol)ses;
boolean found = false;
for (int i = 0; !found && i < keyCols.size(); i++) {
if (es.getMetadataID().equals(keyCols.get(i))) {
reorder[i] = j;
found = true;
}
}
if (!found) {
toCriteria.add(j);
}
}
List<Criteria> joinCriteria = (List<Criteria>) joinNode.getProperty(Info.JOIN_CRITERIA);
for (int index : toCriteria) {
SingleElementSymbol lses = leftExpressions.get(index);
SingleElementSymbol rses = rightExpressions.get(index);
CompareCriteria cc = new CompareCriteria(lses, CompareCriteria.EQ, rses);
if (joinCriteria == null || joinCriteria.isEmpty()) {
joinCriteria = new ArrayList<Criteria>();
joinCriteria.add(cc);
joinNode.setProperty(Info.JOIN_TYPE, JoinType.JOIN_INNER);
}
}
joinNode.setProperty(Info.JOIN_CRITERIA, joinCriteria);
leftExpressions = RelationalNode.projectTuple(reorder, leftExpressions);
rightExpressions = RelationalNode.projectTuple(reorder, rightExpressions);
joinNode.setProperty(NodeConstants.Info.LEFT_EXPRESSIONS, leftExpressions);
joinNode.setProperty(NodeConstants.Info.RIGHT_EXPRESSIONS, rightExpressions);
}
boolean pushedLeft = insertSort(joinNode.getFirstChild(), leftExpressions, joinNode, metadata, capabilitiesFinder, pushLeft);
insertSort(joinNode.getLastChild(), rightExpressions, joinNode, metadata, capabilitiesFinder, pushRight);
if (joinNode.getProperty(NodeConstants.Info.JOIN_TYPE) == JoinType.JOIN_INNER && (!pushRight || !pushedLeft)) {
joinNode.setProperty(NodeConstants.Info.JOIN_STRATEGY, JoinStrategyType.ENHANCED_SORT);
}
}
return plan;
}