This is an automated email from the ASF dual-hosted git repository.
tdsilva pushed a commit to branch phoenix-stats
in repository https://gitbox.apache.org/repos/asf/phoenix.git
The following commit(s) were added to refs/heads/phoenix-stats by this push:
new ef6dcb5 Phoenix-Stats Initial commit and provide utility functions.
new f681949 Merge pull request #463 from dbwong/phoenix-stats
ef6dcb5 is described below
commit ef6dcb5ece73638ef007be0aac7b24f19f65e01a
Author: Daniel Wong <[email protected]>
AuthorDate: Tue Mar 12 19:32:38 2019 -0700
Phoenix-Stats Initial commit and provide utility functions.
---
.../org/apache/phoenix/compile/ScanRanges.java | 123 ++++++
.../phoenix/iterate/BaseResultIterators.java | 140 ++++---
.../phoenix/compile/ScanRangesIntersectTest.java | 438 ++++++++++++++++++++-
.../org/apache/phoenix/query/KeyRangeMoreTest.java | 14 +
4 files changed, 651 insertions(+), 64 deletions(-)
diff --git
a/phoenix-core/src/main/java/org/apache/phoenix/compile/ScanRanges.java
b/phoenix-core/src/main/java/org/apache/phoenix/compile/ScanRanges.java
index 2a7dbb4..c802678 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/ScanRanges.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/ScanRanges.java
@@ -694,4 +694,127 @@ public class ScanRanges {
return rowTimestampRange;
}
+ /**
+ * Produces the list of KeyRanges representing the fully qualified row key
by calling into ScanUtil.setKey
+ * repeatedly for every combination of KeyRanges in the ranges field. The
bounds will be set according to the
+ * properties of the setKey method.
+ *
+ * @return list of KeyRanges representing the fully qualified rowkey,
coalesced
+ */
+ public List<KeyRange> getRowKeyRanges() {
+ // If its scanRanges are everything or nothing, then we short circuit
and leave
+ // as schema is not filled in
+ if (isEverything()) { return
Lists.newArrayList(KeyRange.EVERYTHING_RANGE); }
+ if (isDegenerate()) { return Lists.newArrayList(KeyRange.EMPTY_RANGE);
}
+
+ List<KeyRange> queryRowKeyRanges =
Lists.newArrayListWithExpectedSize(this.ranges.size());
+
+
+ // If scanRanges.ranges has no information then should be in the
scanRanges.scanRange
+ if (ranges.size() == 0) {
+ queryRowKeyRanges.add(this.getScanRange());
+ } else { // We have a composite key need the row key from the
combination
+ // make a copy of ranges as we may add items to fully qualify our
rowkey
+ List<List<KeyRange>> newRanges = new ArrayList<>(this.getRanges());
+
+ int[] slotSpans = this.getSlotSpans();
+
+ // If the ranges here do not qualify all the keys then those keys
are unbound
+ if (newRanges.size() < schema.getMaxFields()) {
+ int originalSize = newRanges.size();
+ for (int i = 0; i < schema.getMaxFields() - originalSize; i++)
{
+
newRanges.add(Lists.newArrayList(KeyRange.EVERYTHING_RANGE));
+ }
+ slotSpans = new int[schema.getMaxFields()];
+ System.arraycopy(this.getSlotSpans(), 0, slotSpans, 0,
this.getSlotSpans().length);
+ }
+
+ // Product to bound our counting loop for safety
+ int rangesPermutationsCount = 1;
+ for (int i = 0; i < newRanges.size(); i++) {
+ rangesPermutationsCount *= newRanges.get(i).size();
+ }
+
+ // Have to construct the intersection
+ List<KeyRange> expandedRanges =
Lists.newArrayListWithCapacity(rangesPermutationsCount);
+ int[] positions = new int[slotSpans.length];
+
+ int maxKeyLength = SchemaUtil.getMaxKeyLength(schema, newRanges);
+ byte[] keyBuffer = new byte[maxKeyLength];
+
+ // Counting Loop
+ int count = 0;
+ while (count < rangesPermutationsCount) {
+ byte[] lowerBound;
+ byte[] upperBound;
+
+ // Note ScanUtil.setKey internally handles the upper/lower
exclusive from a Scan
+ // point of view. It would be good to break it out in the
future for rowkey
+ // construction vs ScanKey construction To handle differences
between
+ // hbase 2 and hbase 1 scan boundaries
+ int result = ScanUtil.setKey(schema, newRanges, slotSpans,
positions, KeyRange.Bound.LOWER, keyBuffer,
+ 0, 0, slotSpans.length);
+
+ if (result < 0) {
+ // unbound
+ lowerBound = KeyRange.UNBOUND;
+ } else {
+ lowerBound = Arrays.copyOf(keyBuffer, result);
+ }
+
+ result = ScanUtil.setKey(schema, newRanges, slotSpans,
positions, KeyRange.Bound.UPPER, keyBuffer, 0, 0,
+ slotSpans.length);
+
+ if (result < 0) {
+ // unbound
+ upperBound = KeyRange.UNBOUND;
+ } else {
+ upperBound = Arrays.copyOf(keyBuffer, result);
+ }
+
+
+ /*
+ * This is already considered inside of ScanUtil.setKey we may
want to refactor to pull these out.
+ * range/single boundary bound increment
+ * range inclusive lower no
+ * range inclusive upper yes, at the
end if occurs at any slots.
+ * range exclusive lower yes
+ * range exclusive upper no
+ * single inclusive lower no
+ * single inclusive upper yes, at the
end if it is the last slots.
+ */
+
+ boolean lowerInclusive = true;
+ // Don't send a null range, send an empty range.
+ if (lowerBound.length == 0 && upperBound.length == 0) {
+ lowerInclusive = false;
+ }
+
+ KeyRange keyRange = KeyRange.getKeyRange(lowerBound,
lowerInclusive, upperBound, false);
+ expandedRanges.add(keyRange);
+
+ // update position
+ // This loops through all settings of each of the primary keys
by counting from
+ // the trailing edge based on the number of settings of that
key.
+ int i;
+ for (i = positions.length - 1; i >= 0; i--) {
+ if (positions[i] < newRanges.get(i).size() - 1) {
+ positions[i]++;
+ break;
+ } else {
+ positions[i] = 0;
+ }
+ }
+
+ if (i < 0) {
+ break;
+ }
+ count++;
+ }
+ queryRowKeyRanges.addAll(expandedRanges);
+ }
+
+ return queryRowKeyRanges;
+ }
+
}
diff --git
a/phoenix-core/src/main/java/org/apache/phoenix/iterate/BaseResultIterators.java
b/phoenix-core/src/main/java/org/apache/phoenix/iterate/BaseResultIterators.java
index 34e1f89..07f5e09 100644
---
a/phoenix-core/src/main/java/org/apache/phoenix/iterate/BaseResultIterators.java
+++
b/phoenix-core/src/main/java/org/apache/phoenix/iterate/BaseResultIterators.java
@@ -138,7 +138,7 @@ import com.google.common.collect.Lists;
* Class that parallelizes the scan over a table using the ExecutorService
provided. Each region of the table will be scanned in parallel with
* the results accessible through {@link #getIterators()}
*
- *
+ *
* @since 0.1
*/
public abstract class BaseResultIterators extends ExplainTable implements
ResultIterators {
@@ -162,20 +162,13 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
private final boolean useStatsForParallelization;
protected Map<ImmutableBytesPtr,ServerCache> caches;
private final QueryPlan dataPlan;
-
- static final Function<HRegionLocation, KeyRange> TO_KEY_RANGE = new
Function<HRegionLocation, KeyRange>() {
- @Override
- public KeyRange apply(HRegionLocation region) {
- return KeyRange.getKeyRange(region.getRegionInfo().getStartKey(),
region.getRegionInfo().getEndKey());
- }
- };
private PTable getTable() {
return plan.getTableRef().getTable();
}
-
+
abstract protected boolean isSerial();
-
+
protected boolean useStats() {
/*
* Don't use guide posts:
@@ -188,7 +181,7 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
}
return !isSerial();
}
-
+
private static void initializeScan(QueryPlan plan, Integer perScanLimit,
Integer offset, Scan scan) throws SQLException {
StatementContext context = plan.getContext();
TableRef tableRef = plan.getTableRef();
@@ -232,7 +225,7 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
optimizeProjection = true;
if (projector.projectEveryRow()) {
if (table.getViewType() == ViewType.MAPPED) {
- // Since we don't have the empty key value in
MAPPED tables,
+ // Since we don't have the empty key value in
MAPPED tables,
// we must project all CFs in HRS. However, only
the
// selected column values are returned back to
client.
context.getWhereConditionColumns().clear();
@@ -258,7 +251,7 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
if (perScanLimit != null) {
ScanUtil.andFilterAtEnd(scan, new PageFilter(perScanLimit));
}
-
+
if(offset!=null){
ScanUtil.addOffsetAttribute(scan, offset);
}
@@ -268,7 +261,7 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
cols <
plan.getTableRef().getTable().getRowKeySchema().getFieldCount() &&
plan.getGroupBy().isOrderPreserving() &&
(context.getAggregationManager().isEmpty() ||
plan.getGroupBy().isUngroupedAggregate())) {
-
+
ScanUtil.andFilterAtEnd(scan,
new
DistinctPrefixFilter(plan.getTableRef().getTable().getRowKeySchema(),
cols));
@@ -290,7 +283,7 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
}
}
}
-
+
private static void setQualifierRanges(boolean keyOnlyFilter, PTable
table, Scan scan,
StatementContext context) throws SQLException {
if (EncodedColumnsUtil.useEncodedQualifierListOptimization(table,
scan)) {
@@ -328,7 +321,7 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
EncodedColumnsUtil.getEmptyKeyValueInfo(table);
qualifierSet.add(emptyKeyValueInfo.getFirst());
}
- // In case of a keyOnlyFilter, we only need to project the
+ // In case of a keyOnlyFilter, we only need to project the
// empty key value column
if (!keyOnlyFilter) {
Pair<Integer, Integer> qualifierRangeForFamily =
@@ -366,11 +359,11 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
}
}
}
-
+
private static void optimizeProjection(StatementContext context, Scan
scan, PTable table, FilterableStatement statement) {
Map<byte[], NavigableSet<byte[]>> familyMap = scan.getFamilyMap();
// columnsTracker contain cf -> qualifiers which should get returned.
- Map<ImmutableBytesPtr, NavigableSet<ImmutableBytesPtr>> columnsTracker
=
+ Map<ImmutableBytesPtr, NavigableSet<ImmutableBytesPtr>> columnsTracker
=
new TreeMap<ImmutableBytesPtr,
NavigableSet<ImmutableBytesPtr>>();
Set<byte[]> conditionOnlyCfs = new
TreeSet<byte[]>(Bytes.BYTES_COMPARATOR);
int referencedCfCount = familyMap.size();
@@ -452,7 +445,7 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
// cols is null means the whole CF will get scanned.
if (cols != null) {
if (whereCol.getSecond() == null) {
- scan.addFamily(family);
+ scan.addFamily(family);
} else {
scan.addColumn(family, whereCol.getSecond());
}
@@ -477,13 +470,13 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
// in the scan in this case. We still want the other optimization
that causes
// the ExplicitColumnTracker not to be used, though.
if (!statement.isAggregate() && filteredColumnNotInProjection) {
- ScanUtil.andFilterAtEnd(scan,
+ ScanUtil.andFilterAtEnd(scan,
trackedColumnsBitset != null ? new
EncodedQualifiersColumnProjectionFilter(SchemaUtil.getEmptyColumnFamily(table),
trackedColumnsBitset, conditionOnlyCfs, table.getEncodingScheme()) : new
ColumnProjectionFilter(SchemaUtil.getEmptyColumnFamily(table),
columnsTracker, conditionOnlyCfs,
EncodedColumnsUtil.usesEncodedColumnNames(table.getEncodingScheme())));
}
}
}
-
+
public BaseResultIterators(QueryPlan plan, Integer perScanLimit, Integer
offset, ParallelScanGrouper scanGrouper, Scan scan,
Map<ImmutableBytesPtr,ServerCache> caches, QueryPlan dataPlan) throws
SQLException {
super(plan.getContext(), plan.getTableRef(), plan.getGroupBy(),
plan.getOrderBy(),
plan.getStatement().getHint(),
QueryUtil.getOffsetLimit(plan.getLimit(), plan.getOffset()), offset);
@@ -505,7 +498,7 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
}
// Used to tie all the scans together during logging
scanId = new UUID(ThreadLocalRandom.current().nextLong(),
ThreadLocalRandom.current().nextLong()).toString();
-
+
initializeScan(plan, perScanLimit, offset, scan);
this.useStatsForParallelization =
PhoenixConfigurationUtil.getStatsForParallelizationProp(context.getConnection(),
table);
this.scans = getParallelScans();
@@ -550,7 +543,34 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
}
return ranges;
}
-
+
+ /**
+ * @return List of KeyRanges to represent each region
+ * @throws SQLException
+ */
+ private List<KeyRange> getRegionRowKeyRanges() throws SQLException {
+ List<HRegionLocation> regionLocations =
getRegionBoundaries(scanGrouper); // Load the region information
+
+ List<KeyRange> regionKeyRanges =
Lists.newArrayListWithCapacity(regionLocations.size());
+
+ // Map each HRegionLocation to a KeyRange - no Java 8
+ for (int i = 0; i < regionLocations.size(); i++) {
+ HRegionLocation regionLocation = regionLocations.get(i);
+ HRegionInfo regionInfo = regionLocation.getRegionInfo();
+
+ byte[] startKey = regionInfo.getStartKey();
+ byte[] endKey = regionInfo.getEndKey();
+
+ // Region is lowerInclusive true by definition
+ // Region is upperInclusive false by definition
+ // Unless it returns HConstants.EMPTY_BYTE_ARRAY which indicates
it is unbound
+ KeyRange range = KeyRange.getKeyRange(startKey, endKey);
+
+ regionKeyRanges.add(range);
+ }
+ return regionKeyRanges;
+ }
+
private static int getIndexContainingInclusive(List<byte[]> boundaries,
byte[] inclusiveKey) {
int guideIndex = Collections.binarySearch(boundaries, inclusiveKey,
Bytes.BYTES_COMPARATOR);
// If we found an exact match, return the index+1, as the inclusiveKey
will be contained
@@ -558,7 +578,7 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
guideIndex = (guideIndex < 0 ? -(guideIndex + 1) : (guideIndex + 1));
return guideIndex;
}
-
+
private static int getIndexContainingExclusive(List<byte[]> boundaries,
byte[] exclusiveKey) {
int guideIndex = Collections.binarySearch(boundaries, exclusiveKey,
Bytes.BYTES_COMPARATOR);
// If we found an exact match, return the index we found as the
exclusiveKey won't be
@@ -613,7 +633,7 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
Math.min(estimate.lastUpdated,
gps.getGuidePostTimestamps()[guideIndex]);
}
-
+
private List<Scan> addNewScan(List<List<Scan>> parallelScans, List<Scan>
scans, Scan scan,
byte[] startKey, boolean crossedRegionBoundary, HRegionLocation
regionLocation) {
boolean startNewScan = scanGrouper.shouldStartNewScan(plan, scans,
startKey, crossedRegionBoundary);
@@ -730,7 +750,7 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
// on PK column types (namely that you can only have a fixed width
nullable
// column as your last column), the type check is more of a sanity
check
// since it wouldn't make sense to have an index with every column
in common.
- if (indexColumn.getDataType() == dataColumn.getDataType()
+ if (indexColumn.getDataType() == dataColumn.getDataType()
&&
dataColumnName.equals(IndexUtil.getDataColumnName(indexColumnName))) {
nColumnsInCommon++;
continue;
@@ -739,13 +759,13 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
}
return nColumnsInCommon;
}
-
+
// public for testing
public static ScanRanges computePrefixScanRanges(ScanRanges
dataScanRanges, int nColumnsInCommon) {
if (nColumnsInCommon == 0) {
return ScanRanges.EVERYTHING;
}
-
+
int offset = 0;
List<List<KeyRange>> cnf =
Lists.newArrayListWithExpectedSize(nColumnsInCommon);
int[] slotSpan = new int[nColumnsInCommon];
@@ -784,7 +804,7 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
ScanRanges commonScanRanges =
ScanRanges.create(dataScanRanges.getSchema(), cnf, slotSpan, null, useSkipScan,
-1);
return commonScanRanges;
}
-
+
/**
* Truncates range to be a max of rangeSpan fields
* @param schema row key schema
@@ -823,7 +843,7 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
newRange = true;
}
}
-
+
return newRange ? KeyRange.getKeyRange(lowerRange, lowerInclusive,
upperRange, upperInclusive) : range;
}
@@ -864,7 +884,7 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
// return true if we've clipped the rowKey
return maxOffset != offset;
}
-
+
/**
* Compute the list of parallel scans to run for a given query. The inner
scans
* may be concatenated together directly, while the other ones may need to
be
@@ -899,7 +919,7 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
hasGuidePosts = gps != GuidePostsInfo.NO_GUIDEPOST;
// Case when stats collection did run but there possibly wasn't enough
data. In such a
// case we generate an empty guide post with the byte estimate being
set as guide post
- // width.
+ // width.
boolean emptyGuidePost = gps.isEmptyGuidePost();
byte[] startRegionBoundaryKey = startKey;
byte[] stopRegionBoundaryKey = stopKey;
@@ -930,7 +950,7 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
stopRegionBoundaryKey = stopKey = scanStopRow;
}
}
-
+
int regionIndex = 0;
int startRegionIndex = 0;
int stopIndex = regionBoundaries.size();
@@ -944,9 +964,9 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
}
}
List<List<Scan>> parallelScans =
Lists.newArrayListWithExpectedSize(stopIndex - regionIndex + 1);
-
+
ImmutableBytesWritable currentKey = new
ImmutableBytesWritable(startKey);
-
+
int gpsSize = gps.getGuidePostsCount();
int estGuidepostsPerRegion = gpsSize == 0 ? 1 : gpsSize /
regionLocations.size() + 1;
int keyOffset = 0;
@@ -1099,14 +1119,14 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
// may not have been entered if our scan end key is smaller
than the
// first guide post in that region).
boolean gpsAfterStopKey = false;
- gpsAvailableForAllRegions &=
+ gpsAvailableForAllRegions &=
( gpsInThisRegion && everNotDelayed) || // GP in this
region
( regionIndex == startRegionIndex && gpsForFirstRegion )
|| // GP in first region (before start key)
( gpsAfterStopKey = ( regionIndex == stopIndex &&
intersectWithGuidePosts && // GP in last region (after stop key)
( endRegionKey.length == 0 || // then check if gp
is in the region
- currentGuidePost.compareTo(endRegionKey) < 0) )
);
+ currentGuidePost.compareTo(endRegionKey) < 0) ) );
if (gpsAfterStopKey) {
- // If gp after stop key, but still in last region, track
min ts as fallback
+ // If gp after stop key, but still in last region, track
min ts as fallback
fallbackTs =
Math.min(fallbackTs,
gps.getGuidePostTimestamps()[guideIndex]);
@@ -1174,7 +1194,7 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
return pageLimit;
}
- private static Long computeMinTimestamp(boolean gpsAvailableForAllRegions,
+ private static Long computeMinTimestamp(boolean gpsAvailableForAllRegions,
GuidePostEstimate estimates,
long fallbackTs) {
if (gpsAvailableForAllRegions) {
@@ -1189,19 +1209,19 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
}
/**
- * Loop through List<List<Scan>> parallelScans object,
+ * Loop through List<List<Scan>> parallelScans object,
* rolling dice on each scan based on startRowKey.
- *
- * All FilterableStatement should have tableSamplingRate.
- * In case it is delete statement, an unsupported message is raised.
+ *
+ * All FilterableStatement should have tableSamplingRate.
+ * In case it is delete statement, an unsupported message is raised.
* In case it is null tableSamplingRate, 100% sampling rate will be
applied by default.
- *
+ *
* @param parallelScans
*/
private void sampleScans(final List<List<Scan>> parallelScans, final
Double tableSamplingRate){
if(tableSamplingRate==null||tableSamplingRate==100d) return;
final Predicate<byte[]>
tableSamplerPredicate=TableSamplerPredicate.of(tableSamplingRate);
-
+
for(Iterator<List<Scan>> is = parallelScans.iterator();is.hasNext();){
for(Iterator<Scan> i=is.next().iterator();i.hasNext();){
final Scan scan=i.next();
@@ -1209,14 +1229,14 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
}
}
}
-
+
public static <T> List<T> reverseIfNecessary(List<T> list, boolean
reverse) {
if (!reverse) {
return list;
}
return Lists.reverse(list);
}
-
+
/**
* Executes the scan in parallel across all regions, blocking until all
scans are complete.
* @return the result iterators for the scan of each region
@@ -1282,7 +1302,7 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
try {
long timeOutForScan = maxQueryEndTime -
EnvironmentEdgeManager.currentTimeMillis();
if (timeOutForScan < 0) {
- throw new
SQLExceptionInfo.Builder(SQLExceptionCode.OPERATION_TIMED_OUT).setMessage(".
Query couldn't be completed in the alloted time: " + queryTimeOut + "
ms").build().buildException();
+ throw new
SQLExceptionInfo.Builder(SQLExceptionCode.OPERATION_TIMED_OUT).setMessage(".
Query couldn't be completed in the alloted time: " + queryTimeOut + "
ms").build().buildException();
}
// make sure we apply the iterators in order
if (isLocalIndex && previousScan != null &&
previousScan.getScan() != null
@@ -1290,7 +1310,7 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
previousScan.getScan().getStopRow()) <
0)
|| (isReverse &&
previousScan.getScan().getStopRow().length > 0 &&
Bytes.compareTo(scanPair.getFirst().getAttribute(SCAN_ACTUAL_START_ROW),
previousScan.getScan().getStopRow()) >
0)
- ||
(Bytes.compareTo(scanPair.getFirst().getStopRow(),
previousScan.getScan().getStopRow()) == 0))
+ ||
(Bytes.compareTo(scanPair.getFirst().getStopRow(),
previousScan.getScan().getStopRow()) == 0))
&&
Bytes.compareTo(scanPair.getFirst().getAttribute(SCAN_START_ROW_SUFFIX),
previousScan.getScan().getAttribute(SCAN_START_ROW_SUFFIX))==0)) {
continue;
}
@@ -1335,7 +1355,7 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
iterators, isReverse,
maxQueryEndTime, previousScan,
clearedCache, concatIterators,
scanPairItr, scanPair, retryCount);
}
-
+
}
}
}
@@ -1410,11 +1430,11 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
maxQueryEndTime, newNestedScans.size(), previousScan,
retryCount);
return concatIterators;
}
-
+
@Override
public void close() throws SQLException {
-
+
// Don't call cancel on already started work, as it causes the
HConnection
// to get into a funk. Instead, just cancel queued work.
boolean cancelledWork = false;
@@ -1470,7 +1490,7 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
if (plan.useRoundRobinIterator()) {
/*
* When using a round robin iterator we shouldn't concatenate
the iterators together. This is because a
- * round robin iterator should be calling next() on these
iterators directly after selecting them in a
+ * round robin iterator should be calling next() on these
iterators directly after selecting them in a
* round robin fashion. This helps take advantage of loading
the underlying scanners' caches in parallel
* as well as preventing errors arising out of scanner lease
expirations.
*/
@@ -1487,7 +1507,7 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
private final Scan scan;
private final boolean isFirstScan;
private final boolean isLastScan;
-
+
public ScanLocator(Scan scan, int outerListIndex, int innerListIndex,
boolean isFirstScan, boolean isLastScan) {
this.outerListIndex = outerListIndex;
this.innerListIndex = innerListIndex;
@@ -1511,12 +1531,12 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
return isLastScan;
}
}
-
- abstract protected String getName();
+
+ abstract protected String getName();
abstract protected void submitWork(List<List<Scan>> nestedScans,
List<List<Pair<Scan,Future<PeekingResultIterator>>>> nestedFutures,
Queue<PeekingResultIterator> allIterators, int estFlattenedSize,
boolean isReverse, ParallelScanGrouper scanGrouper) throws SQLException;
-
+
@Override
public int size() {
return this.scans.size();
@@ -1540,7 +1560,7 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
}
}
buf.append(getName()).append(" ").append(size()).append("-WAY ");
-
+
if(this.plan.getStatement().getTableSamplingRate()!=null){
buf.append(plan.getStatement().getTableSamplingRate()/100D).append("-").append("SAMPLED
");
}
@@ -1557,11 +1577,11 @@ public abstract class BaseResultIterators extends
ExplainTable implements Result
public Long getEstimatedRowCount() {
return this.estimatedRows;
}
-
+
public Long getEstimatedByteCount() {
return this.estimatedSize;
}
-
+
@Override
public String toString() {
return "ResultIterators [name=" + getName() + ",id=" + scanId +
",scans=" + scans + "]";
diff --git
a/phoenix-core/src/test/java/org/apache/phoenix/compile/ScanRangesIntersectTest.java
b/phoenix-core/src/test/java/org/apache/phoenix/compile/ScanRangesIntersectTest.java
index 6b9af9d..accc28e 100644
---
a/phoenix-core/src/test/java/org/apache/phoenix/compile/ScanRangesIntersectTest.java
+++
b/phoenix-core/src/test/java/org/apache/phoenix/compile/ScanRangesIntersectTest.java
@@ -4,12 +4,12 @@
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
- * "License"); you maynot use this file except in compliance
+ * "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicablelaw or agreed to in writing, software
+ * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
@@ -17,16 +17,25 @@
*/
package org.apache.phoenix.compile;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.phoenix.filter.SkipScanFilter;
import org.apache.phoenix.query.KeyRange;
+import org.apache.phoenix.schema.PDatum;
+import org.apache.phoenix.schema.RowKeySchema;
+import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.schema.types.PChar;
+import org.apache.phoenix.schema.types.PDataType;
import org.apache.phoenix.schema.types.PVarchar;
import org.junit.Test;
@@ -58,12 +67,433 @@ public class ScanRangesIntersectTest {
assertEquals(expectedKeys, filter.getSlots().get(0));
}
}
-
+
+ private static byte[] stringToByteArray(String str){
+ return PVarchar.INSTANCE.toBytes(str);
+ }
+
+ private static String ByteArrayToString(byte[] bytes){
+ String literalString = PVarchar.INSTANCE.toStringLiteral(bytes,null);
+ return literalString.substring(1,literalString.length() - 1);
+ }
+
private static List<KeyRange> points(String... points) {
List<KeyRange> keys =
Lists.newArrayListWithExpectedSize(points.length);
for (String point : points) {
- keys.add(KeyRange.getKeyRange(PVarchar.INSTANCE.toBytes(point)));
+ keys.add(KeyRange.getKeyRange(stringToByteArray(point)));
}
return keys;
}
+
+ private static PDatum SIMPLE_CHAR = new PDatum() {
+ @Override
+ public boolean isNullable() {
+ return false;
+ }
+
+ @Override
+ public PDataType getDataType() {
+ return PChar.INSTANCE;
+ }
+
+ @Override
+ public Integer getMaxLength() {
+ return 1;
+ }
+
+ @Override
+ public Integer getScale() {
+ return null;
+ }
+
+ @Override
+ public SortOrder getSortOrder() {
+ return SortOrder.getDefault();
+ }
+ };
+
+ // Does not handle some edge conditions like empty string
+ private String handleScanNextKey(String key) {
+ char lastChar = key.charAt(key.length() - 1);
+ return key.substring(0, key.length() - 1) +
String.valueOf((char)(lastChar + 1));
+ }
+
+ @Test
+ public void getRowKeyRangesTestEverythingRange() {
+ int rowKeySchemaFields = 1;
+ RowKeySchema schema = buildSimpleRowKeySchema(rowKeySchemaFields);
+
+ List<List<KeyRange>> ranges = new ArrayList<>();
+ ranges.add(Lists.newArrayList(KeyRange.EVERYTHING_RANGE));
+ ScanRanges scanRanges = ScanRanges.createSingleSpan(schema, ranges);
+
+ List<KeyRange> rowKeyRanges = scanRanges.getRowKeyRanges();
+ assertEquals(1, rowKeyRanges.size());
+ assertEquals(KeyRange.EVERYTHING_RANGE,rowKeyRanges.get(0));
+ }
+
+ @Test
+ public void getRowKeyRangesTestEmptyRange() {
+ int rowKeySchemaFields = 1;
+ RowKeySchema schema = buildSimpleRowKeySchema(rowKeySchemaFields);
+
+ List<List<KeyRange>> ranges = new ArrayList<>();
+ ranges.add(Lists.newArrayList(KeyRange.EMPTY_RANGE));
+ ScanRanges scanRanges = ScanRanges.createSingleSpan(schema, ranges);
+
+ List<KeyRange> rowKeyRanges = scanRanges.getRowKeyRanges();
+ assertEquals(1, rowKeyRanges.size());
+ assertEquals(KeyRange.EMPTY_RANGE,rowKeyRanges.get(0));
+ }
+
+ @Test
+ public void getRowKeyRangesTestPointLookUp() {
+ String pointString = "A";
+ KeyRange pointKeyRange =
KeyRange.getKeyRange(stringToByteArray(pointString));
+ ScanRanges singlePointScanRange =
ScanRanges.createPointLookup(Lists.newArrayList(pointKeyRange));
+
+ List<KeyRange> rowKeyRanges = singlePointScanRange.getRowKeyRanges();
+ assertEquals(1, rowKeyRanges.size());
+ assertEquals(false, rowKeyRanges.get(0).isSingleKey());
+ assertEquals(singleKeyToScanRange(pointString),
rowKeyRanges.get(0).toString());
+ }
+
+ @Test
+ public void getRowKeyRangesTestPointsLookUp() {
+ String pointString1 = "A";
+ String pointString2 = "B";
+ KeyRange pointKeyRange1 =
KeyRange.getKeyRange(stringToByteArray(pointString1));
+ KeyRange pointKeyRange2 =
KeyRange.getKeyRange(stringToByteArray(pointString2));
+ ScanRanges singlePointScanRange = ScanRanges
+ .createPointLookup(Lists.newArrayList(pointKeyRange1,
pointKeyRange2));
+
+ List<KeyRange> rowKeyRanges = singlePointScanRange.getRowKeyRanges();
+ assertEquals(2, rowKeyRanges.size());
+ assertEquals(false, rowKeyRanges.get(0).isSingleKey());
+ assertEquals(singleKeyToScanRange(pointString1),
rowKeyRanges.get(0).toString());
+ assertEquals(false, rowKeyRanges.get(1).isSingleKey());
+ assertEquals(singleKeyToScanRange(pointString2),
rowKeyRanges.get(1).toString());
+ }
+
+ @Test
+ public void getRowKeyRangesTestOneRangeLookUp() {
+ RowKeySchema schema = buildSimpleRowKeySchema(1);
+
+ String lowerString = "A";
+ String upperString = "B";
+
+ KeyRange rangeKeyRange =
KeyRange.getKeyRange(stringToByteArray(lowerString),
stringToByteArray(upperString));
+ List<List<KeyRange>> ranges = new ArrayList<>();
+ ranges.add(Lists.newArrayList(rangeKeyRange));
+ ScanRanges scanRanges = ScanRanges.createSingleSpan(schema, ranges);
+
+ List<KeyRange> rowKeyRanges = scanRanges.getRowKeyRanges();
+ assertEquals(1, rowKeyRanges.size());
+ assertEquals(false, rowKeyRanges.get(0).isSingleKey());
+ assertEquals(lowerString,
ByteArrayToString(rowKeyRanges.get(0).getLowerRange()));
+ assertEquals(upperString,
ByteArrayToString(rowKeyRanges.get(0).getUpperRange()));
+ }
+
+ @Test
+ public void getRowKeyRangesTestOneExclusiveRangeLookUp() {
+ RowKeySchema schema = buildSimpleRowKeySchema(2);
+
+ String lowerString = "A";
+ String upperString = "C";
+
+ KeyRange rangeKeyRange =
KeyRange.getKeyRange(stringToByteArray(lowerString),false,
stringToByteArray(upperString),false);
+ List<List<KeyRange>> ranges = new ArrayList<>();
+ ranges.add(Lists.newArrayList(rangeKeyRange));
+ ScanRanges scanRanges = ScanRanges.createSingleSpan(schema, ranges);
+
+ List<KeyRange> rowKeyRanges = scanRanges.getRowKeyRanges();
+ assertEquals(1, rowKeyRanges.size());
+ assertEquals(false, rowKeyRanges.get(0).isSingleKey());
+ assertEquals("[B - C)", rowKeyRanges.get(0).toString());
+ }
+
+ @Test
+ public void getRowKeyRangesTestOneExclusiveRangeNotFullyQualifiedLookUp() {
+ RowKeySchema schema = buildSimpleRowKeySchema(2);
+
+ String lowerString = "A";
+ String upperString = "C";
+
+ KeyRange rangeKeyRange =
KeyRange.getKeyRange(stringToByteArray(lowerString),false,
stringToByteArray(upperString),false);
+ List<List<KeyRange>> ranges = new ArrayList<>();
+ ranges.add(Lists.newArrayList(rangeKeyRange));
+ ScanRanges scanRanges = ScanRanges.createSingleSpan(schema, ranges);
+
+ List<KeyRange> rowKeyRanges = scanRanges.getRowKeyRanges();
+ assertEquals(1, rowKeyRanges.size());
+ assertEquals(false, rowKeyRanges.get(0).isSingleKey());
+ assertEquals("[B - C)", rowKeyRanges.get(0).toString());
+ }
+
+ @Test
+ public void getRowKeyRangesTestTwoRangesLookUp() {
+ int rowKeySchemaFields = 2;
+ RowKeySchema schema = buildSimpleRowKeySchema(rowKeySchemaFields);
+ int[] slotSpan = new int[rowKeySchemaFields];
+
+ String lowerString = "A";
+ String upperString = "B";
+
+ String lowerString2 = "C";
+ String upperString2 = "D";
+
+ KeyRange rangeKeyRange1 =
KeyRange.getKeyRange(stringToByteArray(lowerString), true,
+ stringToByteArray(upperString), true);
+ KeyRange rangeKeyRange2 =
KeyRange.getKeyRange(stringToByteArray(lowerString2), true,
+ stringToByteArray(upperString2), true);
+
+ List<List<KeyRange>> ranges = new ArrayList<>();
+ ranges.add(Lists.newArrayList(rangeKeyRange1));
+ ranges.add(Lists.newArrayList(rangeKeyRange2));
+
+ ScanRanges scanRanges = ScanRanges.create(schema, ranges, slotSpan,
null, true, -1);
+
+ List<KeyRange> rowKeyRanges = scanRanges.getRowKeyRanges();
+ assertEquals(1, rowKeyRanges.size());
+ assertEquals(false, rowKeyRanges.get(0).isSingleKey());
+ assertEquals(lowerString + lowerString2,
ByteArrayToString(rowKeyRanges.get(0).getLowerRange()));
+ assertEquals(handleScanNextKey(upperString + upperString2),
+ ByteArrayToString(rowKeyRanges.get(0).getUpperRange()));
+ }
+
+ @Test
+ public void getRowKeyRangesTestNotFullyQualifiedRowKeyLookUp() {
+ int rowKeySchemaFields = 2;
+ RowKeySchema schema = buildSimpleRowKeySchema(rowKeySchemaFields);
+ int[] slotSpan = new int[rowKeySchemaFields];
+
+ String keyString1 = "A";
+ String keyString2 = "B";
+
+ KeyRange rangeKeyRange1 =
KeyRange.getKeyRange(stringToByteArray(keyString1));
+ KeyRange rangeKeyRange2 =
KeyRange.getKeyRange(stringToByteArray(keyString2));
+
+ List<List<KeyRange>> ranges = new ArrayList<>();
+ ranges.add(Lists.newArrayList(rangeKeyRange1, rangeKeyRange2));
+
+ ScanRanges scanRanges = ScanRanges.create(schema, ranges, slotSpan,
null, true, -1);
+
+ List<KeyRange> rowKeyRanges = scanRanges.getRowKeyRanges();
+ assertEquals(2, rowKeyRanges.size());
+ assertEquals(false, rowKeyRanges.get(0).isSingleKey());
+ assertEquals(keyString1,
ByteArrayToString(rowKeyRanges.get(0).getLowerRange()));
+ assertEquals(handleScanNextKey(keyString1),
ByteArrayToString(rowKeyRanges.get(0).getUpperRange()));
+ assertEquals(false, rowKeyRanges.get(1).isSingleKey());
+ assertEquals(keyString2,
ByteArrayToString(rowKeyRanges.get(1).getLowerRange()));
+ assertEquals(handleScanNextKey(keyString2),
ByteArrayToString(rowKeyRanges.get(1).getUpperRange()));
+ }
+
+ @Test
+ public void getRowKeyRangesTestValuesAndRangesLookUp() {
+ int rowKeySchemaFields = 2;
+ RowKeySchema schema = buildSimpleRowKeySchema(rowKeySchemaFields);
+ int[] slotSpan = new int[rowKeySchemaFields];
+
+ String point1 = "A";
+ String point2 = "B";
+
+ String lowerString2 = "C";
+ String upperString2 = "D";
+
+ KeyRange pointKeyRange1 =
KeyRange.getKeyRange(stringToByteArray(point1));
+ KeyRange pointKeyRange2 =
KeyRange.getKeyRange(stringToByteArray(point2));
+
+ KeyRange rangeKeyRange =
KeyRange.getKeyRange(stringToByteArray(lowerString2), true,
+ stringToByteArray(upperString2), true);
+
+ List<List<KeyRange>> ranges = new ArrayList<>();
+ ranges.add(Lists.newArrayList(pointKeyRange1, pointKeyRange2));
+ ranges.add(Lists.newArrayList(rangeKeyRange));
+
+ ScanRanges scanRanges = ScanRanges.create(schema, ranges, slotSpan,
null, true, -1);
+
+ List<KeyRange> rowKeyRanges = scanRanges.getRowKeyRanges();
+ assertEquals(2, rowKeyRanges.size());
+ assertEquals(false, rowKeyRanges.get(0).isSingleKey());
+ assertEquals(point1 + lowerString2,
ByteArrayToString(rowKeyRanges.get(0).getLowerRange()));
+ assertEquals(handleScanNextKey(point1 + upperString2),
ByteArrayToString(rowKeyRanges.get(0).getUpperRange()));
+
+ assertEquals(false, rowKeyRanges.get(1).isSingleKey());
+ assertEquals(point2 + lowerString2,
ByteArrayToString(rowKeyRanges.get(1).getLowerRange()));
+ assertEquals(handleScanNextKey(point2 + upperString2),
ByteArrayToString(rowKeyRanges.get(1).getUpperRange()));
+ }
+
+
+ @Test
+ public void getRowKeyRangesSorted() {
+ int rowKeySchemaFields = 2;
+ RowKeySchema schema = buildSimpleRowKeySchema(rowKeySchemaFields);
+ int[] slotSpan = new int[rowKeySchemaFields];
+
+ String k1a = "A";
+ String k1b = "B";
+ String k1c = "C";
+
+ String k2x = "X";
+ String k2y = "Y";
+ String k2z = "Z";
+
+ KeyRange pointKey1Range1 =
KeyRange.getKeyRange(stringToByteArray(k1a));
+ KeyRange pointKey1Range2 =
KeyRange.getKeyRange(stringToByteArray(k1b));
+ KeyRange pointKey1Range3 =
KeyRange.getKeyRange(stringToByteArray(k1c));
+ KeyRange pointKey2Range1 =
KeyRange.getKeyRange(stringToByteArray(k2x));
+ KeyRange pointKey2Range2 =
KeyRange.getKeyRange(stringToByteArray(k2y));
+ KeyRange pointKey2Range3 =
KeyRange.getKeyRange(stringToByteArray(k2z));
+
+ List<List<KeyRange>> ranges = new ArrayList<>();
+ ranges.add(Lists.newArrayList(pointKey1Range1, pointKey1Range2,
pointKey1Range3));
+
ranges.add(Lists.newArrayList(pointKey2Range1,pointKey2Range2,pointKey2Range3));
+
+ ScanRanges scanRanges = ScanRanges.create(schema, ranges, slotSpan,
null, true, -1);
+
+ List<KeyRange> rowKeyRanges = scanRanges.getRowKeyRanges();
+ assertEquals(9, rowKeyRanges.size());
+ assertEquals(singleKeyToScanRange("AX"),
rowKeyRanges.get(0).toString());
+ assertEquals(singleKeyToScanRange("AY"),
rowKeyRanges.get(1).toString());
+ assertEquals(singleKeyToScanRange("AZ"),
rowKeyRanges.get(2).toString());
+
+ assertEquals(singleKeyToScanRange("BX"),
rowKeyRanges.get(3).toString());
+ assertEquals(singleKeyToScanRange("BY"),
rowKeyRanges.get(4).toString());
+ assertEquals(singleKeyToScanRange("BZ"),
rowKeyRanges.get(5).toString());
+
+ assertEquals(singleKeyToScanRange("CX"),
rowKeyRanges.get(6).toString());
+ assertEquals(singleKeyToScanRange("CY"),
rowKeyRanges.get(7).toString());
+ assertEquals(singleKeyToScanRange("CZ"),
rowKeyRanges.get(8).toString());
+
+ }
+
+ @Test
+ public void getRowKeyRangesAdjacentSubRanges() {
+ int rowKeySchemaFields = 2;
+ RowKeySchema schema = buildSimpleRowKeySchema(rowKeySchemaFields);
+ int[] slotSpan = new int[rowKeySchemaFields];
+
+ List<KeyRange> keyRanges = new ArrayList<>();
+ keyRanges.add(KeyRange.getKeyRange(stringToByteArray("A"), true,
+ stringToByteArray("C"), false));
+ keyRanges.add(KeyRange.getKeyRange(stringToByteArray("C"), true,
+ stringToByteArray("E"), false));
+ keyRanges.add(KeyRange.getKeyRange(stringToByteArray("E"), true,
+ stringToByteArray("G"), false));
+ keyRanges.add(KeyRange.getKeyRange(stringToByteArray("G"), true,
+ stringToByteArray("I"), false));
+
+ List<List<KeyRange>> ranges = new ArrayList<>();
+ ranges.add(keyRanges);
+
+ ScanRanges scanRanges = ScanRanges.create(schema, ranges, slotSpan,
null, true, -1);
+
+ List<KeyRange> rowKeyRanges = scanRanges.getRowKeyRanges();
+ assertEquals(4, rowKeyRanges.size());
+ assertEquals("[A - C)", rowKeyRanges.get(0).toString());
+ assertEquals("[C - E)", rowKeyRanges.get(1).toString());
+ assertEquals("[E - G)", rowKeyRanges.get(2).toString());
+ assertEquals("[G - I)", rowKeyRanges.get(3).toString());
+ }
+
+ @Test
+ public void getRowKeyRangesAdjacentSubRangesUpperInclusive() {
+ int rowKeySchemaFields = 1;
+ RowKeySchema schema = buildSimpleRowKeySchema(rowKeySchemaFields);
+
+ int[] slotSpan = new int[rowKeySchemaFields];
+
+ List<KeyRange> keyRanges = new ArrayList<>();
+ keyRanges.add(KeyRange.getKeyRange(stringToByteArray("A"), false,
+ stringToByteArray("C"), true));
+ keyRanges.add(KeyRange.getKeyRange(stringToByteArray("C"), false,
+ stringToByteArray("E"), true));
+ keyRanges.add(KeyRange.getKeyRange(stringToByteArray("E"), false,
+ stringToByteArray("G"), true));
+ keyRanges.add(KeyRange.getKeyRange(stringToByteArray("G"), false,
+ stringToByteArray("I"), true));
+
+ List<List<KeyRange>> ranges = new ArrayList<>();
+ ranges.add(keyRanges);
+
+ ScanRanges scanRanges = ScanRanges.create(schema, ranges, slotSpan,
null, true, -1);
+
+ List<KeyRange> rowKeyRanges = scanRanges.getRowKeyRanges();
+ assertEquals(4, rowKeyRanges.size());
+ assertEquals("[B - D)", rowKeyRanges.get(0).toString());
+ assertEquals("[D - F)", rowKeyRanges.get(1).toString());
+ assertEquals("[F - H)", rowKeyRanges.get(2).toString());
+ assertEquals("[H - J)", rowKeyRanges.get(3).toString());
+ }
+
+ /*
+ * range/single boundary bound increment
+ * range inclusive lower no
+ * range inclusive upper yes, at the end if occurs
at any slots.
+ * range exclusive lower yes
+ * range exclusive upper no
+ * single inclusive lower no
+ * single inclusive upper yes, at the end if it is
the last slots.
+ */
+
+ @Test
+ public void getRangeKeyExclusiveLowerIncrementedUpperNotIncremented() {
+ int rowKeySchemaFields = 1;
+ RowKeySchema schema = buildSimpleRowKeySchema(rowKeySchemaFields);
+
+ int[] slotSpan = new int[rowKeySchemaFields];
+
+ String lowerKeyString = "E";
+ String upperKeyString = "O";
+ KeyRange rangeKeyRange =
KeyRange.getKeyRange(stringToByteArray(lowerKeyString),false,
stringToByteArray(upperKeyString), false);
+ List<List<KeyRange>> ranges = new ArrayList<>();
+ ranges.add(Lists.newArrayList(rangeKeyRange));
+
+ ScanRanges scanRanges = ScanRanges.create(schema, ranges, slotSpan,
null, true, -1);
+
+ List<KeyRange> rowKeyRanges = scanRanges.getRowKeyRanges();
+ assertEquals(1, rowKeyRanges.size());
+ assertTrue(rowKeyRanges.get(0).isLowerInclusive());
+ assertFalse(rowKeyRanges.get(0).isUpperInclusive());
+
assertArrayEquals(stringToByteArray(handleScanNextKey(lowerKeyString)),rowKeyRanges.get(0).getLowerRange());
+
assertArrayEquals(stringToByteArray(upperKeyString),rowKeyRanges.get(0).getUpperRange());
+ }
+
+ @Test
+ public void getAdjacentKeysLowerNotIncrementedUpperIncrementedLastSlots() {
+ int rowKeySchemaFields = 2;
+ RowKeySchema schema = buildSimpleRowKeySchema(rowKeySchemaFields);
+
+ int[] slotSpan = new int[rowKeySchemaFields];
+
+ KeyRange keyRange1_1 = KeyRange.getKeyRange(stringToByteArray("A"));
+ KeyRange keyRange2_1 = KeyRange.getKeyRange(stringToByteArray("B"));
+
+ KeyRange keyRange1_2 = KeyRange.getKeyRange(stringToByteArray("C"));
+ KeyRange keyRange2_2 = KeyRange.getKeyRange(stringToByteArray("D"));
+ List<List<KeyRange>> ranges = new ArrayList<>();
+ ranges.add(Lists.newArrayList(keyRange1_1,keyRange2_1));
+ ranges.add(Lists.newArrayList(keyRange1_2,keyRange2_2));
+
+ ScanRanges scanRanges = ScanRanges.create(schema, ranges, slotSpan,
null, true, -1);
+
+ List<KeyRange> rowKeyRanges = scanRanges.getRowKeyRanges();
+ assertEquals(4, rowKeyRanges.size());
+
assertEquals(singleKeyToScanRange("AC"),rowKeyRanges.get(0).toString());
+
assertEquals(singleKeyToScanRange("AD"),rowKeyRanges.get(1).toString());
+
assertEquals(singleKeyToScanRange("BC"),rowKeyRanges.get(2).toString());
+
assertEquals(singleKeyToScanRange("BD"),rowKeyRanges.get(3).toString());
+ }
+
+ private RowKeySchema buildSimpleRowKeySchema(int fields){
+ RowKeySchema.RowKeySchemaBuilder builder = new
RowKeySchema.RowKeySchemaBuilder(fields);
+ for(int i = 0; i < fields; i++) {
+ builder.addField(SIMPLE_CHAR, SIMPLE_CHAR.isNullable(),
SIMPLE_CHAR.getSortOrder());
+ }
+ return builder.build();
+ }
+
+ private String singleKeyToScanRange(String key){
+ return String.format("[%s - %s\\x00)",key,key);
+ }
}
diff --git
a/phoenix-core/src/test/java/org/apache/phoenix/query/KeyRangeMoreTest.java
b/phoenix-core/src/test/java/org/apache/phoenix/query/KeyRangeMoreTest.java
index 6f0c4c7..3763541 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/query/KeyRangeMoreTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/query/KeyRangeMoreTest.java
@@ -24,6 +24,7 @@ import java.util.Collections;
import java.util.List;
import com.google.common.collect.Lists;
+import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.phoenix.schema.types.PInteger;
import org.junit.Test;
@@ -171,6 +172,19 @@ public class KeyRangeMoreTest extends TestCase {
}
@Test
+ public void testHBaseConstants() {
+ byte[] key = {0,7};
+ KeyRange key1 = KeyRange.getKeyRange(HConstants.EMPTY_BYTE_ARRAY,key);
+ assertTrue(key1.lowerUnbound());
+
+ KeyRange key2 = KeyRange.getKeyRange(key,HConstants.EMPTY_BYTE_ARRAY);
+ assertTrue(key2.upperUnbound());
+
+ KeyRange key3 =
KeyRange.getKeyRange(HConstants.EMPTY_BYTE_ARRAY,HConstants.EMPTY_BYTE_ARRAY);
+ assertEquals(KeyRange.EVERYTHING_RANGE, key3);
+ }
+
+ @Test
public void testListIntersectForBoundary() throws Exception {
List<KeyRange> rowKeyRanges1=Arrays.asList(KeyRange.EVERYTHING_RANGE);
List<KeyRange> rowKeyRanges2=new ArrayList<KeyRange>();