Repository: phoenix Updated Branches: refs/heads/4.3 ad2c9ab2d -> 8b6ab90a7
PHOENIX-1709 And expression of primary key RVCs can not compile Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/8b6ab90a Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/8b6ab90a Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/8b6ab90a Branch: refs/heads/4.3 Commit: 8b6ab90a7def3fef31c9a6c98ef1ee1bb6a818d8 Parents: ad2c9ab Author: James Taylor <jtay...@salesforce.com> Authored: Sat Mar 7 10:24:18 2015 -0800 Committer: James Taylor <jtay...@salesforce.com> Committed: Tue Mar 10 11:03:55 2015 -0700 ---------------------------------------------------------------------- .../apache/phoenix/compile/WhereOptimizer.java | 28 +++++++------- .../org/apache/phoenix/schema/RowKeySchema.java | 39 ++++++++++++++++++-- .../phoenix/compile/WhereOptimizerTest.java | 38 +++++++++++++++++++ 3 files changed, 86 insertions(+), 19 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/phoenix/blob/8b6ab90a/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 a1a349a..713076e 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 @@ -71,7 +71,6 @@ import org.apache.phoenix.util.ScanUtil; import org.apache.phoenix.util.SchemaUtil; import org.apache.phoenix.util.StringUtil; -import com.google.common.base.Preconditions; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; import com.google.common.collect.Sets; @@ -1068,9 +1067,6 @@ public class WhereOptimizer { } public final KeySlot intersect(KeySlot that) { - Preconditions.checkArgument(!this.keyRanges.isEmpty()); - Preconditions.checkArgument(!that.keyRanges.isEmpty()); - if (this.getPKSpan() == 1 && that.getPKSpan() == 1) { if (this.getPKPosition() != that.getPKPosition()) { throw new IllegalArgumentException("Position must be equal for intersect"); @@ -1102,8 +1098,6 @@ public class WhereOptimizer { } else { assert(this.getPKSpan() > 1); assert(this.getPKPosition() <= that.getPKPosition()); - int position = this.getPKPosition(); - int thatPosition = that.getPKPosition(); List<KeyRange> newKeyRanges = Lists.newArrayListWithExpectedSize(this.getKeyRanges().size()); // We know we have a set of RVCs (i.e. multi-span key ranges) // Get the PK slot value in the RVC for the position of the other KeySlot @@ -1112,17 +1106,21 @@ public class WhereOptimizer { for (KeyRange keyRange : this.getKeyRanges()) { assert(keyRange.isSingleKey()); byte[] key = keyRange.getLowerRange(); - schema.iterator(key, ptr); // initialize for iteration - while (position < thatPosition) { // skip to part of RVC that overlaps - schema.next(ptr, position++, key.length); - } - // Create a range just for the overlapping column - List<KeyRange> slotKeyRanges = Collections.singletonList(KeyRange.getKeyRange(ByteUtil.copyKeyBytesIfNecessary(ptr))); - // Intersect with other ranges and add to list if it overlaps - if (!isDegenerate(KeyRange.intersect(slotKeyRanges, that.getKeyRanges()))) { - newKeyRanges.add(keyRange); + int position = this.getPKPosition(); + int thatPosition = that.getPKPosition(); + ptr.set(key); + if (schema.position(ptr, position, thatPosition)) { + // Create a range just for the overlapping column + List<KeyRange> slotKeyRanges = Collections.singletonList(KeyRange.getKeyRange(ByteUtil.copyKeyBytesIfNecessary(ptr))); + // Intersect with other ranges and add to list if it overlaps + if (!isDegenerate(KeyRange.intersect(slotKeyRanges, that.getKeyRanges()))) { + newKeyRanges.add(keyRange); + } } } + if (isDegenerate(newKeyRanges)) { + return null; + } return new KeySlot( new BaseKeyPart(this.getKeyPart().getColumn(), SchemaUtil.concat(this.getKeyPart().getExtractNodes(), http://git-wip-us.apache.org/repos/asf/phoenix/blob/8b6ab90a/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 7c36e41..e5aa571 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 @@ -23,7 +23,6 @@ import java.util.Collections; import java.util.List; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; - import org.apache.phoenix.query.QueryConstants; @@ -111,17 +110,23 @@ public class RowKeySchema extends ValueSchema { // navigation methods that "select" different chunks of the row key held in a bytes ptr /** - * Move the bytes ptr to the next position in the row key relative to its current position + * Move the bytes ptr to the next position in the row key relative to its current position. You + * must have a complete row key. Use @link {@link #position(ImmutableBytesWritable, int, int)} + * if you have a partial row key. * @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 * set, and null if the value is null and there are no more values */ + public Boolean next(ImmutableBytesWritable ptr, int position, int maxOffset) { + return next(ptr, position, maxOffset, false); + } + @edu.umd.cs.findbugs.annotations.SuppressWarnings( value="NP_BOOLEAN_RETURN_NULL", justification="Designed to return null.") - public Boolean next(ImmutableBytesWritable ptr, int position, int maxOffset) { + private Boolean next(ImmutableBytesWritable ptr, int position, int maxOffset, boolean isFirst) { if (ptr.getOffset() + ptr.getLength() >= maxOffset) { ptr.set(ptr.get(), maxOffset, 0); return null; @@ -134,7 +139,9 @@ public class RowKeySchema extends ValueSchema { // backing byte array. ptr.set(ptr.get(), ptr.getOffset() + ptr.getLength(), 0); // If positioned at SEPARATOR_BYTE, skip it. - if (position > 0 && !getField(position-1).getDataType().isFixedWidth()) { + // Don't look back at previous fields if this is our first next call, as + // we may have a partial key for RVCs that doesn't include the leading field. + if (position > 0 && !isFirst && !getField(position-1).getDataType().isFixedWidth()) { ptr.set(ptr.get(), ptr.getOffset()+ptr.getLength()+1, 0); } Field field = this.getField(position); @@ -270,6 +277,30 @@ public class RowKeySchema extends ValueSchema { readExtraFields(ptr, newPosition + 1, maxOffset, extraSpan); return returnValue; } + + + /** + * Positions ptr at the part of the row key for the field at endPosition, + * starting from the field at position. + * @param ptr bytes pointer that points to row key being traversed. + * @param position the starting field position + * @param endPosition the ending field position + * @return true if the row key has a value at endPosition with ptr pointing to + * that value and false otherwise with ptr not necessarily set. + */ + public boolean position(ImmutableBytesWritable ptr, int position, int endPosition) { + int maxOffset = ptr.getLength(); + this.iterator(ptr); // initialize for iteration + boolean isFirst = true; + while (position <= endPosition) { + if (this.next(ptr, position++, maxOffset, isFirst) == null) { + return false; + } + isFirst = false; + } + return true; + } + /** * Extends the boundaries of the {@code ptr} to contain the next {@code extraSpan} fields in the row key. http://git-wip-us.apache.org/repos/asf/phoenix/blob/8b6ab90a/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 ddbacb7..0ec6b45 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 @@ -57,6 +57,7 @@ import org.apache.phoenix.query.QueryConstants; import org.apache.phoenix.schema.ColumnNotFoundException; import org.apache.phoenix.schema.types.PChar; import org.apache.phoenix.schema.types.PDate; +import org.apache.phoenix.schema.types.PInteger; import org.apache.phoenix.schema.types.PUnsignedLong; import org.apache.phoenix.schema.types.PVarchar; import org.apache.phoenix.util.ByteUtil; @@ -1818,4 +1819,41 @@ public class WhereOptimizerTest extends BaseConnectionlessQueryTest { assertArrayEquals(Bytes.toBytes("a"), scan.getStartRow()); assertArrayEquals(ByteUtil.concat(Bytes.toBytes("a"), QueryConstants.SEPARATOR_BYTE_ARRAY, Bytes.toBytes("b"), QueryConstants.SEPARATOR_BYTE_ARRAY), scan.getStopRow()); } + + @Test + public void testAndWithRVC() throws Exception { + String ddl; + String query; + StatementContext context; + Connection conn = DriverManager.getConnection(getUrl()); + + ddl = "create table t (a integer not null, b integer not null, c integer constraint pk primary key (a,b))"; + conn.createStatement().execute(ddl); + conn.close(); + + query = "select c from t where (a,b) in ( (1,2) , (1,3) ) and b = 4"; + context = compileStatement(query, Collections.<Object>emptyList()); + assertDegenerate(context.getScan()); + + query = "select c from t where a in (1,2) and b = 3 and (a,b) in ( (1,2) , (1,3))"; + context = compileStatement(query, Collections.<Object>emptyList()); + assertArrayEquals(ByteUtil.concat(PInteger.INSTANCE.toBytes(1), PInteger.INSTANCE.toBytes(3)), context.getScan().getStartRow()); + assertArrayEquals(ByteUtil.concat(PInteger.INSTANCE.toBytes(1), ByteUtil.nextKey(PInteger.INSTANCE.toBytes(3))), context.getScan().getStopRow()); + + query = "select c from t where a = 1 and b = 3 and (a,b) in ( (1,2) , (1,3))"; + context = compileStatement(query, Collections.<Object>emptyList()); + assertArrayEquals(ByteUtil.concat(PInteger.INSTANCE.toBytes(1), PInteger.INSTANCE.toBytes(3)), context.getScan().getStartRow()); + assertArrayEquals(ByteUtil.concat(PInteger.INSTANCE.toBytes(1), ByteUtil.nextKey(PInteger.INSTANCE.toBytes(3))), context.getScan().getStopRow()); + + // Test with RVC occurring later in the PK + ddl = "create table t1 (d varchar, e char(3) not null, a integer not null, b integer not null, c integer constraint pk primary key (d, e, a,b))"; + conn.createStatement().execute(ddl); + conn.close(); + + query = "select c from t1 where d = 'a' and e = 'foo' and a in (1,2) and b = 3 and (a,b) in ( (1,2) , (1,3))"; + context = compileStatement(query, Collections.<Object>emptyList()); + assertArrayEquals(ByteUtil.concat(PVarchar.INSTANCE.toBytes("a"), QueryConstants.SEPARATOR_BYTE_ARRAY, PChar.INSTANCE.toBytes("foo"), PInteger.INSTANCE.toBytes(1), PInteger.INSTANCE.toBytes(3)), context.getScan().getStartRow()); + assertArrayEquals(ByteUtil.concat(PVarchar.INSTANCE.toBytes("a"), QueryConstants.SEPARATOR_BYTE_ARRAY, PChar.INSTANCE.toBytes("foo"), PInteger.INSTANCE.toBytes(1), ByteUtil.nextKey(PInteger.INSTANCE.toBytes(3))), context.getScan().getStopRow()); + } + }