001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.filter; 019 020import static org.junit.jupiter.api.Assertions.assertEquals; 021import static org.junit.jupiter.api.Assertions.assertThrows; 022import static org.junit.jupiter.api.Assertions.assertTrue; 023 024import java.io.IOException; 025import java.util.ArrayList; 026import java.util.Arrays; 027import java.util.List; 028import org.apache.hadoop.hbase.Cell; 029import org.apache.hadoop.hbase.HBaseTestingUtil; 030import org.apache.hadoop.hbase.HConstants; 031import org.apache.hadoop.hbase.KeyValueUtil; 032import org.apache.hadoop.hbase.TableName; 033import org.apache.hadoop.hbase.client.Put; 034import org.apache.hadoop.hbase.client.Result; 035import org.apache.hadoop.hbase.client.ResultScanner; 036import org.apache.hadoop.hbase.client.Scan; 037import org.apache.hadoop.hbase.client.Table; 038import org.apache.hadoop.hbase.filter.MultiRowRangeFilter.RowRange; 039import org.apache.hadoop.hbase.testclassification.LargeTests; 040import org.apache.hadoop.hbase.util.Bytes; 041import org.junit.jupiter.api.AfterAll; 042import org.junit.jupiter.api.BeforeAll; 043import org.junit.jupiter.api.Tag; 044import org.junit.jupiter.api.Test; 045import org.junit.jupiter.api.TestInfo; 046import org.slf4j.Logger; 047import org.slf4j.LoggerFactory; 048 049@Tag(LargeTests.TAG) 050public class TestMultiRowRangeFilter { 051 052 private final static HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil(); 053 private static final Logger LOG = LoggerFactory.getLogger(TestMultiRowRangeFilter.class); 054 private byte[] family = Bytes.toBytes("family"); 055 private byte[] qf = Bytes.toBytes("qf"); 056 private byte[] value = Bytes.toBytes("val"); 057 private TableName tableName; 058 private int numRows = 100; 059 060 @BeforeAll 061 public static void setUpBeforeClass() throws Exception { 062 TEST_UTIL.startMiniCluster(); 063 } 064 065 @AfterAll 066 public static void tearDownAfterClass() throws Exception { 067 TEST_UTIL.shutdownMiniCluster(); 068 } 069 070 @Test 071 public void testRowKeyPrefixWithEmptyPrefix() throws IOException { 072 byte[] prefix = {}; 073 byte[][] rowKeyPrefixes = new byte[1][]; 074 rowKeyPrefixes[0] = prefix; 075 MultiRowRangeFilter filter = new MultiRowRangeFilter(rowKeyPrefixes); 076 List<RowRange> actualRanges = filter.getRowRanges(); 077 List<RowRange> expectedRanges = new ArrayList<>(); 078 expectedRanges 079 .add(new RowRange(HConstants.EMPTY_START_ROW, true, HConstants.EMPTY_END_ROW, false)); 080 assertRangesEqual(expectedRanges, actualRanges); 081 } 082 083 @Test 084 public void testRowKeyPrefixWithLastIncrementablePrefix() throws IOException { 085 byte[] prefix = { (byte) 0x12, (byte) 0x23, (byte) 0xFF, (byte) 0xFE }; 086 byte[][] rowKeyPrefixes = new byte[1][]; 087 rowKeyPrefixes[0] = prefix; 088 MultiRowRangeFilter filter = new MultiRowRangeFilter(rowKeyPrefixes); 089 List<RowRange> actualRanges = filter.getRowRanges(); 090 List<RowRange> expectedRanges = new ArrayList<>(); 091 final byte[] expectedStop = { (byte) 0x12, (byte) 0x23, (byte) 0xFF, (byte) 0xFF }; 092 expectedRanges.add(new RowRange(prefix, true, expectedStop, false)); 093 assertRangesEqual(expectedRanges, actualRanges); 094 } 095 096 @Test 097 public void testRowKeyPrefixWithoutLastIncrementablePrefix() throws IOException { 098 byte[] prefix = { (byte) 0x12, (byte) 0x23, (byte) 0xFF, (byte) 0xFF }; 099 byte[][] rowKeyPrefixes = new byte[1][]; 100 rowKeyPrefixes[0] = prefix; 101 MultiRowRangeFilter filter = new MultiRowRangeFilter(rowKeyPrefixes); 102 List<RowRange> actualRanges = filter.getRowRanges(); 103 List<RowRange> expectedRanges = new ArrayList<>(); 104 final byte[] expectedStop = { (byte) 0x12, (byte) 0x24 }; 105 expectedRanges.add(new RowRange(prefix, true, expectedStop, false)); 106 assertRangesEqual(expectedRanges, actualRanges); 107 } 108 109 @Test 110 public void testRowKeyPrefixWithMergablePrefix() throws IOException { 111 byte[] prefix1 = { (byte) 0x12, (byte) 0x23, (byte) 0xFF, (byte) 0xFE }; 112 byte[] prefix2 = { (byte) 0x12, (byte) 0x23, (byte) 0xFF, (byte) 0xFF }; 113 byte[][] rowKeyPrefixes = new byte[2][]; 114 rowKeyPrefixes[0] = prefix1; 115 rowKeyPrefixes[1] = prefix2; 116 MultiRowRangeFilter filter = new MultiRowRangeFilter(rowKeyPrefixes); 117 List<RowRange> actualRanges = filter.getRowRanges(); 118 List<RowRange> expectedRanges = new ArrayList<>(); 119 final byte[] expectedStop = { (byte) 0x12, (byte) 0x24 }; 120 expectedRanges.add(new RowRange(prefix1, true, expectedStop, false)); 121 assertRangesEqual(expectedRanges, actualRanges); 122 } 123 124 @Test 125 public void testRanges() throws IOException { 126 byte[] key1Start = new byte[] { -3 }; 127 byte[] key1End = new byte[] { -2 }; 128 129 byte[] key2Start = new byte[] { 5 }; 130 byte[] key2End = new byte[] { 6 }; 131 132 byte[] badKey = new byte[] { -10 }; 133 134 MultiRowRangeFilter filter = new MultiRowRangeFilter( 135 Arrays.asList(new MultiRowRangeFilter.RowRange(key1Start, true, key1End, false), 136 new MultiRowRangeFilter.RowRange(key2Start, true, key2End, false))); 137 filter.filterRowKey(KeyValueUtil.createFirstOnRow(badKey)); 138 /* 139 * FAILS -- includes BAD key! Expected :SEEK_NEXT_USING_HINT Actual :INCLUDE 140 */ 141 assertEquals(Filter.ReturnCode.SEEK_NEXT_USING_HINT, filter.filterCell(null)); 142 } 143 144 @Test 145 public void testOutOfOrderScannerNextException() throws Exception { 146 MultiRowRangeFilter filter = new MultiRowRangeFilter(Arrays.asList( 147 new MultiRowRangeFilter.RowRange(Bytes.toBytes("b"), true, Bytes.toBytes("c"), true), 148 new MultiRowRangeFilter.RowRange(Bytes.toBytes("d"), true, Bytes.toBytes("e"), true))); 149 filter.filterRowKey(KeyValueUtil.createFirstOnRow(Bytes.toBytes("a"))); 150 assertEquals(Filter.ReturnCode.SEEK_NEXT_USING_HINT, filter.filterCell(null)); 151 filter.filterRowKey(KeyValueUtil.createFirstOnRow(Bytes.toBytes("b"))); 152 assertEquals(Filter.ReturnCode.INCLUDE, filter.filterCell(null)); 153 filter.filterRowKey(KeyValueUtil.createFirstOnRow(Bytes.toBytes("c"))); 154 assertEquals(Filter.ReturnCode.INCLUDE, filter.filterCell(null)); 155 filter.filterRowKey(KeyValueUtil.createFirstOnRow(Bytes.toBytes("d"))); 156 assertEquals(Filter.ReturnCode.INCLUDE, filter.filterCell(null)); 157 filter.filterRowKey(KeyValueUtil.createFirstOnRow(Bytes.toBytes("e"))); 158 assertEquals(Filter.ReturnCode.INCLUDE, filter.filterCell(null)); 159 } 160 161 @Test 162 public void testMergeAndSortWithEmptyStartRow() throws IOException { 163 List<RowRange> ranges = new ArrayList<>(); 164 ranges.add(new RowRange(Bytes.toBytes(""), true, Bytes.toBytes(20), false)); 165 ranges.add(new RowRange(Bytes.toBytes(15), true, Bytes.toBytes(40), false)); 166 List<RowRange> actualRanges = MultiRowRangeFilter.sortAndMerge(ranges); 167 List<RowRange> expectedRanges = new ArrayList<>(); 168 expectedRanges.add(new RowRange(Bytes.toBytes(""), true, Bytes.toBytes(40), false)); 169 assertRangesEqual(expectedRanges, actualRanges); 170 } 171 172 @Test 173 public void testMergeAndSortWithEmptyStopRow() throws IOException { 174 List<RowRange> ranges = new ArrayList<>(); 175 ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false)); 176 ranges.add(new RowRange(Bytes.toBytes(15), true, Bytes.toBytes(""), false)); 177 ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(70), false)); 178 List<RowRange> actualRanges = MultiRowRangeFilter.sortAndMerge(ranges); 179 List<RowRange> expectedRanges = new ArrayList<>(); 180 expectedRanges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(""), false)); 181 assertRangesEqual(expectedRanges, actualRanges); 182 } 183 184 @Test 185 public void testMergeAndSortWithEmptyStartRowAndStopRow() throws IOException { 186 List<RowRange> ranges = new ArrayList<>(); 187 ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false)); 188 ranges.add(new RowRange(Bytes.toBytes(""), true, Bytes.toBytes(""), false)); 189 ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(70), false)); 190 List<RowRange> actualRanges = MultiRowRangeFilter.sortAndMerge(ranges); 191 List<RowRange> expectedRanges = new ArrayList<>(); 192 expectedRanges.add(new RowRange(Bytes.toBytes(""), true, Bytes.toBytes(""), false)); 193 assertRangesEqual(expectedRanges, actualRanges); 194 } 195 196 @Test 197 public void testMultiRowRangeWithoutRange() throws IOException { 198 List<RowRange> ranges = new ArrayList<>(); 199 assertThrows(IllegalArgumentException.class, () -> { 200 new MultiRowRangeFilter(ranges); 201 }); 202 } 203 204 @Test 205 public void testMultiRowRangeWithInvalidRange() throws IOException { 206 List<RowRange> ranges = new ArrayList<>(); 207 ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false)); 208 // the start row larger than the stop row 209 ranges.add(new RowRange(Bytes.toBytes(80), true, Bytes.toBytes(20), false)); 210 ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(70), false)); 211 assertThrows(IllegalArgumentException.class, () -> { 212 new MultiRowRangeFilter(ranges); 213 }); 214 } 215 216 @Test 217 public void testMergeAndSortWithoutOverlap() throws IOException { 218 List<RowRange> ranges = new ArrayList<>(); 219 ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false)); 220 ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(40), false)); 221 ranges.add(new RowRange(Bytes.toBytes(60), true, Bytes.toBytes(70), false)); 222 List<RowRange> actualRanges = MultiRowRangeFilter.sortAndMerge(ranges); 223 List<RowRange> expectedRanges = new ArrayList<>(); 224 expectedRanges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false)); 225 expectedRanges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(40), false)); 226 expectedRanges.add(new RowRange(Bytes.toBytes(60), true, Bytes.toBytes(70), false)); 227 assertRangesEqual(expectedRanges, actualRanges); 228 } 229 230 @Test 231 public void testMergeAndSortWithOverlap() throws IOException { 232 List<RowRange> ranges = new ArrayList<>(); 233 ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false)); 234 ranges.add(new RowRange(Bytes.toBytes(15), true, Bytes.toBytes(40), false)); 235 ranges.add(new RowRange(Bytes.toBytes(20), true, Bytes.toBytes(30), false)); 236 ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(50), false)); 237 ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(70), false)); 238 ranges.add(new RowRange(Bytes.toBytes(90), true, Bytes.toBytes(100), false)); 239 ranges.add(new RowRange(Bytes.toBytes(95), true, Bytes.toBytes(100), false)); 240 List<RowRange> actualRanges = MultiRowRangeFilter.sortAndMerge(ranges); 241 List<RowRange> expectedRanges = new ArrayList<>(); 242 expectedRanges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(70), false)); 243 expectedRanges.add(new RowRange(Bytes.toBytes(90), true, Bytes.toBytes(100), false)); 244 assertRangesEqual(expectedRanges, actualRanges); 245 } 246 247 @Test 248 public void testMergeAndSortWithStartRowInclusive() throws IOException { 249 List<RowRange> ranges = new ArrayList<>(); 250 ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false)); 251 ranges.add(new RowRange(Bytes.toBytes(20), true, Bytes.toBytes(""), false)); 252 List<RowRange> actualRanges = MultiRowRangeFilter.sortAndMerge(ranges); 253 List<RowRange> expectedRanges = new ArrayList<>(); 254 expectedRanges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(""), false)); 255 assertRangesEqual(expectedRanges, actualRanges); 256 } 257 258 @Test 259 public void testMergeAndSortWithRowExclusive() throws IOException { 260 List<RowRange> ranges = new ArrayList<>(); 261 ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false)); 262 ranges.add(new RowRange(Bytes.toBytes(20), false, Bytes.toBytes(""), false)); 263 List<RowRange> actualRanges = MultiRowRangeFilter.sortAndMerge(ranges); 264 List<RowRange> expectedRanges = new ArrayList<>(); 265 expectedRanges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false)); 266 expectedRanges.add(new RowRange(Bytes.toBytes(20), false, Bytes.toBytes(""), false)); 267 assertRangesEqual(expectedRanges, actualRanges); 268 } 269 270 @Test 271 public void testMergeAndSortWithRowInclusive() throws IOException { 272 List<RowRange> ranges = new ArrayList<>(); 273 ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), true)); 274 ranges.add(new RowRange(Bytes.toBytes(20), false, Bytes.toBytes(""), false)); 275 List<RowRange> actualRanges = MultiRowRangeFilter.sortAndMerge(ranges); 276 List<RowRange> expectedRanges = new ArrayList<>(); 277 expectedRanges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(""), false)); 278 assertRangesEqual(expectedRanges, actualRanges); 279 } 280 281 public void assertRangesEqual(List<RowRange> expected, List<RowRange> actual) { 282 assertEquals(expected.size(), actual.size()); 283 for (int i = 0; i < expected.size(); i++) { 284 assertTrue(Bytes.equals(expected.get(i).getStartRow(), actual.get(i).getStartRow())); 285 assertTrue(expected.get(i).isStartRowInclusive() == actual.get(i).isStartRowInclusive()); 286 assertTrue(Bytes.equals(expected.get(i).getStopRow(), actual.get(i).getStopRow())); 287 assertTrue(expected.get(i).isStopRowInclusive() == actual.get(i).isStopRowInclusive()); 288 } 289 } 290 291 @Test 292 public void testMultiRowRangeFilterWithRangeOverlap(TestInfo testInfo) throws IOException { 293 tableName = TableName.valueOf(testInfo.getTestMethod().get().getName()); 294 Table ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE); 295 generateRows(numRows, ht, family, qf, value); 296 297 Scan scan = new Scan(); 298 scan.readAllVersions(); 299 300 List<RowRange> ranges = new ArrayList<>(); 301 ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false)); 302 ranges.add(new RowRange(Bytes.toBytes(15), true, Bytes.toBytes(40), false)); 303 ranges.add(new RowRange(Bytes.toBytes(65), true, Bytes.toBytes(75), false)); 304 ranges.add(new RowRange(Bytes.toBytes(60), true, null, false)); 305 ranges.add(new RowRange(Bytes.toBytes(60), true, Bytes.toBytes(80), false)); 306 307 MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges); 308 scan.setFilter(filter); 309 int resultsSize = getResultsSize(ht, scan); 310 LOG.info("found " + resultsSize + " results"); 311 List<Cell> results1 = getScanResult(Bytes.toBytes(10), Bytes.toBytes(40), ht); 312 List<Cell> results2 = getScanResult(Bytes.toBytes(60), Bytes.toBytes(""), ht); 313 314 assertEquals(results1.size() + results2.size(), resultsSize); 315 316 ht.close(); 317 } 318 319 @Test 320 public void testMultiRowRangeFilterWithoutRangeOverlap(TestInfo testInfo) throws IOException { 321 tableName = TableName.valueOf(testInfo.getTestMethod().get().getName()); 322 Table ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE); 323 generateRows(numRows, ht, family, qf, value); 324 325 Scan scan = new Scan(); 326 scan.readAllVersions(); 327 328 List<RowRange> ranges = new ArrayList<>(); 329 ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(40), false)); 330 ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false)); 331 ranges.add(new RowRange(Bytes.toBytes(60), true, Bytes.toBytes(70), false)); 332 333 MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges); 334 scan.setFilter(filter); 335 int resultsSize = getResultsSize(ht, scan); 336 LOG.info("found " + resultsSize + " results"); 337 List<Cell> results1 = getScanResult(Bytes.toBytes(10), Bytes.toBytes(20), ht); 338 List<Cell> results2 = getScanResult(Bytes.toBytes(30), Bytes.toBytes(40), ht); 339 List<Cell> results3 = getScanResult(Bytes.toBytes(60), Bytes.toBytes(70), ht); 340 341 assertEquals(results1.size() + results2.size() + results3.size(), resultsSize); 342 343 ht.close(); 344 } 345 346 @Test 347 public void testMultiRowRangeFilterWithEmptyStartRow(TestInfo testInfo) throws IOException { 348 tableName = TableName.valueOf(testInfo.getTestMethod().get().getName()); 349 Table ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE); 350 generateRows(numRows, ht, family, qf, value); 351 Scan scan = new Scan(); 352 scan.readAllVersions(); 353 354 List<RowRange> ranges = new ArrayList<>(); 355 ranges.add(new RowRange(Bytes.toBytes(""), true, Bytes.toBytes(10), false)); 356 ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(40), false)); 357 358 MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges); 359 scan.setFilter(filter); 360 int resultsSize = getResultsSize(ht, scan); 361 List<Cell> results1 = getScanResult(Bytes.toBytes(""), Bytes.toBytes(10), ht); 362 List<Cell> results2 = getScanResult(Bytes.toBytes(30), Bytes.toBytes(40), ht); 363 assertEquals(results1.size() + results2.size(), resultsSize); 364 365 ht.close(); 366 } 367 368 @Test 369 public void testMultiRowRangeFilterWithEmptyStopRow(TestInfo testInfo) throws IOException { 370 tableName = TableName.valueOf(testInfo.getTestMethod().get().getName()); 371 Table ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE); 372 generateRows(numRows, ht, family, qf, value); 373 Scan scan = new Scan(); 374 scan.readAllVersions(); 375 376 List<RowRange> ranges = new ArrayList<>(); 377 ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(""), false)); 378 ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(40), false)); 379 380 MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges); 381 scan.setFilter(filter); 382 int resultsSize = getResultsSize(ht, scan); 383 List<Cell> results1 = getScanResult(Bytes.toBytes(10), Bytes.toBytes(""), ht); 384 assertEquals(results1.size(), resultsSize); 385 386 ht.close(); 387 } 388 389 @Test 390 public void testMultiRowRangeFilterWithInclusive(TestInfo testInfo) throws IOException { 391 tableName = TableName.valueOf(testInfo.getTestMethod().get().getName()); 392 Table ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE); 393 generateRows(numRows, ht, family, qf, value); 394 395 Scan scan = new Scan(); 396 scan.readAllVersions(); 397 398 List<RowRange> ranges = new ArrayList<>(); 399 ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false)); 400 ranges.add(new RowRange(Bytes.toBytes(20), true, Bytes.toBytes(40), false)); 401 ranges.add(new RowRange(Bytes.toBytes(65), true, Bytes.toBytes(75), false)); 402 ranges.add(new RowRange(Bytes.toBytes(60), true, null, false)); 403 ranges.add(new RowRange(Bytes.toBytes(60), true, Bytes.toBytes(80), false)); 404 405 MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges); 406 scan.setFilter(filter); 407 int resultsSize = getResultsSize(ht, scan); 408 LOG.info("found " + resultsSize + " results"); 409 List<Cell> results1 = getScanResult(Bytes.toBytes(10), Bytes.toBytes(40), ht); 410 List<Cell> results2 = getScanResult(Bytes.toBytes(60), Bytes.toBytes(""), ht); 411 412 assertEquals(results1.size() + results2.size(), resultsSize); 413 414 ht.close(); 415 } 416 417 @Test 418 public void testMultiRowRangeFilterWithExclusive(TestInfo testInfo) throws IOException { 419 tableName = TableName.valueOf(testInfo.getTestMethod().get().getName()); 420 TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_CLIENT_SCANNER_TIMEOUT_PERIOD, 6000000); 421 TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE); 422 try (Table ht = TEST_UTIL.getConnection().getTableBuilder(tableName, null) 423 .setReadRpcTimeout(600000).setOperationTimeout(6000000).build()) { 424 generateRows(numRows, ht, family, qf, value); 425 426 Scan scan = new Scan(); 427 scan.readAllVersions(); 428 429 List<RowRange> ranges = new ArrayList<>(); 430 ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false)); 431 ranges.add(new RowRange(Bytes.toBytes(20), false, Bytes.toBytes(40), false)); 432 ranges.add(new RowRange(Bytes.toBytes(65), true, Bytes.toBytes(75), false)); 433 434 MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges); 435 scan.setFilter(filter); 436 int resultsSize = getResultsSize(ht, scan); 437 LOG.info("found " + resultsSize + " results"); 438 List<Cell> results1 = getScanResult(Bytes.toBytes(10), Bytes.toBytes(40), ht); 439 List<Cell> results2 = getScanResult(Bytes.toBytes(65), Bytes.toBytes(75), ht); 440 441 assertEquals((results1.size() - 1) + results2.size(), resultsSize); 442 } 443 } 444 445 @Test 446 public void testMultiRowRangeWithFilterListAndOperator(TestInfo testInfo) throws IOException { 447 tableName = TableName.valueOf(testInfo.getTestMethod().get().getName()); 448 Table ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE); 449 generateRows(numRows, ht, family, qf, value); 450 451 Scan scan = new Scan(); 452 scan.readAllVersions(); 453 454 List<RowRange> ranges1 = new ArrayList<>(); 455 ranges1.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false)); 456 ranges1.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(40), false)); 457 ranges1.add(new RowRange(Bytes.toBytes(60), true, Bytes.toBytes(70), false)); 458 459 MultiRowRangeFilter filter1 = new MultiRowRangeFilter(ranges1); 460 461 List<RowRange> ranges2 = new ArrayList<>(); 462 ranges2.add(new RowRange(Bytes.toBytes(20), true, Bytes.toBytes(40), false)); 463 ranges2.add(new RowRange(Bytes.toBytes(80), true, Bytes.toBytes(90), false)); 464 465 MultiRowRangeFilter filter2 = new MultiRowRangeFilter(ranges2); 466 467 FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL); 468 filterList.addFilter(filter1); 469 filterList.addFilter(filter2); 470 scan.setFilter(filterList); 471 int resultsSize = getResultsSize(ht, scan); 472 LOG.info("found " + resultsSize + " results"); 473 List<Cell> results1 = getScanResult(Bytes.toBytes(30), Bytes.toBytes(40), ht); 474 475 assertEquals(results1.size(), resultsSize); 476 477 ht.close(); 478 } 479 480 @Test 481 public void testMultiRowRangeWithFilterListOrOperator(TestInfo testInfo) throws IOException { 482 tableName = TableName.valueOf(testInfo.getTestMethod().get().getName()); 483 Table ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE); 484 generateRows(numRows, ht, family, qf, value); 485 486 Scan scan = new Scan(); 487 scan.readAllVersions(); 488 489 List<RowRange> ranges1 = new ArrayList<>(); 490 ranges1.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(40), false)); 491 ranges1.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false)); 492 ranges1.add(new RowRange(Bytes.toBytes(60), true, Bytes.toBytes(70), false)); 493 494 MultiRowRangeFilter filter1 = new MultiRowRangeFilter(ranges1); 495 496 List<RowRange> ranges2 = new ArrayList<>(); 497 ranges2.add(new RowRange(Bytes.toBytes(20), true, Bytes.toBytes(40), false)); 498 ranges2.add(new RowRange(Bytes.toBytes(80), true, Bytes.toBytes(90), false)); 499 500 MultiRowRangeFilter filter2 = new MultiRowRangeFilter(ranges2); 501 502 FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ONE); 503 filterList.addFilter(filter1); 504 filterList.addFilter(filter2); 505 scan.setFilter(filterList); 506 int resultsSize = getResultsSize(ht, scan); 507 LOG.info("found " + resultsSize + " results"); 508 List<Cell> results1 = getScanResult(Bytes.toBytes(10), Bytes.toBytes(40), ht); 509 List<Cell> results2 = getScanResult(Bytes.toBytes(60), Bytes.toBytes(70), ht); 510 List<Cell> results3 = getScanResult(Bytes.toBytes(80), Bytes.toBytes(90), ht); 511 512 assertEquals(results1.size() + results2.size() + results3.size(), resultsSize); 513 514 ht.close(); 515 } 516 517 @Test 518 public void testOneRowRange(TestInfo testInfo) throws IOException { 519 tableName = TableName.valueOf(testInfo.getTestMethod().get().getName()); 520 Table ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE); 521 generateRows(numRows, ht, family, qf, value); 522 ArrayList<MultiRowRangeFilter.RowRange> rowRangesList = new ArrayList<>(); 523 rowRangesList 524 .add(new MultiRowRangeFilter.RowRange(Bytes.toBytes(50), true, Bytes.toBytes(50), true)); 525 Scan scan = new Scan(); 526 scan.setFilter(new MultiRowRangeFilter(rowRangesList)); 527 int resultsSize = getResultsSize(ht, scan); 528 assertEquals(1, resultsSize); 529 rowRangesList.clear(); 530 rowRangesList 531 .add(new MultiRowRangeFilter.RowRange(Bytes.toBytes(50), true, Bytes.toBytes(51), false)); 532 scan = new Scan(); 533 scan.setFilter(new MultiRowRangeFilter(rowRangesList)); 534 resultsSize = getResultsSize(ht, scan); 535 assertEquals(1, resultsSize); 536 rowRangesList.clear(); 537 rowRangesList 538 .add(new MultiRowRangeFilter.RowRange(Bytes.toBytes(50), true, Bytes.toBytes(51), true)); 539 scan = new Scan(); 540 scan.setFilter(new MultiRowRangeFilter(rowRangesList)); 541 resultsSize = getResultsSize(ht, scan); 542 assertEquals(2, resultsSize); 543 ht.close(); 544 } 545 546 @Test 547 public void testReverseMultiRowRangeFilterWithinTable(TestInfo testInfo) throws IOException { 548 tableName = TableName.valueOf(testInfo.getTestMethod().get().getName()); 549 Table ht = TEST_UTIL.createTable(tableName, family); 550 generateRows(numRows, ht, family, qf, value); 551 552 Scan scan = new Scan(); 553 scan.setReversed(true); 554 List<RowRange> ranges = 555 Arrays.asList(new RowRange(Bytes.toBytes(20), true, Bytes.toBytes(30), true), 556 new RowRange(Bytes.toBytes(50), true, Bytes.toBytes(60), true)); 557 MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges); 558 scan.setFilter(filter); 559 560 List<Integer> expectedResults = new ArrayList<>(); 561 for (int i = 60; i >= 50; i--) { 562 expectedResults.add(i); 563 } 564 for (int i = 30; i >= 20; i--) { 565 expectedResults.add(i); 566 } 567 568 List<Cell> results = getResults(ht, scan); 569 List<Integer> actualResults = new ArrayList<>(); 570 StringBuilder sb = new StringBuilder(); 571 for (Cell result : results) { 572 int observedValue = 573 Bytes.toInt(result.getRowArray(), result.getRowOffset(), result.getRowLength()); 574 actualResults.add(observedValue); 575 if (sb.length() > 0) { 576 sb.append(", "); 577 } 578 sb.append(observedValue); 579 } 580 assertEquals(22, results.size(), "Saw results: " + sb); 581 } 582 583 @Test 584 public void testReverseMultiRowRangeFilterIncludingMaxRow(TestInfo testInfo) throws IOException { 585 tableName = TableName.valueOf(testInfo.getTestMethod().get().getName()); 586 Table ht = TEST_UTIL.createTable(tableName, family); 587 for (String rowkey : Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h")) { 588 byte[] row = Bytes.toBytes(rowkey); 589 Put p = new Put(row); 590 p.addColumn(family, qf, value); 591 ht.put(p); 592 } 593 TEST_UTIL.flush(); 594 595 Scan scan = new Scan(); 596 scan.setReversed(true); 597 List<RowRange> ranges = 598 Arrays.asList(new RowRange(Bytes.toBytes("b"), true, Bytes.toBytes("c"), true), 599 new RowRange(Bytes.toBytes("f"), true, Bytes.toBytes("h"), true)); 600 MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges); 601 scan.setFilter(filter); 602 603 List<String> expected = Arrays.asList("h", "g", "f", "c", "b"); 604 List<String> actual = new ArrayList<>(); 605 for (Cell cell : getResults(ht, scan)) { 606 actual.add(Bytes.toString(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength())); 607 } 608 609 assertEquals(expected, actual); 610 } 611 612 @Test 613 public void testReverseMultiRowRangeFilterIncludingMinRow(TestInfo testInfo) throws IOException { 614 tableName = TableName.valueOf(testInfo.getTestMethod().get().getName()); 615 Table ht = TEST_UTIL.createTable(tableName, family); 616 for (String rowkey : Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h")) { 617 byte[] row = Bytes.toBytes(rowkey); 618 Put p = new Put(row); 619 p.addColumn(family, qf, value); 620 ht.put(p); 621 } 622 TEST_UTIL.flush(); 623 624 Scan scan = new Scan(); 625 scan.setReversed(true); 626 List<RowRange> ranges = 627 Arrays.asList(new RowRange(Bytes.toBytes("a"), true, Bytes.toBytes("c"), true), 628 new RowRange(Bytes.toBytes("f"), true, Bytes.toBytes("g"), true)); 629 MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges); 630 scan.setFilter(filter); 631 632 List<String> expected = Arrays.asList("g", "f", "c", "b", "a"); 633 List<String> actual = new ArrayList<>(); 634 for (Cell cell : getResults(ht, scan)) { 635 actual.add(Bytes.toString(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength())); 636 } 637 638 assertEquals(expected, actual); 639 } 640 641 @Test 642 public void testReverseMultiRowRangeFilterIncludingMinAndMaxRow(TestInfo testInfo) 643 throws IOException { 644 tableName = TableName.valueOf(testInfo.getTestMethod().get().getName()); 645 Table ht = TEST_UTIL.createTable(tableName, family); 646 for (String rowkey : Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h")) { 647 byte[] row = Bytes.toBytes(rowkey); 648 Put p = new Put(row); 649 p.addColumn(family, qf, value); 650 ht.put(p); 651 } 652 TEST_UTIL.flush(); 653 654 Scan scan = new Scan(); 655 scan.setReversed(true); 656 List<RowRange> ranges = 657 Arrays.asList(new RowRange(Bytes.toBytes("a"), true, Bytes.toBytes("c"), true), 658 new RowRange(Bytes.toBytes("f"), true, Bytes.toBytes("h"), true)); 659 MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges); 660 scan.setFilter(filter); 661 662 List<String> expected = Arrays.asList("h", "g", "f", "c", "b", "a"); 663 List<String> actual = new ArrayList<>(); 664 for (Cell cell : getResults(ht, scan)) { 665 actual.add(Bytes.toString(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength())); 666 } 667 668 assertEquals(expected, actual); 669 } 670 671 private void generateRows(int numberOfRows, Table ht, byte[] family, byte[] qf, byte[] value) 672 throws IOException { 673 for (int i = 0; i < numberOfRows; i++) { 674 byte[] row = Bytes.toBytes(i); 675 Put p = new Put(row); 676 p.addColumn(family, qf, value); 677 ht.put(p); 678 } 679 TEST_UTIL.flush(); 680 } 681 682 private List<Cell> getScanResult(byte[] startRow, byte[] stopRow, Table ht) throws IOException { 683 Scan scan = new Scan(); 684 scan.readAllVersions(); 685 if (!Bytes.toString(startRow).isEmpty()) { 686 scan.withStartRow(startRow); 687 } 688 if (!Bytes.toString(stopRow).isEmpty()) { 689 scan.withStopRow(stopRow); 690 } 691 ResultScanner scanner = ht.getScanner(scan); 692 List<Cell> kvList = new ArrayList<>(); 693 Result r; 694 while ((r = scanner.next()) != null) { 695 for (Cell kv : r.listCells()) { 696 kvList.add(kv); 697 } 698 } 699 scanner.close(); 700 return kvList; 701 } 702 703 private List<Cell> getResults(Table ht, Scan scan) throws IOException { 704 ResultScanner scanner = ht.getScanner(scan); 705 List<Cell> results = new ArrayList<>(); 706 Result r; 707 while ((r = scanner.next()) != null) { 708 for (Cell kv : r.listCells()) { 709 results.add(kv); 710 } 711 } 712 scanner.close(); 713 return results; 714 } 715 716 private int getResultsSize(Table ht, Scan scan) throws IOException { 717 return getResults(ht, scan).size(); 718 } 719}