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