You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by ja...@apache.org on 2014/09/13 02:21:35 UTC
git commit: PHOENIX-1211 Use skip scan when row value constructor
uses leading row key columns (Kyle Buzsaki)
Repository: phoenix
Updated Branches:
refs/heads/4.0 7cdc43770 -> 3ebcbd76c
PHOENIX-1211 Use skip scan when row value constructor uses leading row key columns (Kyle Buzsaki)
Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/3ebcbd76
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/3ebcbd76
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/3ebcbd76
Branch: refs/heads/4.0
Commit: 3ebcbd76cb5b83111f56372de1900c19a7c9e220
Parents: 7cdc437
Author: James Taylor <ja...@apache.org>
Authored: Fri Sep 12 17:21:14 2014 -0700
Committer: James Taylor <ja...@apache.org>
Committed: Fri Sep 12 17:21:14 2014 -0700
----------------------------------------------------------------------
.../org/apache/phoenix/end2end/InListIT.java | 24 +++++---
.../phoenix/end2end/RowValueConstructorIT.java | 53 +++++++++++++++++
.../org/apache/phoenix/compile/ScanRanges.java | 6 +-
.../apache/phoenix/compile/WhereCompiler.java | 4 +-
.../apache/phoenix/compile/WhereOptimizer.java | 6 --
.../apache/phoenix/filter/SkipScanFilter.java | 55 +++++++++++------
.../org/apache/phoenix/schema/RowKeySchema.java | 62 ++++++++++++++++++--
.../java/org/apache/phoenix/util/ScanUtil.java | 39 +++++++++---
.../phoenix/compile/WhereOptimizerTest.java | 4 +-
9 files changed, 202 insertions(+), 51 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/phoenix/blob/3ebcbd76/phoenix-core/src/it/java/org/apache/phoenix/end2end/InListIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/InListIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/InListIT.java
index 524d494..dc60b69 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/InListIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/InListIT.java
@@ -164,6 +164,11 @@ public class InListIT extends BaseHBaseManagedTimeIT {
private static final List<Boolean> TENANCIES = Arrays.asList(false, true);
private static final List<PDataType> INTEGER_TYPES = Arrays.asList(PDataType.INTEGER, PDataType.LONG);
private static final List<Integer> SALT_BUCKET_NUMBERS = Arrays.asList(0, 4);
+
+ // we should be including the RANGE_SCAN hint here, but a bug with ParallelIterators causes tests to fail
+ // see the relevant JIRA here: https://issues.apache.org/jira/browse/PHOENIX-1251
+ private static final List<String> HINTS = Arrays.asList("", "/*+ SKIP_SCAN */");
+// private static final List<String> HINTS = Arrays.asList("", "/*+ SKIP_SCAN */", "/*+ RANGE_SCAN */");
/**
* Tests the given where clause against the given upserts by comparing against the list of
@@ -193,14 +198,19 @@ public class InListIT extends BaseHBaseManagedTimeIT {
}
conn.commit();
- // perform the query
- String sql = "SELECT nonPk FROM " + tableName + " " + whereClause;
- ResultSet rs = conn.createStatement().executeQuery(sql);
- for(String expected : expecteds) {
- assertTrue(rs.next());
- assertEquals(expected, rs.getString(1));
+ for(String hint : HINTS) {
+ String context = "where: " + whereClause + ", type: " + pkType + ", salt buckets: "
+ + saltBuckets + ", multitenant: " + isMultiTenant + ", hint: " + hint + "";
+
+ // perform the query
+ String sql = "SELECT " + hint + " nonPk FROM " + tableName + " " + whereClause;
+ ResultSet rs = conn.createStatement().executeQuery(sql);
+ for (String expected : expecteds) {
+ assertTrue("did not include result '" + expected + "' (" + context + ")", rs.next());
+ assertEquals(context, expected, rs.getString(1));
+ }
+ assertFalse(context, rs.next());
}
- assertFalse(rs.next());
}
}
}
http://git-wip-us.apache.org/repos/asf/phoenix/blob/3ebcbd76/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java
index 041725c..bf3d9db 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java
@@ -53,6 +53,7 @@ import java.util.Properties;
import org.apache.phoenix.util.DateUtil;
import org.apache.phoenix.util.PhoenixRuntime;
import org.apache.phoenix.util.PropertiesUtil;
+import org.apache.phoenix.util.QueryUtil;
import org.apache.phoenix.util.TestUtil;
import org.junit.Test;
import org.junit.experimental.categories.Category;
@@ -1228,4 +1229,56 @@ public class RowValueConstructorIT extends BaseClientManagedTimeIT {
assertEquals(4, rs.getInt(2));
assertFalse(rs.next());
}
+
+ @Test
+ public void testForceSkipScan() throws Exception {
+ String tempTableWithCompositePK = "TEMP_TABLE_COMPOSITE_PK";
+ Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+ Connection conn = DriverManager.getConnection(getUrl(), props);
+ try {
+ conn.createStatement().execute("CREATE TABLE " + tempTableWithCompositePK
+ + " (col0 INTEGER NOT NULL, "
+ + " col1 INTEGER NOT NULL, "
+ + " col2 INTEGER NOT NULL, "
+ + " col3 INTEGER "
+ + " CONSTRAINT pk PRIMARY KEY (col0, col1, col2)) "
+ + " SALT_BUCKETS=4");
+
+ PreparedStatement upsertStmt = conn.prepareStatement(
+ "upsert into " + tempTableWithCompositePK + "(col0, col1, col2, col3) " + "values (?, ?, ?, ?)");
+ for (int i = 0; i < 3; i++) {
+ upsertStmt.setInt(1, i + 1);
+ upsertStmt.setInt(2, i + 2);
+ upsertStmt.setInt(3, i + 3);
+ upsertStmt.setInt(4, i + 5);
+ upsertStmt.execute();
+ }
+ conn.commit();
+
+ String query = "SELECT * FROM " + tempTableWithCompositePK + " WHERE (col0, col1) in ((2, 3), (3, 4), (4, 5))";
+ PreparedStatement statement = conn.prepareStatement(query);
+ ResultSet rs = statement.executeQuery();
+ assertTrue(rs.next());
+ assertEquals(rs.getInt(1), 2);
+ assertEquals(rs.getInt(2), 3);
+ assertEquals(rs.getInt(3), 4);
+ assertEquals(rs.getInt(4), 6);
+ assertTrue(rs.next());
+ assertEquals(rs.getInt(1), 3);
+ assertEquals(rs.getInt(2), 4);
+ assertEquals(rs.getInt(3), 5);
+ assertEquals(rs.getInt(4), 7);
+
+ assertFalse(rs.next());
+
+ String plan = "CLIENT PARALLEL 4-WAY SKIP SCAN ON 12 KEYS OVER TEMP_TABLE_COMPOSITE_PK [0,2] - [3,4]\n" +
+ "CLIENT MERGE SORT";
+ String explainQuery = "EXPLAIN " + query;
+ rs = conn.createStatement().executeQuery(explainQuery);
+ assertEquals(query, plan, QueryUtil.getExplainPlan(rs));
+ } finally {
+ conn.close();
+ }
+ }
+
}
http://git-wip-us.apache.org/repos/asf/phoenix/blob/3ebcbd76/phoenix-core/src/main/java/org/apache/phoenix/compile/ScanRanges.java
----------------------------------------------------------------------
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 1052601..dc8e0b3 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
@@ -100,7 +100,7 @@ public class ScanRanges {
this.slotSpan = slotSpan;
this.schema = schema;
if (schema != null && !ranges.isEmpty()) {
- this.filter = new SkipScanFilter(this.ranges, schema);
+ this.filter = new SkipScanFilter(this.ranges, slotSpan, schema);
}
this.forceRangeScan = forceRangeScan;
}
@@ -152,7 +152,7 @@ public class ScanRanges {
}
private static boolean isPointLookup(RowKeySchema schema, List<List<KeyRange>> ranges, int[] slotSpan) {
- if (ScanUtil.calculateSlotSpan(ranges, slotSpan) < schema.getMaxFields()) {
+ if (ScanUtil.getTotalSpan(ranges, slotSpan) < schema.getMaxFields()) {
return false;
}
for (List<KeyRange> orRanges : ranges) {
@@ -261,7 +261,7 @@ public class ScanRanges {
}
public int getPkColumnSpan() {
- return this == ScanRanges.NOTHING ? 0 : ScanUtil.calculateSlotSpan(ranges, slotSpan);
+ return this == ScanRanges.NOTHING ? 0 : ScanUtil.getTotalSpan(ranges, slotSpan);
}
@Override
http://git-wip-us.apache.org/repos/asf/phoenix/blob/3ebcbd76/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereCompiler.java
index 7bcb6d0..2e72f43 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereCompiler.java
@@ -255,9 +255,7 @@ public class WhereCompiler {
}
ScanRanges scanRanges = context.getScanRanges();
- boolean forcedSkipScan = statement.getHint().hasHint(Hint.SKIP_SCAN);
- boolean forcedRangeScan = statement.getHint().hasHint(Hint.RANGE_SCAN);
- if (forcedSkipScan || (scanRanges.useSkipScanFilter() && !forcedRangeScan)) {
+ if (scanRanges.useSkipScanFilter()) {
ScanUtil.andFilterAtBeginning(scan, scanRanges.getSkipScanFilter());
}
}
http://git-wip-us.apache.org/repos/asf/phoenix/blob/3ebcbd76/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereOptimizer.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereOptimizer.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereOptimizer.java
index 5e03158..ab92a14 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereOptimizer.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereOptimizer.java
@@ -284,12 +284,6 @@ public class WhereOptimizer {
// If we have fully qualified point keys with multi-column spans (i.e. RVC),
// we can still use our skip scan. The ScanRanges.create() call will explode
// out the keys.
- if (hasMultiColumnSpan) {
- forcedRangeScan |= pkPos < nPKColumns;
- if (forcedRangeScan && removeFromExtractNodes != null) {
- extractNodes.removeAll(removeFromExtractNodes);
- }
- }
context.setScanRanges(
ScanRanges.create(schema, cnf, Arrays.copyOf(slotSpan, cnf.size()), forcedRangeScan, nBuckets),
minMaxRange);
http://git-wip-us.apache.org/repos/asf/phoenix/blob/3ebcbd76/phoenix-core/src/main/java/org/apache/phoenix/filter/SkipScanFilter.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/filter/SkipScanFilter.java b/phoenix-core/src/main/java/org/apache/phoenix/filter/SkipScanFilter.java
index b9b091d..ccdbe4c 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/filter/SkipScanFilter.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/filter/SkipScanFilter.java
@@ -87,21 +87,25 @@ public class SkipScanFilter extends FilterBase implements Writable {
}
public SkipScanFilter(List<List<KeyRange>> slots, RowKeySchema schema) {
- init(slots, schema);
+ this(slots, ScanUtil.getDefaultSlotSpans(slots.size()), schema);
+ }
+
+ public SkipScanFilter(List<List<KeyRange>> slots, int[] slotSpan, RowKeySchema schema) {
+ init(slots, slotSpan, schema);
}
public void setOffset(int offset) {
this.offset = offset;
}
- private void init(List<List<KeyRange>> slots, RowKeySchema schema) {
+ private void init(List<List<KeyRange>> slots, int[] slotSpan, RowKeySchema schema) {
for (List<KeyRange> ranges : slots) {
if (ranges.isEmpty()) {
throw new IllegalStateException();
}
}
this.slots = slots;
- this.slotSpan = ScanUtil.getDefaultSlotSpans(slots.size());
+ this.slotSpan = slotSpan;
this.schema = schema;
this.maxKeyLength = SchemaUtil.getMaxKeyLength(schema, slots);
this.position = new int[slots.size()];
@@ -130,6 +134,8 @@ public class SkipScanFilter extends FilterBase implements Writable {
}
private void setNextCellHint(Cell kv) {
+ Cell previousCellHint = nextCellHint;
+
if (offset == 0) {
nextCellHint = new KeyValue(startKey, 0, startKeyLength,
null, 0, 0, null, 0, 0, HConstants.LATEST_TIMESTAMP, Type.Maximum, null, 0, 0);
@@ -140,6 +146,10 @@ public class SkipScanFilter extends FilterBase implements Writable {
nextCellHint = new KeyValue(nextKey, 0, nextKey.length,
null, 0, 0, null, 0, 0, HConstants.LATEST_TIMESTAMP, Type.Maximum, null, 0, 0);
}
+
+ // we should either have no previous hint, or the next hint should always come after the previous hint
+ assert previousCellHint == null || KeyValue.COMPARATOR.compare(nextCellHint, previousCellHint) > 0
+ : "next hint must come after previous hint (prev=" + previousCellHint + ", next=" + nextCellHint + ", kv=" + kv + ")";
}
@Override
@@ -158,7 +168,7 @@ public class SkipScanFilter extends FilterBase implements Writable {
public SkipScanFilter intersect(byte[] lowerInclusiveKey, byte[] upperExclusiveKey) {
List<List<KeyRange>> newSlots = Lists.newArrayListWithCapacity(slots.size());
if (intersect(lowerInclusiveKey, upperExclusiveKey, newSlots)) {
- return new SkipScanFilter(newSlots, schema);
+ return new SkipScanFilter(newSlots, slotSpan, schema);
}
return null;
}
@@ -185,7 +195,7 @@ public class SkipScanFilter extends FilterBase implements Writable {
int lastSlot = slots.size()-1;
if (!lowerUnbound) {
// Find the position of the first slot of the lower range
- schema.next(ptr, 0, schema.iterator(lowerInclusiveKey,ptr));
+ schema.next(ptr, 0, schema.iterator(lowerInclusiveKey,ptr), slotSpan[0]);
startPos = ScanUtil.searchClosestKeyRangeWithUpperHigherThanPtr(slots.get(0), ptr, 0);
// Lower range is past last upper range of first slot, so cannot possibly be in range
if (startPos >= slots.get(0).size()) {
@@ -196,7 +206,7 @@ public class SkipScanFilter extends FilterBase implements Writable {
int endPos = slots.get(0).size()-1;
if (!upperUnbound) {
// Find the position of the first slot of the upper range
- schema.next(ptr, 0, schema.iterator(upperExclusiveKey,ptr));
+ schema.next(ptr, 0, schema.iterator(upperExclusiveKey,ptr), slotSpan[0]);
endPos = ScanUtil.searchClosestKeyRangeWithUpperHigherThanPtr(slots.get(0), ptr, startPos);
// Upper range lower than first lower range of first slot, so cannot possibly be in range
if (endPos == 0 && Bytes.compareTo(upperExclusiveKey, slots.get(0).get(0).getLowerRange()) <= 0) {
@@ -321,7 +331,7 @@ public class SkipScanFilter extends FilterBase implements Writable {
int earliestRangeIndex = nSlots-1;
int minOffset = offset;
int maxOffset = schema.iterator(currentKey, minOffset, length, ptr);
- schema.next(ptr, i, maxOffset);
+ schema.next(ptr, ScanUtil.getRowKeyPosition(slotSpan, i), maxOffset, slotSpan[i]);
while (true) {
// Increment to the next range while the upper bound of our current slot is less than our current key
while (position[i] < slots.get(i).size() && slots.get(i).get(position[i]).compareUpperToLowerBound(ptr) < 0) {
@@ -360,7 +370,7 @@ public class SkipScanFilter extends FilterBase implements Writable {
// the current key, so we'll end up incrementing the start key until it's bigger than the
// current key.
setStartKey();
- schema.reposition(ptr, i, j, minOffset, maxOffset);
+ schema.reposition(ptr, ScanUtil.getRowKeyPosition(slotSpan, i), ScanUtil.getRowKeyPosition(slotSpan, j), minOffset, maxOffset, slotSpan[j]);
} else {
int currentLength = setStartKey(ptr, minOffset, j+1);
// From here on, we use startKey as our buffer (resetting minOffset and maxOffset)
@@ -368,7 +378,7 @@ public class SkipScanFilter extends FilterBase implements Writable {
// Reinitialize the iterator to be positioned at previous slot position
minOffset = 0;
maxOffset = startKeyLength;
- schema.iterator(startKey, minOffset, maxOffset, ptr, j+1);
+ schema.iterator(startKey, minOffset, maxOffset, ptr, ScanUtil.getRowKeyPosition(slotSpan, j)+1);
// Do nextKey after setting the accessor b/c otherwise the null byte may have
// been incremented causing us not to find it
ByteUtil.nextKey(startKey, currentLength);
@@ -393,7 +403,7 @@ public class SkipScanFilter extends FilterBase implements Writable {
}
i++;
// If we run out of slots in our key, it means we have a partial key.
- if (schema.next(ptr, i, maxOffset) == null) {
+ if (schema.next(ptr, ScanUtil.getRowKeyPosition(slotSpan, i), maxOffset, slotSpan[i]) == null) {
// If the rest of the slots are checking for IS NULL, then break because
// that's the case (since we don't store trailing nulls).
if (allTrailingNulls(i)) {
@@ -483,28 +493,35 @@ public class SkipScanFilter extends FilterBase implements Writable {
int andLen = in.readInt();
List<List<KeyRange>> slots = Lists.newArrayListWithExpectedSize(andLen);
for (int i=0; i<andLen; i++) {
- int orlen = in.readInt();
- List<KeyRange> orclause = Lists.newArrayListWithExpectedSize(orlen);
- slots.add(orclause);
- for (int j=0; j<orlen; j++) {
+ int orLen = in.readInt();
+ List<KeyRange> orClause = Lists.newArrayListWithExpectedSize(orLen);
+ slots.add(orClause);
+ for (int j=0; j<orLen; j++) {
KeyRange range = new KeyRange();
range.readFields(in);
- orclause.add(range);
+ orClause.add(range);
}
}
- this.init(slots, schema);
+ int[] slotSpan = new int[andLen];
+ for (int i = 0; i < andLen; i++) {
+ slotSpan[i] = in.readInt();
+ }
+ this.init(slots, slotSpan, schema);
}
@Override
public void write(DataOutput out) throws IOException {
schema.write(out);
out.writeInt(slots.size());
- for (List<KeyRange> orclause : slots) {
- out.writeInt(orclause.size());
- for (KeyRange range : orclause) {
+ for (List<KeyRange> orClause : slots) {
+ out.writeInt(orClause.size());
+ for (KeyRange range : orClause) {
range.write(out);
}
}
+ for (int span : slotSpan) {
+ out.writeInt(span);
+ }
}
@Override
http://git-wip-us.apache.org/repos/asf/phoenix/blob/3ebcbd76/phoenix-core/src/main/java/org/apache/phoenix/schema/RowKeySchema.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/RowKeySchema.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/RowKeySchema.java
index 4d98c69..510d11b 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/RowKeySchema.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/RowKeySchema.java
@@ -71,6 +71,8 @@ public class RowKeySchema extends ValueSchema {
return this.getMinNullable();
}
+ // "iterator" initialization methods that initialize a bytes ptr with a row key for further navigation
+
@edu.umd.cs.findbugs.annotations.SuppressWarnings(
value="NP_BOOLEAN_RETURN_NULL",
justification="Designed to return null.")
@@ -105,11 +107,12 @@ public class RowKeySchema extends ValueSchema {
public int iterator(ImmutableBytesWritable ptr) {
return iterator(ptr.get(),ptr.getOffset(),ptr.getLength(), ptr);
}
-
+
+ // navigation methods that "select" different chunks of the row key held in a bytes ptr
+
/**
- * Move the bytes ptr to the next position relative to the current ptr
- * @param ptr bytes pointer pointing to the value at the positional index
- * provided.
+ * Move the bytes ptr to the next position in the row key relative to its current position
+ * @param ptr bytes pointer pointing to the value at the positional index provided.
* @param position zero-based index of the next field in the value schema
* @param maxOffset max possible offset value when iterating
* @return true if a value was found and ptr was set, false if the value is null and ptr was not
@@ -151,6 +154,23 @@ public class RowKeySchema extends ValueSchema {
}
return ptr.getLength() > 0;
}
+
+ /**
+ * Like {@link #next(org.apache.hadoop.hbase.io.ImmutableBytesWritable, int, int)}, but also
+ * includes the next {@code extraSpan} additional fields in the bytes ptr.
+ * This allows multiple fields to be treated as one concatenated whole.
+ * @param ptr bytes pointer pointing to the value at the positional index provided.
+ * @param position zero-based index of the next field in the value schema
+ * @param maxOffset max possible offset value when iterating
+ * @param extraSpan the number of extra fields to expand the ptr to contain
+ * @return true if a value was found and ptr was set, false if the value is null and ptr was not
+ * set, and null if the value is null and there are no more values
+ */
+ public Boolean next(ImmutableBytesWritable ptr, int position, int maxOffset, int extraSpan) {
+ Boolean returnValue = next(ptr, position, maxOffset);
+ readExtraFields(ptr, position + 1, maxOffset, extraSpan);
+ return returnValue;
+ }
@edu.umd.cs.findbugs.annotations.SuppressWarnings(
value="NP_BOOLEAN_RETURN_NULL",
@@ -238,4 +258,38 @@ public class RowKeySchema extends ValueSchema {
return hasValue;
}
+
+ /**
+ * Like {@link #reposition(org.apache.hadoop.hbase.io.ImmutableBytesWritable, int, int, int, int)},
+ * but also includes the next {@code extraSpan} additional fields in the bytes ptr.
+ * This allows multiple fields to be treated as one concatenated whole.
+ * @param extraSpan the number of extra fields to expand the ptr to contain.
+ */
+ public Boolean reposition(ImmutableBytesWritable ptr, int oldPosition, int newPosition, int minOffset, int maxOffset, int extraSpan) {
+ Boolean returnValue = reposition(ptr, oldPosition, newPosition, minOffset, maxOffset);
+ readExtraFields(ptr, newPosition + 1, maxOffset, extraSpan);
+ return returnValue;
+ }
+
+ /**
+ * Extends the boundaries of the {@code ptr} to contain the next {@code extraSpan} fields in the row key.
+ * @param ptr bytes pointer pointing to the value at the positional index provided.
+ * @param position row key position of the first extra key to read
+ * @param maxOffset the maximum offset into the bytes pointer to allow
+ * @param extraSpan the number of extra fields to expand the ptr to contain.
+ */
+ private void readExtraFields(ImmutableBytesWritable ptr, int position, int maxOffset, int extraSpan) {
+ int initialOffset = ptr.getOffset();
+
+ for(int i = 0; i < extraSpan; i++) {
+ Boolean returnValue = next(ptr, position + i, maxOffset);
+
+ if(returnValue == null) {
+ break;
+ }
+ }
+
+ int finalLength = ptr.getOffset() - initialOffset + ptr.getLength();
+ ptr.set(ptr.get(), initialOffset, finalLength);
+ }
}
http://git-wip-us.apache.org/repos/asf/phoenix/blob/3ebcbd76/phoenix-core/src/main/java/org/apache/phoenix/util/ScanUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/ScanUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/util/ScanUtil.java
index d8e7f1b..42b20fe 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/ScanUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/ScanUtil.java
@@ -304,7 +304,7 @@ public class ScanUtil {
// but the index for the field it represents in the schema
// should be incremented by 1 + value in the current slotSpan index
// slotSpan stores the number of columns beyond one that the range spans
- int i = slotStartIndex, fieldIndex = slotStartIndex;
+ int i = slotStartIndex, fieldIndex = ScanUtil.getRowKeyPosition(slotSpan, slotStartIndex);
for (i = slotStartIndex; i < slotEndIndex; i++) {
// Build up the key by appending the bound of each key range
// from the current position of each slot.
@@ -519,12 +519,37 @@ public class ScanUtil {
return new int[nSlots];
}
- public static int calculateSlotSpan(List<List<KeyRange>> ranges, int[] slotSpan) {
- int nSlots = ranges.size();
- int totalSlotSpan = nSlots;
- for (int i = 0; i < nSlots; i++) {
- totalSlotSpan += slotSpan[i];
+ /**
+ * Finds the total number of row keys spanned by this ranges / slotSpan pair.
+ * This accounts for slots in the ranges that may span more than on row key.
+ * @param ranges the KeyRange slots paired with this slotSpan. corresponds to {@link ScanRanges#ranges}
+ * @param slotSpan the extra span per skip scan slot. corresponds to {@link ScanRanges#slotSpan}
+ * @return the total number of row keys spanned yb this ranges / slotSpan pair.
+ * @see #getRowKeyPosition(int[], int)
+ */
+ public static int getTotalSpan(List<List<KeyRange>> ranges, int[] slotSpan) {
+ // finds the position at the "end" of the ranges, which is also the total span
+ return getRowKeyPosition(slotSpan, ranges.size());
+ }
+
+ /**
+ * Finds the position in the row key schema for a given position in the scan slots.
+ * For example, with a slotSpan of {0, 1, 0}, the slot at index 1 spans an extra column in the row key. This means
+ * that the slot at index 2 has a slot index of 2 but a row key index of 3.
+ * To calculate the "adjusted position" index, we simply add up the number of extra slots spanned and offset
+ * the slotPosition by that much.
+ * @param slotSpan the extra span per skip scan slot. corresponds to {@link ScanRanges#slotSpan}
+ * @param slotPosition the index of a slot in the SkipScan slots list.
+ * @return the equivalent row key position in the RowKeySchema
+ * @see #getTotalSpan(java.util.List, int[])
+ */
+ public static int getRowKeyPosition(int[] slotSpan, int slotPosition) {
+ int offset = 0;
+
+ for(int i = 0; i < slotPosition; i++) {
+ offset += slotSpan[i];
}
- return totalSlotSpan;
+
+ return offset + slotPosition;
}
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/phoenix/blob/3ebcbd76/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java b/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java
index 2bfe381..bd19663 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java
@@ -1565,7 +1565,7 @@ public class WhereOptimizerTest extends BaseConnectionlessQueryTest {
assertArrayEquals(HConstants.EMPTY_START_ROW, scan.getStartRow());
assertArrayEquals(HConstants.EMPTY_END_ROW, scan.getStopRow());
}
-
+
@Test
public void testUsingRVCNonFullyQualifiedInClause() throws Exception {
String firstOrgId = "000000000000001";
@@ -1577,7 +1577,7 @@ public class WhereOptimizerTest extends BaseConnectionlessQueryTest {
StatementContext context = compileStatement(query, binds);
Scan scan = context.getScan();
Filter filter = scan.getFilter();
- assertTrue(filter instanceof RowKeyComparisonFilter);
+ assertTrue(filter instanceof SkipScanFilter);
assertArrayEquals(ByteUtil.concat(PDataType.VARCHAR.toBytes(firstOrgId), PDataType.VARCHAR.toBytes(firstParentId)), scan.getStartRow());
assertArrayEquals(ByteUtil.nextKey(ByteUtil.concat(PDataType.VARCHAR.toBytes(secondOrgId), PDataType.VARCHAR.toBytes(secondParentId))), scan.getStopRow());
}