PHOENIX-2900 Unable to find hash cache once a salted table 's first region has split (chenglei)
Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/0b39a520 Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/0b39a520 Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/0b39a520 Branch: refs/heads/encodecolumns Commit: 0b39a520970cbba36560e814ba2ae7ea8719b655 Parents: 925d3ee Author: James Taylor <[email protected]> Authored: Sat Jul 23 09:49:33 2016 -0700 Committer: James Taylor <[email protected]> Committed: Sat Jul 23 12:55:29 2016 -0700 ---------------------------------------------------------------------- .../end2end/BaseTenantSpecificViewIndexIT.java | 2 +- .../org/apache/phoenix/end2end/BaseViewIT.java | 4 +- .../phoenix/end2end/index/SaltedIndexIT.java | 4 +- .../apache/phoenix/cache/ServerCacheClient.java | 5 +- .../org/apache/phoenix/compile/ScanRanges.java | 88 +++---- .../phoenix/compile/SaltedScanRangesTest.java | 231 +++++++++++++++++++ .../apache/phoenix/compile/ScanRangesTest.java | 8 +- .../org/apache/phoenix/query/QueryPlanTest.java | 4 +- 8 files changed, 291 insertions(+), 55 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/phoenix/blob/0b39a520/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseTenantSpecificViewIndexIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseTenantSpecificViewIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseTenantSpecificViewIndexIT.java index 11a33da..04f4268 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseTenantSpecificViewIndexIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseTenantSpecificViewIndexIT.java @@ -142,7 +142,7 @@ public class BaseTenantSpecificViewIndexIT extends BaseHBaseManagedTimeIT { String expected = saltBuckets == null ? "CLIENT PARALLEL 1-WAY RANGE SCAN OVER _IDX_T [-32768,'" + tenantId + "','" + valuePrefix + "v2-1']\n" + " SERVER FILTER BY FIRST KEY ONLY" : - "CLIENT PARALLEL 3-WAY RANGE SCAN OVER _IDX_T [0,-32768,'" + tenantId + "','" + valuePrefix + "v2-1']\n" + "CLIENT PARALLEL 3-WAY RANGE SCAN OVER _IDX_T [0,-32768,'" + tenantId + "','" + valuePrefix + "v2-1'] - ["+(saltBuckets.intValue()-1)+",-32768,'" + tenantId + "','" + valuePrefix + "v2-1']\n" + " SERVER FILTER BY FIRST KEY ONLY\n" + "CLIENT MERGE SORT"; assertEquals(expected, QueryUtil.getExplainPlan(rs)); http://git-wip-us.apache.org/repos/asf/phoenix/blob/0b39a520/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseViewIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseViewIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseViewIT.java index 6d8d889..d9a59a9 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseViewIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseViewIT.java @@ -210,7 +210,7 @@ public abstract class BaseViewIT extends BaseOwnClusterHBaseManagedTimeIT { } else { assertEquals(saltBuckets == null ? "CLIENT PARALLEL 1-WAY RANGE SCAN OVER _IDX_" + tableName +" [" + Short.MIN_VALUE + ",51]" - : "CLIENT PARALLEL " + saltBuckets + "-WAY RANGE SCAN OVER _IDX_T" + (transactional ? "_TXN" : "") + " [0," + Short.MIN_VALUE + ",51]\nCLIENT MERGE SORT", + : "CLIENT PARALLEL " + saltBuckets + "-WAY RANGE SCAN OVER _IDX_T" + (transactional ? "_TXN" : "") + " [0," + Short.MIN_VALUE + ",51] - ["+(saltBuckets.intValue()-1)+"," + Short.MIN_VALUE + ",51]\nCLIENT MERGE SORT", queryPlan); } @@ -251,7 +251,7 @@ public abstract class BaseViewIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals(saltBuckets == null ? "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + htableName +" [" + (Short.MIN_VALUE+1) + ",'foo']\n" + " SERVER FILTER BY FIRST KEY ONLY" - : "CLIENT PARALLEL " + saltBuckets + "-WAY RANGE SCAN OVER " + htableName + " [0," + (Short.MIN_VALUE+1) + ",'foo']\n" + : "CLIENT PARALLEL " + saltBuckets + "-WAY RANGE SCAN OVER " + htableName + " [0," + (Short.MIN_VALUE+1) + ",'foo'] - ["+(saltBuckets.intValue()-1)+"," + (Short.MIN_VALUE+1) + ",'foo']\n" + " SERVER FILTER BY FIRST KEY ONLY\n" + "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs)); http://git-wip-us.apache.org/repos/asf/phoenix/blob/0b39a520/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/SaltedIndexIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/SaltedIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/SaltedIndexIT.java index b5c8477..5b2b15a 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/SaltedIndexIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/SaltedIndexIT.java @@ -143,7 +143,7 @@ public class SaltedIndexIT extends BaseHBaseManagedTimeIT { expectedPlan = indexSaltBuckets == null ? "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + TestUtil.DEFAULT_INDEX_TABLE_FULL_NAME + " [~'y']\n" + " SERVER FILTER BY FIRST KEY ONLY" : - ("CLIENT PARALLEL 4-WAY RANGE SCAN OVER " + TestUtil.DEFAULT_INDEX_TABLE_FULL_NAME + " [0,~'y']\n" + + ("CLIENT PARALLEL 4-WAY RANGE SCAN OVER " + TestUtil.DEFAULT_INDEX_TABLE_FULL_NAME + " [0,~'y'] - ["+(indexSaltBuckets.intValue()-1)+",~'y']\n" + " SERVER FILTER BY FIRST KEY ONLY\n" + "CLIENT MERGE SORT"); assertEquals(expectedPlan,QueryUtil.getExplainPlan(rs)); @@ -164,7 +164,7 @@ public class SaltedIndexIT extends BaseHBaseManagedTimeIT { expectedPlan = indexSaltBuckets == null ? "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + TestUtil.DEFAULT_INDEX_TABLE_FULL_NAME + " [*] - [~'x']\n" + " SERVER FILTER BY FIRST KEY ONLY" : - ("CLIENT PARALLEL 4-WAY RANGE SCAN OVER " + TestUtil.DEFAULT_INDEX_TABLE_FULL_NAME + " [0,*] - [0,~'x']\n" + ("CLIENT PARALLEL 4-WAY RANGE SCAN OVER " + TestUtil.DEFAULT_INDEX_TABLE_FULL_NAME + " [0,*] - ["+(indexSaltBuckets.intValue()-1)+",~'x']\n" + " SERVER FILTER BY FIRST KEY ONLY\n" + "CLIENT MERGE SORT"); assertEquals(expectedPlan,QueryUtil.getExplainPlan(rs)); http://git-wip-us.apache.org/repos/asf/phoenix/blob/0b39a520/phoenix-core/src/main/java/org/apache/phoenix/cache/ServerCacheClient.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/cache/ServerCacheClient.java b/phoenix-core/src/main/java/org/apache/phoenix/cache/ServerCacheClient.java index d88b094..67fc410 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/cache/ServerCacheClient.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/cache/ServerCacheClient.java @@ -172,9 +172,8 @@ public class ServerCacheClient { byte[] regionStartKey = entry.getRegionInfo().getStartKey(); byte[] regionEndKey = entry.getRegionInfo().getEndKey(); if ( ! servers.contains(entry) && - keyRanges.intersects(regionStartKey, regionEndKey, - cacheUsingTable.getIndexType() == IndexType.LOCAL ? - ScanUtil.getRowKeyOffset(regionStartKey, regionEndKey) : 0, true)) { + keyRanges.intersectRegion(regionStartKey, regionEndKey, + cacheUsingTable.getIndexType() == IndexType.LOCAL)) { // Call RPC once per server servers.add(entry); if (LOG.isDebugEnabled()) {LOG.debug(addCustomAnnotations("Adding cache entry to be sent for " + entry, connection));} http://git-wip-us.apache.org/repos/asf/phoenix/blob/0b39a520/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 6f0adb0..f3d5bba 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 @@ -68,6 +68,11 @@ public class ScanRanges { return create(schema, ranges, ScanUtil.getDefaultSlotSpans(ranges.size()), KeyRange.EVERYTHING_RANGE, null, true, -1); } + // For testing + public static ScanRanges createSingleSpan(RowKeySchema schema, List<List<KeyRange>> ranges, Integer nBuckets, boolean useSkipSan) { + return create(schema, ranges, ScanUtil.getDefaultSlotSpans(ranges.size()), KeyRange.EVERYTHING_RANGE, nBuckets, useSkipSan, -1); + } + public static ScanRanges create(RowKeySchema schema, List<List<KeyRange>> ranges, int[] slotSpan, KeyRange minMaxRange, Integer nBuckets, boolean useSkipScan, int rowTimestampColIndex) { int offset = nBuckets == null ? 0 : SaltingUtil.NUM_SALTING_BYTES; int nSlots = ranges.size(); @@ -170,12 +175,8 @@ public class ScanRanges { this.minMaxRange = minMaxRange; this.rowTimestampRange = rowTimestampRange; - // Only blow out the bucket values if we're using the skip scan. We need all the - // bucket values in this case because we use intersect against a key that may have - // any of the possible bucket values. Otherwise, we can pretty easily ignore the - // bucket values. - if (useSkipScanFilter && isSalted && !isPointLookup) { - ranges.set(0, SaltingUtil.generateAllSaltingRanges(bucketNum)); + if (isSalted && !isPointLookup) { + ranges.set(0, SaltingUtil.generateAllSaltingRanges(bucketNum)); } this.ranges = ImmutableList.copyOf(ranges); this.slotSpan = slotSpan; @@ -243,7 +244,6 @@ public class ScanRanges { if (stopKey.length > 0 && Bytes.compareTo(startKey, stopKey) >= 0) { return null; } - boolean mayHaveRows = false; // Keep the keys as they are if we have a point lookup, as we've already resolved the // salt bytes in that case. final int scanKeyOffset = this.isSalted && !this.isPointLookup ? SaltingUtil.NUM_SALTING_BYTES : 0; @@ -268,49 +268,53 @@ public class ScanRanges { } } int scanStartKeyOffset = scanKeyOffset; - byte[] scanStartKey = scan == null ? ByteUtil.EMPTY_BYTE_ARRAY : scan.getStartRow(); + byte[] scanStartKey = scan == null ? this.scanRange.getLowerRange() : scan.getStartRow(); // Compare ignoring key prefix and salt byte - if (scanStartKey.length > 0) { - if (startKey.length > 0 && Bytes.compareTo(scanStartKey, scanKeyOffset, scanStartKey.length - scanKeyOffset, startKey, totalKeyOffset, startKey.length - totalKeyOffset) < 0) { - scanStartKey = startKey; - scanStartKeyOffset = totalKeyOffset; + if (scanStartKey.length - scanKeyOffset > 0) { + if (startKey.length - totalKeyOffset > 0) { + if (Bytes.compareTo(scanStartKey, scanKeyOffset, scanStartKey.length - scanKeyOffset, startKey, totalKeyOffset, startKey.length - totalKeyOffset) < 0) { + scanStartKey = startKey; + scanStartKeyOffset = totalKeyOffset; + } } } else { - scanStartKey = startKey; + scanStartKey = startKey; scanStartKeyOffset = totalKeyOffset; - mayHaveRows = true; } int scanStopKeyOffset = scanKeyOffset; - byte[] scanStopKey = scan == null ? ByteUtil.EMPTY_BYTE_ARRAY : scan.getStopRow(); - if (scanStopKey.length > 0) { - if (stopKey.length > 0 && Bytes.compareTo(scanStopKey, scanKeyOffset, scanStopKey.length - scanKeyOffset, stopKey, totalKeyOffset, stopKey.length - totalKeyOffset) > 0) { - scanStopKey = stopKey; - scanStopKeyOffset = totalKeyOffset; + byte[] scanStopKey = scan == null ? this.scanRange.getUpperRange() : scan.getStopRow(); + if (scanStopKey.length - scanKeyOffset > 0) { + if (stopKey.length - totalKeyOffset > 0) { + if (Bytes.compareTo(scanStopKey, scanKeyOffset, scanStopKey.length - scanKeyOffset, stopKey, totalKeyOffset, stopKey.length - totalKeyOffset) > 0) { + scanStopKey = stopKey; + scanStopKeyOffset = totalKeyOffset; + } } } else { - scanStopKey = stopKey; + scanStopKey = stopKey; scanStopKeyOffset = totalKeyOffset; - mayHaveRows = true; } - mayHaveRows = mayHaveRows || Bytes.compareTo(scanStartKey, scanStartKeyOffset, scanStartKey.length - scanStartKeyOffset, scanStopKey, scanStopKeyOffset, scanStopKey.length - scanStopKeyOffset) < 0; - if (!mayHaveRows) { + // If not scanning anything, return null + if (scanStopKey.length - scanStopKeyOffset > 0 && + Bytes.compareTo(scanStartKey, scanStartKeyOffset, scanStartKey.length - scanStartKeyOffset, + scanStopKey, scanStopKeyOffset, scanStopKey.length - scanStopKeyOffset) >= 0) { return null; } if (originalStopKey.length != 0 && scanStopKey.length == 0) { scanStopKey = originalStopKey; } Filter newFilter = null; - // If the scan is using skip scan filter, intersect and replace the filter. - if (scan == null || this.useSkipScanFilter()) { + // Only if the scan is using skip scan filter, intersect and replace the filter. + // For example, we may be forcing a range scan, in which case we do not want to + // intersect the start/stop with the filter. Instead, we rely only on the scan + // start/stop or the scanRanges start/stop. + if (this.useSkipScanFilter()) { byte[] skipScanStartKey = scanStartKey; byte[] skipScanStopKey = scanStopKey; // If we have a keyOffset and we've used the startKey/stopKey that // were passed in (which have the prefix) for the above range check, // we need to remove the prefix before running our intersect method. - // TODO: we could use skipScanFilter.setOffset(keyOffset) if both - // the startKey and stopKey were used above *and* our intersect - // method honored the skipScanFilter.offset variable. if (scanKeyOffset > 0) { if (skipScanStartKey != originalStartKey) { // original already has correct salt byte skipScanStartKey = replaceSaltByte(skipScanStartKey, prefixBytes); @@ -360,6 +364,10 @@ public class ScanRanges { scanStopKey = ScanUtil.getMaxKey(schema, newSkipScanFilter.getSlots(), slotSpan); } } + // If we've got this far, we know we have an intersection + if (scan == null) { + return HAS_INTERSECTION; + } if (newFilter == null) { newFilter = scan.getFilter(); } @@ -391,29 +399,27 @@ public class ScanRanges { } /** - * Return true if the range formed by the lowerInclusiveKey and upperExclusiveKey - * intersects with the scan ranges and false otherwise. We cannot pass in - * a KeyRange here, because the underlying compare functions expect lower inclusive - * and upper exclusive keys. We cannot get their next key because the key must - * conform to the row key schema and if a null byte is added to a lower inclusive - * key, it's no longer a valid, real key. - * @param lowerInclusiveKey lower inclusive key - * @param upperExclusiveKey upper exclusive key - * @param crossesRegionBoundary whether or not the upperExclusiveKey spans upto - * or after the next region. + * Return true if the region with the start and end key + * intersects with the scan ranges and false otherwise. + * @param regionStartKey lower inclusive key + * @param regionEndKey upper exclusive key + * @param isLocalIndex true if the table being scanned is a local index * @return true if the scan range intersects with the specified lower/upper key * range */ - public boolean intersects(byte[] lowerInclusiveKey, byte[] upperExclusiveKey, int keyOffset, boolean crossesRegionBoundary) { + public boolean intersectRegion(byte[] regionStartKey, byte[] regionEndKey, boolean isLocalIndex) { if (isEverything()) { return true; } if (isDegenerate()) { return false; } + // Every range intersects all regions of a local index table + if (isLocalIndex) { + return true; + } - //return filter.hasIntersect(lowerInclusiveKey, upperExclusiveKey); - return intersectScan(null, lowerInclusiveKey, upperExclusiveKey, keyOffset, crossesRegionBoundary) == HAS_INTERSECTION; + return intersectScan(null, regionStartKey, regionEndKey, 0, false) == HAS_INTERSECTION; } public SkipScanFilter getSkipScanFilter() { http://git-wip-us.apache.org/repos/asf/phoenix/blob/0b39a520/phoenix-core/src/test/java/org/apache/phoenix/compile/SaltedScanRangesTest.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/test/java/org/apache/phoenix/compile/SaltedScanRangesTest.java b/phoenix-core/src/test/java/org/apache/phoenix/compile/SaltedScanRangesTest.java new file mode 100644 index 0000000..f8eb3a0 --- /dev/null +++ b/phoenix-core/src/test/java/org/apache/phoenix/compile/SaltedScanRangesTest.java @@ -0,0 +1,231 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * 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 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 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 + * limitations under the License. + */ +package org.apache.phoenix.compile; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.phoenix.query.KeyRange; +import org.apache.phoenix.schema.PDatum; +import org.apache.phoenix.schema.RowKeySchema.RowKeySchemaBuilder; +import org.apache.phoenix.schema.SaltingUtil; +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.apache.phoenix.util.ByteUtil; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import com.google.common.base.Function; +import com.google.common.collect.Lists; + + +/** + * Test for intersect method in {@link ScanRanges} over salted data + */ +@RunWith(Parameterized.class) +public class SaltedScanRangesTest { + + private static Integer nBuckets = 3; + private final ScanRanges scanRanges; + private final KeyRange keyRange; + private final boolean expectedResult; + + public SaltedScanRangesTest(ScanRanges scanRanges, int[] widths, + KeyRange keyRange, boolean expectedResult) { + this.keyRange = keyRange; + this.scanRanges = scanRanges; + this.expectedResult = expectedResult; + } + + @Test + public void test() { + byte[] lowerInclusiveKey = keyRange.getLowerRange(); + if (!keyRange.isLowerInclusive() && !Bytes.equals(lowerInclusiveKey, KeyRange.UNBOUND)) { + // This assumes the last key is fixed length, otherwise the results may be incorrect + // since there's no terminating 0 byte for a variable length key and thus we may be + // incrementing the key too much. + lowerInclusiveKey = ByteUtil.nextKey(lowerInclusiveKey); + } + byte[] upperExclusiveKey = keyRange.getUpperRange(); + if (keyRange.isUpperInclusive()) { + // This assumes the last key is fixed length, otherwise the results may be incorrect + // since there's no terminating 0 byte for a variable length key and thus we may be + // incrementing the key too much. + upperExclusiveKey = ByteUtil.nextKey(upperExclusiveKey); + } + assertEquals(expectedResult, scanRanges.intersectRegion(lowerInclusiveKey,upperExclusiveKey,false)); + } + + @Parameters(name="{0} {2}") + public static Collection<Object> data() { + List<Object> testCases = Lists.newArrayList(); + testCases.addAll( + foreach(new KeyRange[][]{{ + PVarchar.INSTANCE.getKeyRange(Bytes.toBytes("c"), true, Bytes.toBytes("e"), false), + }}, + new int[] {0}, + KeyRange.getKeyRange(KeyRange.UNBOUND, new byte[]{1}), + false, + true)); + testCases.addAll( + foreach(new KeyRange[][]{{ + PVarchar.INSTANCE.getKeyRange(Bytes.toBytes("c"), true, Bytes.toBytes("e"), false), + }}, + new int[] {0}, + KeyRange.getKeyRange(new byte[]{1},new byte[]{2}), + false, + true)); + testCases.addAll( + foreach(new KeyRange[][]{{ + PVarchar.INSTANCE.getKeyRange(Bytes.toBytes("c"), true, Bytes.toBytes("e"), false), + }}, + new int[] {0}, + KeyRange.getKeyRange(new byte[]{2},KeyRange.UNBOUND), + false, + true)); + testCases.addAll( + foreach(new KeyRange[][]{{ + PVarchar.INSTANCE.getKeyRange(Bytes.toBytes("c"), true, Bytes.toBytes("e"), false), + }}, + new int[] {0}, + KeyRange.getKeyRange(new byte[]{1},ByteUtil.concat(new byte[]{1}, Bytes.toBytes("c"))), + false, + false)); + testCases.addAll( + foreach(new KeyRange[][]{{ + PVarchar.INSTANCE.getKeyRange(Bytes.toBytes("c"), true, Bytes.toBytes("e"), false), + }}, + new int[] {0}, + KeyRange.getKeyRange(ByteUtil.concat(new byte[]{1}, Bytes.toBytes("e")), new byte[]{2}), + false, + false)); + testCases.addAll( + foreach(new KeyRange[][]{{ + PVarchar.INSTANCE.getKeyRange(Bytes.toBytes("c"), true, Bytes.toBytes("e"), false), + }}, + new int[] {0}, + KeyRange.getKeyRange(ByteUtil.concat(new byte[]{1}, Bytes.toBytes("d")), new byte[]{2}), + false, + true)); + testCases.addAll( + foreach(new KeyRange[][]{{ + PVarchar.INSTANCE.getKeyRange(Bytes.toBytes("c"), true, Bytes.toBytes("e"), false), + PVarchar.INSTANCE.getKeyRange(Bytes.toBytes("h"), true, Bytes.toBytes("i"), false), + PVarchar.INSTANCE.getKeyRange(Bytes.toBytes("m"), true, Bytes.toBytes("p"), false), + }}, + new int[] {0}, + KeyRange.getKeyRange(ByteUtil.concat(new byte[]{1}, Bytes.toBytes("f")), ByteUtil.concat(new byte[]{1}, Bytes.toBytes("g"))), + false, + true)); + testCases.addAll( + foreach(new KeyRange[][]{{ + PVarchar.INSTANCE.getKeyRange(Bytes.toBytes("c"), true, Bytes.toBytes("e"), false), + PVarchar.INSTANCE.getKeyRange(Bytes.toBytes("h"), true, Bytes.toBytes("i"), false), + PVarchar.INSTANCE.getKeyRange(Bytes.toBytes("m"), true, Bytes.toBytes("p"), false), + }}, + new int[] {0}, + KeyRange.getKeyRange(ByteUtil.concat(new byte[]{1}, Bytes.toBytes("f")), ByteUtil.concat(new byte[]{1}, Bytes.toBytes("g"))), + true, + false)); + return testCases; + } + + private static Collection<?> foreach(ScanRanges ranges, int[] widths, KeyRange keyRange, + boolean expectedResult) { + List<Object> ret = Lists.newArrayList(); + ret.add(new Object[] {ranges, widths, keyRange, expectedResult}); + return ret; + } + + private static Collection<?> foreach(KeyRange[][] ranges, int[] widths, KeyRange keyRange, boolean useSkipScan, + boolean expectedResult) { + List<List<KeyRange>> slots = Lists.transform(Lists.newArrayList(ranges), ARRAY_TO_LIST); + slots = new ArrayList<>(slots); + slots.add(0, Collections.singletonList(KeyRange.getKeyRange(new byte[]{0}))); + RowKeySchemaBuilder builder = new RowKeySchemaBuilder(10); + builder.addField(SaltingUtil.SALTING_COLUMN, false, SortOrder.getDefault()); + for (final int width : widths) { + if (width > 0) { + builder.addField(new PDatum() { + @Override + public boolean isNullable() { + return false; + } + @Override + public PDataType getDataType() { + return PChar.INSTANCE; + } + @Override + public Integer getMaxLength() { + return width; + } + @Override + public Integer getScale() { + return null; + } + @Override + public SortOrder getSortOrder() { + return SortOrder.getDefault(); + } + }, false, SortOrder.getDefault()); + } else { + builder.addField(new PDatum() { + @Override + public boolean isNullable() { + return false; + } + @Override + public PDataType getDataType() { + return PVarchar.INSTANCE; + } + @Override + public Integer getMaxLength() { + return width; + } + @Override + public Integer getScale() { + return null; + } + @Override + public SortOrder getSortOrder() { + return SortOrder.getDefault(); + } + }, false, SortOrder.getDefault()); + } + } + ScanRanges scanRanges = ScanRanges.createSingleSpan(builder.build(), slots, nBuckets , useSkipScan); + return foreach(scanRanges, widths, keyRange, expectedResult); + } + + private static final Function<KeyRange[], List<KeyRange>> ARRAY_TO_LIST = + new Function<KeyRange[], List<KeyRange>>() { + @Override + public List<KeyRange> apply(KeyRange[] input) { + return Lists.newArrayList(input); + } + }; +} http://git-wip-us.apache.org/repos/asf/phoenix/blob/0b39a520/phoenix-core/src/test/java/org/apache/phoenix/compile/ScanRangesTest.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/test/java/org/apache/phoenix/compile/ScanRangesTest.java b/phoenix-core/src/test/java/org/apache/phoenix/compile/ScanRangesTest.java index 0292244..44aac4d 100644 --- a/phoenix-core/src/test/java/org/apache/phoenix/compile/ScanRangesTest.java +++ b/phoenix-core/src/test/java/org/apache/phoenix/compile/ScanRangesTest.java @@ -24,12 +24,12 @@ import java.util.List; import org.apache.hadoop.hbase.util.Bytes; import org.apache.phoenix.query.KeyRange; -import org.apache.phoenix.schema.types.PChar; -import org.apache.phoenix.schema.types.PDataType; import org.apache.phoenix.schema.PDatum; -import org.apache.phoenix.schema.types.PVarchar; import org.apache.phoenix.schema.RowKeySchema.RowKeySchemaBuilder; 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.apache.phoenix.util.ByteUtil; import org.junit.Test; import org.junit.runner.RunWith; @@ -73,7 +73,7 @@ public class ScanRangesTest { // incrementing the key too much. upperExclusiveKey = ByteUtil.nextKey(upperExclusiveKey); } - assertEquals(expectedResult, scanRanges.intersects(lowerInclusiveKey,upperExclusiveKey,0, true)); + assertEquals(expectedResult, scanRanges.intersectRegion(lowerInclusiveKey,upperExclusiveKey,false)); } @Parameters(name="{0} {2}") http://git-wip-us.apache.org/repos/asf/phoenix/blob/0b39a520/phoenix-core/src/test/java/org/apache/phoenix/query/QueryPlanTest.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/test/java/org/apache/phoenix/query/QueryPlanTest.java b/phoenix-core/src/test/java/org/apache/phoenix/query/QueryPlanTest.java index 8af832b..37010b3 100644 --- a/phoenix-core/src/test/java/org/apache/phoenix/query/QueryPlanTest.java +++ b/phoenix-core/src/test/java/org/apache/phoenix/query/QueryPlanTest.java @@ -245,7 +245,7 @@ public class QueryPlanTest extends BaseConnectionlessQueryTest { ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + query); String queryPlan = QueryUtil.getExplainPlan(rs); assertEquals( - "CLIENT PARALLEL 20-WAY RANGE SCAN OVER FOO [0,'a',~'2016-01-28 23:59:59.999'] - [0,'a',~'2016-01-28 00:00:00.000']\n" + + "CLIENT PARALLEL 20-WAY RANGE SCAN OVER FOO [0,'a',~'2016-01-28 23:59:59.999'] - [19,'a',~'2016-01-28 00:00:00.000']\n" + " SERVER FILTER BY FIRST KEY ONLY\n" + "CLIENT MERGE SORT", queryPlan); } finally { @@ -271,7 +271,7 @@ public class QueryPlanTest extends BaseConnectionlessQueryTest { ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + query); String queryPlan = QueryUtil.getExplainPlan(rs); assertEquals( - "CLIENT PARALLEL 20-WAY ROUND ROBIN RANGE SCAN OVER " + tableName + " [0,'a',~'2016-01-28 23:59:59.999'] - [0,'a',~'2016-01-28 00:00:00.000']\n" + + "CLIENT PARALLEL 20-WAY ROUND ROBIN RANGE SCAN OVER " + tableName + " [0,'a',~'2016-01-28 23:59:59.999'] - [19,'a',~'2016-01-28 00:00:00.000']\n" + " SERVER FILTER BY FIRST KEY ONLY", queryPlan); } finally { conn.close();
