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