/** * Fill both upper and lower range of keyRange to keyLength bytes. * If the upper bound is inclusive, it must be filled such that an * intersection with a longer key would still match if the shorter * length matches. For example: (*,00C] intersected with [00Caaa,00Caaa] * should still return [00Caaa,00Caaa] since the 00C matches and is * inclusive. * @param keyLength * @return the newly filled KeyRange */ public KeyRange fill(int keyLength) { byte[] lowerRange = this.getLowerRange(); byte[] newLowerRange = lowerRange; if (!this.lowerUnbound()) { // If lower range is inclusive, fill with 0x00 since conceptually these bytes are included in the range newLowerRange = ByteUtil.fillKey(lowerRange, keyLength); } byte[] upperRange = this.getUpperRange(); byte[] newUpperRange = upperRange; if (!this.upperUnbound()) { // If upper range is inclusive, fill with 0xFF since conceptually these bytes are included in the range newUpperRange = ByteUtil.fillKey(upperRange, keyLength); } if (newLowerRange != lowerRange || newUpperRange != upperRange) { return KeyRange.getKeyRange(newLowerRange, this.isLowerInclusive(), newUpperRange, this.isUpperInclusive()); } return this; }
@Test public void testMultiRVCExpressionsCombinedUsingLiteralExpressions() throws SQLException { String lowerTenantId = "000000000000001"; String lowerParentId = "000000000000002"; Date lowerCreatedDate = new Date(System.currentTimeMillis()); String query = "select * from entity_history where (organization_id, parent_id, created_date) >= (?, ?, ?) AND (organization_id, parent_id) <= ('7', '7')"; Scan scan = new Scan(); List<Object> binds = Arrays.<Object>asList(lowerTenantId, lowerParentId, lowerCreatedDate); HashSet<Expression> extractedFilters = new HashSet<Expression>(); compileStatement(query, scan, binds, extractedFilters); assertTrue(extractedFilters.size() == 2); byte[] expectedStartRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(lowerTenantId), PDataType.VARCHAR.toBytes(lowerParentId), PDataType.DATE.toBytes(lowerCreatedDate)); byte[] expectedStopRow = ByteUtil.nextKey(ByteUtil.concat(ByteUtil.fillKey(PDataType.VARCHAR.toBytes("7"),15), ByteUtil.fillKey(PDataType.VARCHAR.toBytes("7"), 15))); assertArrayEquals(expectedStartRow, scan.getStartRow()); assertArrayEquals(expectedStopRow, scan.getStopRow()); }
@Test public void testOrSameColRangeExpression() throws SQLException { String query = "select * from atable where substr(organization_id,1,3) = ? or organization_id LIKE 'foo%'"; Scan scan = new Scan(); List<Object> binds = Arrays.<Object>asList("00D"); Set<Expression>extractedNodes = new HashSet<Expression>(); StatementContext context = compileStatement(query, scan, binds, extractedNodes); Filter filter = scan.getFilter(); assertNotNull(filter); assertTrue(filter instanceof SkipScanFilter); ScanRanges scanRanges = context.getScanRanges(); assertNotNull(scanRanges); List<List<KeyRange>> ranges = scanRanges.getRanges(); assertEquals(1,ranges.size()); List<List<KeyRange>> expectedRanges = Collections.singletonList(Arrays.asList( PDataType.CHAR.getKeyRange( ByteUtil.fillKey(PDataType.CHAR.toBytes("00D"),15), true, ByteUtil.fillKey(ByteUtil.nextKey(PDataType.CHAR.toBytes("00D")),15), false), PDataType.CHAR.getKeyRange( ByteUtil.fillKey(PDataType.CHAR.toBytes("foo"),15), true, ByteUtil.fillKey(ByteUtil.nextKey(PDataType.CHAR.toBytes("foo")),15), false))); assertEquals(expectedRanges, ranges); assertEquals(1, extractedNodes.size()); assertTrue(extractedNodes.iterator().next() instanceof OrExpression); }
@Override public KeyRange getKeyRange(CompareOp op, Expression rhs) { ImmutableBytesWritable ptr = new ImmutableBytesWritable(); rhs.evaluate(null, ptr); byte[] key = ByteUtil.copyKeyBytesIfNecessary(ptr); // If the column is fixed width, fill is up to it's byte size PDataType type = getColumn().getDataType(); if (type.isFixedWidth()) { Integer length = getColumn().getByteSize(); if (length != null) { key = ByteUtil.fillKey(key, length); } } return ByteUtil.getKeyRange(key, op, type); }
@Test public void testMultiKeyBindExpression() throws SQLException { String tenantId = "000000000000001"; String keyPrefix = "002"; String query = "select * from atable where organization_id=? and substr(entity_id,1,3)=?"; Scan scan = new Scan(); List<Object> binds = Arrays.<Object>asList(tenantId,keyPrefix); compileStatement(query, scan, binds); byte[] startRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(tenantId),ByteUtil.fillKey(PDataType.VARCHAR.toBytes(keyPrefix),15)); assertArrayEquals(startRow, scan.getStartRow()); byte[] stopRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(tenantId),ByteUtil.fillKey(ByteUtil.nextKey(PDataType.VARCHAR.toBytes(keyPrefix)),15)); assertArrayEquals(stopRow, scan.getStopRow()); }
@Test public void testForceRangeScanKeepsFilters() throws SQLException { ensureTableCreated(getUrl(), TestUtil.ENTITY_HISTORY_TABLE_NAME); String tenantId = "000000000000001"; String keyPrefix = "002"; String query = "select /*+ RANGE_SCAN */ ORGANIZATION_ID, PARENT_ID, CREATED_DATE, ENTITY_HISTORY_ID from " + TestUtil.ENTITY_HISTORY_TABLE_NAME + " where ORGANIZATION_ID=? and SUBSTR(PARENT_ID, 1, 3) = ? and CREATED_DATE >= ? and CREATED_DATE < ? order by ORGANIZATION_ID, PARENT_ID, CREATED_DATE, ENTITY_HISTORY_ID limit 6"; Scan scan = new Scan(); Date startTime = new Date(System.currentTimeMillis()); Date stopTime = new Date(startTime.getTime() + TestUtil.MILLIS_IN_DAY); List<Object> binds = Arrays.<Object>asList(tenantId, keyPrefix, startTime, stopTime); Set<Expression> extractedNodes = Sets.newHashSet(); compileStatement(query, scan, binds, 6, extractedNodes); assertEquals(2, extractedNodes.size()); assertNotNull(scan.getFilter()); byte[] expectedStartRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(tenantId), ByteUtil.fillKey(PDataType.VARCHAR.toBytes(keyPrefix),15), PDataType.DATE.toBytes(startTime)); assertArrayEquals(expectedStartRow, scan.getStartRow()); byte[] expectedStopRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(tenantId), ByteUtil.fillKey(ByteUtil.nextKey(PDataType.VARCHAR.toBytes(keyPrefix)),15), PDataType.DATE.toBytes(stopTime)); assertArrayEquals(expectedStopRow, scan.getStopRow()); }
@Test public void testLikeOptKeyExpression() throws SQLException { String tenantId = "000000000000001"; String keyPrefix = "002"; String query = "select * from atable where organization_id = ? and entity_id LIKE '" + keyPrefix + "%003%'"; Scan scan = new Scan(); List<Object> binds = Arrays.<Object>asList(tenantId); Set<Expression>extractedNodes = new HashSet<Expression>(); compileStatement(query, scan, binds, extractedNodes); assertNotNull(scan.getFilter()); assertEquals(1, extractedNodes.size()); byte[] startRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(tenantId),ByteUtil.fillKey(PDataType.VARCHAR.toBytes(keyPrefix),15)); assertArrayEquals(startRow, scan.getStartRow()); byte[] stopRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(tenantId),ByteUtil.fillKey(ByteUtil.nextKey(PDataType.VARCHAR.toBytes(keyPrefix)),15)); assertArrayEquals(stopRow, scan.getStopRow()); }
@Test public void testLikeOptKeyExpression2() throws SQLException { String tenantId = "000000000000001"; String keyPrefix = "002"; String query = "select * from atable where organization_id = ? and substr(entity_id,1,10) LIKE '" + keyPrefix + "%003%'"; Scan scan = new Scan(); List<Object> binds = Arrays.<Object>asList(tenantId); Set<Expression>extractedNodes = new HashSet<Expression>(); compileStatement(query, scan, binds, extractedNodes); assertNotNull(scan.getFilter()); assertEquals(1, extractedNodes.size()); byte[] startRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(tenantId),ByteUtil.fillKey(PDataType.VARCHAR.toBytes(keyPrefix),15)); byte[] stopRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(tenantId),ByteUtil.fillKey(ByteUtil.nextKey(PDataType.VARCHAR.toBytes(keyPrefix)),15)); assertArrayEquals(startRow, scan.getStartRow()); assertArrayEquals(stopRow, scan.getStopRow()); }
@Test public void testTrailingSubstrExpression() throws SQLException { String tenantId = "0xD000000000001"; String entityId = "002333333333333"; String query = "select * from atable where substr(organization_id,1,3)='" + tenantId.substring(0, 3) + "' and entity_id='" + entityId + "'"; Scan scan = new Scan(); List<Object> binds = Collections.emptyList(); compileStatement(query, scan, binds); assertNotNull(scan.getFilter()); byte[] startRow = ByteUtil.concat(ByteUtil.fillKey(PDataType.VARCHAR.toBytes(tenantId.substring(0,3)),15),PDataType.VARCHAR.toBytes(entityId)); assertArrayEquals(startRow, scan.getStartRow()); // Even though the first slot is a non inclusive range, we need to do a next key // on the second slot because of the algorithm we use to seek to and terminate the // loop during skip scan. We could end up having a first slot just under the upper // limit of slot one and a value equal to the value in slot two and we need this to // be less than the upper range that would get formed. byte[] stopRow = ByteUtil.concat(ByteUtil.fillKey(ByteUtil.nextKey(PDataType.VARCHAR.toBytes(tenantId.substring(0,3))),15),ByteUtil.nextKey(PDataType.VARCHAR.toBytes(entityId))); assertArrayEquals(stopRow, scan.getStopRow()); }
@Test public void testKeyRangeExpression9() throws SQLException { String tenantId = "000000000000001"; String keyPrefix1 = "002"; String keyPrefix2 = "0033"; String query = "select * from atable where organization_id='" + tenantId + "' and substr(entity_id,1,3) >= '" + keyPrefix1 + "' and substr(entity_id,1,4) <= '" + keyPrefix2 + "'"; Scan scan = new Scan(); List<Object> binds = Collections.emptyList(); compileStatement(query, scan, binds); assertNull(scan.getFilter()); byte[] startRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(tenantId),ByteUtil.fillKey(PDataType.VARCHAR.toBytes(keyPrefix1),15)); // extra byte is due to implicit internal padding assertArrayEquals(startRow, scan.getStartRow()); byte[] stopRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(tenantId),ByteUtil.fillKey(ByteUtil.nextKey(PDataType.VARCHAR.toBytes(keyPrefix2)),15)); assertArrayEquals(stopRow, scan.getStopRow()); }
@Test public void testMultiKeyExpression() throws SQLException { String tenantId = "000000000000001"; String keyPrefix = "002"; String query = "select * from atable where organization_id='" + tenantId + "' and substr(entity_id,1,3)='" + keyPrefix + "'"; Scan scan = new Scan(); List<Object> binds = Collections.emptyList(); compileStatement(query, scan, binds); assertNull(scan.getFilter()); byte[] startRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(tenantId),ByteUtil.fillKey(PDataType.VARCHAR.toBytes(keyPrefix),15)); assertArrayEquals(startRow, scan.getStartRow()); byte[] stopRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(tenantId),ByteUtil.fillKey(ByteUtil.nextKey(PDataType.VARCHAR.toBytes(keyPrefix)), 15)); assertArrayEquals(stopRow, scan.getStopRow()); }
@Test public void testLikeExtractAllAsEqKeyExpression() throws SQLException { String tenantId = "000000000000001"; String keyPrefix = "002"; String query = "select * from atable where organization_id LIKE ? and entity_id LIKE '" + keyPrefix + "%'"; Scan scan = new Scan(); List<Object> binds = Arrays.<Object>asList(tenantId); compileStatement(query, scan, binds); assertNull(scan.getFilter()); byte[] startRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(tenantId),ByteUtil.fillKey(PDataType.VARCHAR.toBytes(keyPrefix),15)); assertArrayEquals(startRow, scan.getStartRow()); byte[] stopRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(tenantId),ByteUtil.fillKey(ByteUtil.nextKey(PDataType.VARCHAR.toBytes(keyPrefix)),15)); assertArrayEquals(stopRow, scan.getStopRow()); }
@Test public void testKeyRangeExpression1() throws SQLException { String tenantId = "000000000000001"; String keyPrefix1 = "002"; String keyPrefix2= "004"; String query = "select * from atable where organization_id='" + tenantId + "' and substr(entity_id,1,3) >= '" + keyPrefix1 + "' and substr(entity_id,1,3) < '" + keyPrefix2 + "'"; Scan scan = new Scan(); List<Object> binds = Collections.emptyList(); compileStatement(query, scan, binds); assertNull(scan.getFilter()); byte[] startRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(tenantId),ByteUtil.fillKey(PDataType.VARCHAR.toBytes(keyPrefix1),15)); assertArrayEquals(startRow, scan.getStartRow()); byte[] stopRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(tenantId),ByteUtil.fillKey(PDataType.VARCHAR.toBytes(keyPrefix2),15)); assertArrayEquals(stopRow, scan.getStopRow()); }
@Test public void testLikeExtractAllKeyExpression() throws SQLException { String tenantId = "000000000000001"; String keyPrefix = "002"; String query = "select * from atable where organization_id = ? and entity_id LIKE '" + keyPrefix + "%'"; Scan scan = new Scan(); List<Object> binds = Arrays.<Object>asList(tenantId); compileStatement(query, scan, binds); assertNull(scan.getFilter()); byte[] startRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(tenantId),ByteUtil.fillKey(PDataType.VARCHAR.toBytes(keyPrefix),15)); assertArrayEquals(startRow, scan.getStartRow()); byte[] stopRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(tenantId),ByteUtil.fillKey(ByteUtil.nextKey(PDataType.VARCHAR.toBytes(keyPrefix)),15)); assertArrayEquals(stopRow, scan.getStopRow()); }
@Test public void testKeyRangeExpression2() throws SQLException { String tenantId = "000000000000001"; String keyPrefix1 = "002"; String keyPrefix2= "004"; String query = "select * from atable where organization_id='" + tenantId + "' and substr(entity_id,1,3) >= '" + keyPrefix1 + "' and substr(entity_id,1,3) <= '" + keyPrefix2 + "'"; Scan scan = new Scan(); List<Object> binds = Collections.emptyList(); compileStatement(query, scan, binds); assertNull(scan.getFilter()); byte[] startRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(tenantId),ByteUtil.fillKey(PDataType.VARCHAR.toBytes(keyPrefix1),15)); assertArrayEquals(startRow, scan.getStartRow()); byte[] stopRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(tenantId),ByteUtil.fillKey(ByteUtil.nextKey(PDataType.VARCHAR.toBytes(keyPrefix2)),15)); assertArrayEquals(stopRow, scan.getStopRow()); }
@Test public void testLikeExtractKeyExpression2() throws SQLException { String tenantId = "000000000000001"; String keyPrefix = "002"; // TODO: verify that _ at end of like doesn't go to equals String query = "select * from atable where organization_id = ? and entity_id LIKE '" + keyPrefix + "_'"; Scan scan = new Scan(); List<Object> binds = Arrays.<Object>asList(tenantId); compileStatement(query, scan, binds); assertNotNull(scan.getFilter()); byte[] startRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(tenantId),ByteUtil.fillKey(PDataType.VARCHAR.toBytes(keyPrefix),15)); assertArrayEquals(startRow, scan.getStartRow()); byte[] stopRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(tenantId),ByteUtil.fillKey(ByteUtil.nextKey(PDataType.VARCHAR.toBytes(keyPrefix)),15)); assertArrayEquals(stopRow, scan.getStopRow()); }
PDataType.CHAR.getKeyRange(Bytes.toBytes("A"), true, Bytes.toBytes("A"), true),}}, new int[] {1,1,1}, ByteUtil.fillKey(PDataType.VARCHAR.toBytes("a1A"), 3), Bound.LOWER )); PDataType.CHAR.getKeyRange(Bytes.toBytes("A"), true, Bytes.toBytes("B"), true),}}, new int[] {1,1,1}, ByteUtil.fillKey(PDataType.VARCHAR.toBytes("a1A"), 3), Bound.LOWER )); PDataType.CHAR.getKeyRange(Bytes.toBytes("A"), true, Bytes.toBytes("A"), true),}}, new int[] {1,1,1}, ByteUtil.fillKey(PDataType.VARCHAR.toBytes("a1A"), 3), Bound.LOWER )); PDataType.CHAR.getKeyRange(Bytes.toBytes("A"), false, Bytes.toBytes("B"), true),}}, new int[] {1,1,1}, ByteUtil.fillKey(PDataType.VARCHAR.toBytes("b2B"), 3), Bound.LOWER )); PDataType.CHAR.getKeyRange(Bytes.toBytes("A"), false, Bytes.toBytes("B"), true),}}, new int[] {1,1,1}, ByteUtil.fillKey(PDataType.VARCHAR.toBytes("b1B"), 3), Bound.LOWER ));
@Test public void testKeyRangeExpression3() throws SQLException { String tenantId = "000000000000001"; String keyPrefix1 = "002"; String keyPrefix2= "004"; String query = "select * from atable where organization_id='" + tenantId + "' and substr(entity_id,1,3) > '" + keyPrefix1 + "' and substr(entity_id,1,3) <= '" + keyPrefix2 + "'"; Scan scan = new Scan(); List<Object> binds = Collections.emptyList(); compileStatement(query, scan, binds); assertNull(scan.getFilter()); byte[] startRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(tenantId),ByteUtil.fillKey(ByteUtil.nextKey(PDataType.VARCHAR.toBytes(keyPrefix1)),15)); assertArrayEquals(startRow, scan.getStartRow()); byte[] stopRow = ByteUtil.concat(PDataType.VARCHAR.toBytes(tenantId),ByteUtil.fillKey(ByteUtil.nextKey(PDataType.VARCHAR.toBytes(keyPrefix2)),15)); assertArrayEquals(stopRow, scan.getStopRow()); }