int thePosition = -1;
boolean extractAll = true;
// TODO: Have separate list for single span versus multi span
// For multi-span, we only need to keep a single range.
List<KeyRange> slotRanges = Lists.newArrayList();
KeyRange minMaxRange = KeyRange.EMPTY_RANGE;
for (KeySlots childSlot : childSlots) {
if (childSlot == DEGENERATE_KEY_PARTS) {
// TODO: can this ever happen and can we safely filter the expression tree?
continue;
}
if (childSlot.getMinMaxRange() != null) {
if (!slotRanges.isEmpty() && thePosition != initialPos) { // ORing together rvc in initial slot with other slots
return null;
}
minMaxRange = minMaxRange.union(childSlot.getMinMaxRange());
thePosition = initialPos;
for (KeySlot slot : childSlot) {
List<Expression> extractNodes = slot.getKeyPart().getExtractNodes();
extractAll &= !extractNodes.isEmpty();
slotExtractNodes.addAll(extractNodes);
}
} else {
// TODO: Do the same optimization that we do for IN if the childSlots specify a fully qualified row key
for (KeySlot slot : childSlot) {
// We have a nested OR with nothing for this slot, so continue
if (slot == null) {
continue; // FIXME: I don't think this is ever necessary
}
/*
* If we see a different PK column than before, we can't
* optimize it because our SkipScanFilter only handles
* top level expressions that are ANDed together (where in
* the same column expressions may be ORed together).
* For example, WHERE a=1 OR b=2 cannot be handled, while
* WHERE (a=1 OR a=2) AND (b=2 OR b=3) can be handled.
* TODO: We could potentially handle these cases through
* multiple, nested SkipScanFilters, where each OR expression
* is handled by its own SkipScanFilter and the outer one
* increments the child ones and picks the one with the smallest
* key.
*/
if (thePosition == -1) {
theSlot = slot;
thePosition = slot.getPKPosition();
} else if (thePosition != slot.getPKPosition()) {
return null;
}
List<Expression> extractNodes = slot.getKeyPart().getExtractNodes();
extractAll &= !extractNodes.isEmpty();
slotExtractNodes.addAll(extractNodes);
slotRanges.addAll(slot.getKeyRanges());
}
}
}
if (thePosition == -1) {
return null;
}
// With a mix of both, we can't use skip scan, so empty out the union
// and only extract the min/max nodes.
if (!slotRanges.isEmpty() && minMaxRange != KeyRange.EMPTY_RANGE) {
boolean clearExtracts = false;
// Union the minMaxRanges together with the slotRanges.
for (KeyRange range : slotRanges) {
if (!clearExtracts) {
/*
* Detect when to clear the extract nodes by determining if there
* are gaps left by combining the ranges. If there are gaps, we
* cannot extract the nodes, but must them as filters instead.
*/
KeyRange intersection = minMaxRange.intersect(range);
if (intersection == KeyRange.EMPTY_RANGE
|| !range.equals(intersection.union(range))
|| !minMaxRange.equals(intersection.union(minMaxRange))) {
clearExtracts = true;
}
}
minMaxRange = minMaxRange.union(range);
}