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.client;
019
020import static org.junit.jupiter.api.Assertions.assertEquals;
021import static org.junit.jupiter.api.Assertions.assertFalse;
022import static org.junit.jupiter.api.Assertions.assertTrue;
023
024import java.io.IOException;
025import org.apache.hadoop.hbase.Cell;
026import org.apache.hadoop.hbase.CellUtil;
027import org.apache.hadoop.hbase.CompareOperator;
028import org.apache.hadoop.hbase.HConstants;
029import org.apache.hadoop.hbase.TableName;
030import org.apache.hadoop.hbase.client.Scan.ReadType;
031import org.apache.hadoop.hbase.filter.Filter;
032import org.apache.hadoop.hbase.filter.KeyOnlyFilter;
033import org.apache.hadoop.hbase.filter.QualifierFilter;
034import org.apache.hadoop.hbase.filter.RegexStringComparator;
035import org.apache.hadoop.hbase.util.Bytes;
036import org.junit.jupiter.api.TestTemplate;
037
038public class FromClientSideTestReverseScan extends FromClientSideTestBase {
039
040  protected FromClientSideTestReverseScan(Class<? extends ConnectionRegistry> registryImpl,
041    int numHedgedReqs) {
042    super(registryImpl, numHedgedReqs);
043  }
044
045  @TestTemplate
046  public void testSuperSimpleWithReverseScan() throws Exception {
047    TEST_UTIL.createTable(tableName, FAMILY);
048    try (Connection conn = getConnection(); Table ht = conn.getTable(tableName)) {
049      Put put = new Put(Bytes.toBytes("0-b11111-0000000000000000000"));
050      put.addColumn(FAMILY, QUALIFIER, VALUE);
051      ht.put(put);
052      put = new Put(Bytes.toBytes("0-b11111-0000000000000000002"));
053      put.addColumn(FAMILY, QUALIFIER, VALUE);
054      ht.put(put);
055      put = new Put(Bytes.toBytes("0-b11111-0000000000000000004"));
056      put.addColumn(FAMILY, QUALIFIER, VALUE);
057      ht.put(put);
058      put = new Put(Bytes.toBytes("0-b11111-0000000000000000006"));
059      put.addColumn(FAMILY, QUALIFIER, VALUE);
060      ht.put(put);
061      put = new Put(Bytes.toBytes("0-b11111-0000000000000000008"));
062      put.addColumn(FAMILY, QUALIFIER, VALUE);
063      ht.put(put);
064      put = new Put(Bytes.toBytes("0-b22222-0000000000000000001"));
065      put.addColumn(FAMILY, QUALIFIER, VALUE);
066      ht.put(put);
067      put = new Put(Bytes.toBytes("0-b22222-0000000000000000003"));
068      put.addColumn(FAMILY, QUALIFIER, VALUE);
069      ht.put(put);
070      put = new Put(Bytes.toBytes("0-b22222-0000000000000000005"));
071      put.addColumn(FAMILY, QUALIFIER, VALUE);
072      ht.put(put);
073      put = new Put(Bytes.toBytes("0-b22222-0000000000000000007"));
074      put.addColumn(FAMILY, QUALIFIER, VALUE);
075      ht.put(put);
076      put = new Put(Bytes.toBytes("0-b22222-0000000000000000009"));
077      put.addColumn(FAMILY, QUALIFIER, VALUE);
078      ht.put(put);
079      Scan scan = new Scan().withStartRow(Bytes.toBytes("0-b11111-9223372036854775807"))
080        .withStopRow(Bytes.toBytes("0-b11111-0000000000000000000"), true);
081      scan.setReversed(true);
082      try (ResultScanner scanner = ht.getScanner(scan)) {
083        Result result = scanner.next();
084        assertTrue(Bytes.equals(result.getRow(), Bytes.toBytes("0-b11111-0000000000000000008")));
085      }
086    }
087  }
088
089  @TestTemplate
090  public void testFiltersWithReverseScan() throws Exception {
091    TEST_UTIL.createTable(tableName, FAMILY);
092    try (Connection conn = getConnection(); Table ht = conn.getTable(tableName)) {
093      byte[][] ROWS = makeN(ROW, 10);
094      byte[][] QUALIFIERS =
095        { Bytes.toBytes("col0-<d2v1>-<d3v2>"), Bytes.toBytes("col1-<d2v1>-<d3v2>"),
096          Bytes.toBytes("col2-<d2v1>-<d3v2>"), Bytes.toBytes("col3-<d2v1>-<d3v2>"),
097          Bytes.toBytes("col4-<d2v1>-<d3v2>"), Bytes.toBytes("col5-<d2v1>-<d3v2>"),
098          Bytes.toBytes("col6-<d2v1>-<d3v2>"), Bytes.toBytes("col7-<d2v1>-<d3v2>"),
099          Bytes.toBytes("col8-<d2v1>-<d3v2>"), Bytes.toBytes("col9-<d2v1>-<d3v2>") };
100      for (int i = 0; i < 10; i++) {
101        Put put = new Put(ROWS[i]);
102        put.addColumn(FAMILY, QUALIFIERS[i], VALUE);
103        ht.put(put);
104      }
105      Scan scan = new Scan();
106      scan.setReversed(true);
107      scan.addFamily(FAMILY);
108      Filter filter =
109        new QualifierFilter(CompareOperator.EQUAL, new RegexStringComparator("col[1-5]"));
110      scan.setFilter(filter);
111      try (ResultScanner scanner = ht.getScanner(scan)) {
112        int expectedIndex = 5;
113        for (Result result : scanner) {
114          assertEquals(1, result.size());
115          Cell c = result.rawCells()[0];
116          assertTrue(Bytes.equals(c.getRowArray(), c.getRowOffset(), c.getRowLength(),
117            ROWS[expectedIndex], 0, ROWS[expectedIndex].length));
118          assertTrue(
119            Bytes.equals(c.getQualifierArray(), c.getQualifierOffset(), c.getQualifierLength(),
120              QUALIFIERS[expectedIndex], 0, QUALIFIERS[expectedIndex].length));
121          expectedIndex--;
122        }
123        assertEquals(0, expectedIndex);
124      }
125    }
126  }
127
128  @TestTemplate
129  public void testKeyOnlyFilterWithReverseScan() throws Exception {
130    TEST_UTIL.createTable(tableName, FAMILY);
131    try (Connection conn = getConnection(); Table ht = conn.getTable(tableName)) {
132      byte[][] ROWS = makeN(ROW, 10);
133      byte[][] QUALIFIERS =
134        { Bytes.toBytes("col0-<d2v1>-<d3v2>"), Bytes.toBytes("col1-<d2v1>-<d3v2>"),
135          Bytes.toBytes("col2-<d2v1>-<d3v2>"), Bytes.toBytes("col3-<d2v1>-<d3v2>"),
136          Bytes.toBytes("col4-<d2v1>-<d3v2>"), Bytes.toBytes("col5-<d2v1>-<d3v2>"),
137          Bytes.toBytes("col6-<d2v1>-<d3v2>"), Bytes.toBytes("col7-<d2v1>-<d3v2>"),
138          Bytes.toBytes("col8-<d2v1>-<d3v2>"), Bytes.toBytes("col9-<d2v1>-<d3v2>") };
139      for (int i = 0; i < 10; i++) {
140        Put put = new Put(ROWS[i]);
141        put.addColumn(FAMILY, QUALIFIERS[i], VALUE);
142        ht.put(put);
143      }
144      Scan scan = new Scan();
145      scan.setReversed(true);
146      scan.addFamily(FAMILY);
147      Filter filter = new KeyOnlyFilter(true);
148      scan.setFilter(filter);
149      try (ResultScanner ignored = ht.getScanner(scan)) {
150        int count = 0;
151        for (Result result : ht.getScanner(scan)) {
152          assertEquals(1, result.size());
153          assertEquals(Bytes.SIZEOF_INT, result.rawCells()[0].getValueLength());
154          assertEquals(VALUE.length, Bytes.toInt(CellUtil.cloneValue(result.rawCells()[0])));
155          count++;
156        }
157        assertEquals(10, count);
158      }
159    }
160  }
161
162  /**
163   * Test simple table and non-existent row cases.
164   */
165  @TestTemplate
166  public void testSimpleMissingWithReverseScan() throws Exception {
167    TEST_UTIL.createTable(tableName, FAMILY);
168    try (Connection conn = getConnection(); Table ht = conn.getTable(tableName)) {
169      byte[][] ROWS = makeN(ROW, 4);
170
171      // Try to get a row on an empty table
172      Scan scan = new Scan();
173      scan.setReversed(true);
174      Result result = getSingleScanResult(ht, scan);
175      assertNullResult(result);
176
177      scan = new Scan().withStartRow(ROWS[0]);
178      scan.setReversed(true);
179      result = getSingleScanResult(ht, scan);
180      assertNullResult(result);
181
182      scan = new Scan().withStartRow(ROWS[0]).withStopRow(ROWS[1], true);
183      scan.setReversed(true);
184      result = getSingleScanResult(ht, scan);
185      assertNullResult(result);
186
187      scan = new Scan();
188      scan.setReversed(true);
189      scan.addFamily(FAMILY);
190      result = getSingleScanResult(ht, scan);
191      assertNullResult(result);
192
193      scan = new Scan();
194      scan.setReversed(true);
195      scan.addColumn(FAMILY, QUALIFIER);
196      result = getSingleScanResult(ht, scan);
197      assertNullResult(result);
198
199      // Insert a row
200      Put put = new Put(ROWS[2]);
201      put.addColumn(FAMILY, QUALIFIER, VALUE);
202      ht.put(put);
203
204      // Make sure we can scan the row
205      scan = new Scan();
206      scan.setReversed(true);
207      result = getSingleScanResult(ht, scan);
208      assertSingleResult(result, ROWS[2], FAMILY, QUALIFIER, VALUE);
209
210      scan = new Scan().withStartRow(ROWS[3]).withStopRow(ROWS[0], true);
211      scan.setReversed(true);
212      result = getSingleScanResult(ht, scan);
213      assertSingleResult(result, ROWS[2], FAMILY, QUALIFIER, VALUE);
214
215      scan = new Scan().withStartRow(ROWS[2]).withStopRow(ROWS[1], true);
216      scan.setReversed(true);
217      result = getSingleScanResult(ht, scan);
218      assertSingleResult(result, ROWS[2], FAMILY, QUALIFIER, VALUE);
219
220      // Try to scan empty rows around it
221      // Introduced MemStore#shouldSeekForReverseScan to fix the following
222      scan = new Scan().withStartRow(ROWS[1]);
223      scan.setReversed(true);
224      result = getSingleScanResult(ht, scan);
225      assertNullResult(result);
226    }
227  }
228
229  @TestTemplate
230  public void testNullWithReverseScan() throws Exception {
231    TEST_UTIL.createTable(tableName, FAMILY);
232    try (Connection conn = getConnection(); Table ht = conn.getTable(tableName)) {
233      // Null qualifier (should work)
234      Put put = new Put(ROW);
235      put.addColumn(FAMILY, null, VALUE);
236      ht.put(put);
237      scanTestNull(ht, ROW, FAMILY, VALUE, true);
238      Delete delete = new Delete(ROW);
239      delete.addColumns(FAMILY, null);
240      ht.delete(delete);
241    }
242
243    // Use a new table
244    TableName newTableName = TableName.valueOf(tableName.toString() + "2");
245    TEST_UTIL.createTable(newTableName, FAMILY);
246    try (Connection conn = getConnection(); Table ht = conn.getTable(newTableName)) {
247      // Empty qualifier, byte[0] instead of null (should work)
248      Put put = new Put(ROW);
249      put.addColumn(FAMILY, HConstants.EMPTY_BYTE_ARRAY, VALUE);
250      ht.put(put);
251      scanTestNull(ht, ROW, FAMILY, VALUE, true);
252      TEST_UTIL.flush();
253      scanTestNull(ht, ROW, FAMILY, VALUE, true);
254      Delete delete = new Delete(ROW);
255      delete.addColumns(FAMILY, HConstants.EMPTY_BYTE_ARRAY);
256      ht.delete(delete);
257      // Null value
258      put = new Put(ROW);
259      put.addColumn(FAMILY, QUALIFIER, null);
260      ht.put(put);
261      Scan scan = new Scan();
262      scan.setReversed(true);
263      scan.addColumn(FAMILY, QUALIFIER);
264      Result result = getSingleScanResult(ht, scan);
265      assertSingleResult(result, ROW, FAMILY, QUALIFIER, null);
266    }
267  }
268
269  @TestTemplate
270  @SuppressWarnings("checkstyle:MethodLength")
271  public void testDeletesWithReverseScan() throws Exception {
272    byte[][] ROWS = makeNAscii(ROW, 6);
273    byte[][] FAMILIES = makeNAscii(FAMILY, 3);
274    byte[][] VALUES = makeN(VALUE, 5);
275    long[] ts = { 1000, 2000, 3000, 4000, 5000 };
276    TEST_UTIL.createTable(tableName, FAMILIES, 3);
277    try (Connection conn = getConnection(); Table ht = conn.getTable(tableName)) {
278      Put put = new Put(ROW);
279      put.addColumn(FAMILIES[0], QUALIFIER, ts[0], VALUES[0]);
280      put.addColumn(FAMILIES[0], QUALIFIER, ts[1], VALUES[1]);
281      ht.put(put);
282
283      Delete delete = new Delete(ROW);
284      delete.addFamily(FAMILIES[0], ts[0]);
285      ht.delete(delete);
286
287      Scan scan = new Scan().withStartRow(ROW);
288      scan.setReversed(true);
289      scan.addFamily(FAMILIES[0]);
290      scan.readVersions(Integer.MAX_VALUE);
291      Result result = getSingleScanResult(ht, scan);
292      assertNResult(result, ROW, FAMILIES[0], QUALIFIER, new long[] { ts[1] },
293        new byte[][] { VALUES[1] }, 0, 0);
294
295      // Test delete latest version
296      put = new Put(ROW);
297      put.addColumn(FAMILIES[0], QUALIFIER, ts[4], VALUES[4]);
298      put.addColumn(FAMILIES[0], QUALIFIER, ts[2], VALUES[2]);
299      put.addColumn(FAMILIES[0], QUALIFIER, ts[3], VALUES[3]);
300      put.addColumn(FAMILIES[0], null, ts[4], VALUES[4]);
301      put.addColumn(FAMILIES[0], null, ts[2], VALUES[2]);
302      put.addColumn(FAMILIES[0], null, ts[3], VALUES[3]);
303      ht.put(put);
304
305      delete = new Delete(ROW);
306      delete.addColumn(FAMILIES[0], QUALIFIER); // ts[4]
307      ht.delete(delete);
308
309      scan = new Scan().withStartRow(ROW);
310      scan.setReversed(true);
311      scan.addColumn(FAMILIES[0], QUALIFIER);
312      scan.readVersions(Integer.MAX_VALUE);
313      result = getSingleScanResult(ht, scan);
314      assertNResult(result, ROW, FAMILIES[0], QUALIFIER, new long[] { ts[1], ts[2], ts[3] },
315        new byte[][] { VALUES[1], VALUES[2], VALUES[3] }, 0, 2);
316
317      // Test for HBASE-1847
318      delete = new Delete(ROW);
319      delete.addColumn(FAMILIES[0], null);
320      ht.delete(delete);
321
322      // Cleanup null qualifier
323      delete = new Delete(ROW);
324      delete.addColumns(FAMILIES[0], null);
325      ht.delete(delete);
326
327      // Expected client behavior might be that you can re-put deleted values
328      // But alas, this is not to be. We can't put them back in either case.
329
330      put = new Put(ROW);
331      put.addColumn(FAMILIES[0], QUALIFIER, ts[0], VALUES[0]);
332      put.addColumn(FAMILIES[0], QUALIFIER, ts[4], VALUES[4]);
333      ht.put(put);
334
335      // The Scanner returns the previous values, the expected-naive-unexpected
336      // behavior
337
338      scan = new Scan().withStartRow(ROW);
339      scan.setReversed(true);
340      scan.addFamily(FAMILIES[0]);
341      scan.readVersions(Integer.MAX_VALUE);
342      result = getSingleScanResult(ht, scan);
343      assertNResult(result, ROW, FAMILIES[0], QUALIFIER, new long[] { ts[1], ts[2], ts[3] },
344        new byte[][] { VALUES[1], VALUES[2], VALUES[3] }, 0, 2);
345
346      // Test deleting an entire family from one row but not the other various
347      // ways
348
349      put = new Put(ROWS[0]);
350      put.addColumn(FAMILIES[1], QUALIFIER, ts[0], VALUES[0]);
351      put.addColumn(FAMILIES[1], QUALIFIER, ts[1], VALUES[1]);
352      put.addColumn(FAMILIES[2], QUALIFIER, ts[2], VALUES[2]);
353      put.addColumn(FAMILIES[2], QUALIFIER, ts[3], VALUES[3]);
354      ht.put(put);
355
356      put = new Put(ROWS[1]);
357      put.addColumn(FAMILIES[1], QUALIFIER, ts[0], VALUES[0]);
358      put.addColumn(FAMILIES[1], QUALIFIER, ts[1], VALUES[1]);
359      put.addColumn(FAMILIES[2], QUALIFIER, ts[2], VALUES[2]);
360      put.addColumn(FAMILIES[2], QUALIFIER, ts[3], VALUES[3]);
361      ht.put(put);
362
363      put = new Put(ROWS[2]);
364      put.addColumn(FAMILIES[1], QUALIFIER, ts[0], VALUES[0]);
365      put.addColumn(FAMILIES[1], QUALIFIER, ts[1], VALUES[1]);
366      put.addColumn(FAMILIES[2], QUALIFIER, ts[2], VALUES[2]);
367      put.addColumn(FAMILIES[2], QUALIFIER, ts[3], VALUES[3]);
368      ht.put(put);
369
370      delete = new Delete(ROWS[0]);
371      delete.addFamily(FAMILIES[2]);
372      ht.delete(delete);
373
374      delete = new Delete(ROWS[1]);
375      delete.addColumns(FAMILIES[1], QUALIFIER);
376      ht.delete(delete);
377
378      delete = new Delete(ROWS[2]);
379      delete.addColumn(FAMILIES[1], QUALIFIER);
380      delete.addColumn(FAMILIES[1], QUALIFIER);
381      delete.addColumn(FAMILIES[2], QUALIFIER);
382      ht.delete(delete);
383
384      scan = new Scan().withStartRow(ROWS[0]);
385      scan.setReversed(true);
386      scan.addFamily(FAMILIES[1]);
387      scan.addFamily(FAMILIES[2]);
388      scan.readVersions(Integer.MAX_VALUE);
389      result = getSingleScanResult(ht, scan);
390      assertEquals(2, result.size(), "Expected 2 keys but received " + result.size());
391      assertNResult(result, ROWS[0], FAMILIES[1], QUALIFIER, new long[] { ts[0], ts[1] },
392        new byte[][] { VALUES[0], VALUES[1] }, 0, 1);
393
394      scan = new Scan().withStartRow(ROWS[1]);
395      scan.setReversed(true);
396      scan.addFamily(FAMILIES[1]);
397      scan.addFamily(FAMILIES[2]);
398      scan.readVersions(Integer.MAX_VALUE);
399      result = getSingleScanResult(ht, scan);
400      assertEquals(2, result.size(), "Expected 2 keys but received " + result.size());
401
402      scan = new Scan().withStartRow(ROWS[2]);
403      scan.setReversed(true);
404      scan.addFamily(FAMILIES[1]);
405      scan.addFamily(FAMILIES[2]);
406      scan.readVersions(Integer.MAX_VALUE);
407      result = getSingleScanResult(ht, scan);
408      assertEquals(1, result.size());
409      assertNResult(result, ROWS[2], FAMILIES[2], QUALIFIER, new long[] { ts[2] },
410        new byte[][] { VALUES[2] }, 0, 0);
411
412      // Test if we delete the family first in one row (HBASE-1541)
413
414      delete = new Delete(ROWS[3]);
415      delete.addFamily(FAMILIES[1]);
416      ht.delete(delete);
417
418      put = new Put(ROWS[3]);
419      put.addColumn(FAMILIES[2], QUALIFIER, VALUES[0]);
420      ht.put(put);
421
422      put = new Put(ROWS[4]);
423      put.addColumn(FAMILIES[1], QUALIFIER, VALUES[1]);
424      put.addColumn(FAMILIES[2], QUALIFIER, VALUES[2]);
425      ht.put(put);
426
427      scan = new Scan().withStartRow(ROWS[4]);
428      scan.setReversed(true);
429      scan.addFamily(FAMILIES[1]);
430      scan.addFamily(FAMILIES[2]);
431      scan.readVersions(Integer.MAX_VALUE);
432      try (ResultScanner scanner = ht.getScanner(scan)) {
433        result = scanner.next();
434        assertEquals(2, result.size(), "Expected 2 keys but received " + result.size());
435        assertTrue(Bytes.equals(CellUtil.cloneRow(result.rawCells()[0]), ROWS[4]));
436        assertTrue(Bytes.equals(CellUtil.cloneRow(result.rawCells()[1]), ROWS[4]));
437        assertTrue(Bytes.equals(CellUtil.cloneValue(result.rawCells()[0]), VALUES[1]));
438        assertTrue(Bytes.equals(CellUtil.cloneValue(result.rawCells()[1]), VALUES[2]));
439        result = scanner.next();
440        assertEquals(1, result.size(), "Expected 1 key but received " + result.size());
441        assertTrue(Bytes.equals(CellUtil.cloneRow(result.rawCells()[0]), ROWS[3]));
442        assertTrue(Bytes.equals(CellUtil.cloneValue(result.rawCells()[0]), VALUES[0]));
443      }
444    }
445  }
446
447  /**
448   * Tests reversed scan under multi regions
449   */
450  @TestTemplate
451  public void testReversedScanUnderMultiRegions() throws Exception {
452    // Test Initialization.
453    byte[] maxByteArray = ConnectionUtils.MAX_BYTE_ARRAY;
454    byte[][] splitRows = new byte[][] { Bytes.toBytes("005"),
455      Bytes.add(Bytes.toBytes("005"), Bytes.multiple(maxByteArray, 16)), Bytes.toBytes("006"),
456      Bytes.add(Bytes.toBytes("006"), Bytes.multiple(maxByteArray, 8)), Bytes.toBytes("007"),
457      Bytes.add(Bytes.toBytes("007"), Bytes.multiple(maxByteArray, 4)), Bytes.toBytes("008"),
458      Bytes.multiple(maxByteArray, 2) };
459    TEST_UTIL.createTable(tableName, FAMILY, splitRows);
460    TEST_UTIL.waitUntilAllRegionsAssigned(tableName);
461    try (Connection conn = getConnection(); Table table = conn.getTable(tableName)) {
462      try (RegionLocator l = conn.getRegionLocator(tableName)) {
463        assertEquals(splitRows.length + 1, l.getAllRegionLocations().size());
464      }
465      // Insert one row each region
466      int insertNum = splitRows.length;
467      for (byte[] splitRow : splitRows) {
468        Put put = new Put(splitRow);
469        put.addColumn(FAMILY, QUALIFIER, VALUE);
470        table.put(put);
471      }
472
473      // scan forward
474      try (ResultScanner scanner = table.getScanner(new Scan())) {
475        int count = 0;
476        for (Result r : scanner) {
477          assertFalse(r.isEmpty());
478          count++;
479        }
480        assertEquals(insertNum, count);
481      }
482
483      // scan backward
484      Scan scan = new Scan();
485      scan.setReversed(true);
486      try (ResultScanner scanner = table.getScanner(scan)) {
487        int count = 0;
488        byte[] lastRow = null;
489        for (Result r : scanner) {
490          assertFalse(r.isEmpty());
491          count++;
492          byte[] thisRow = r.getRow();
493          if (lastRow != null) {
494            assertTrue(Bytes.compareTo(thisRow, lastRow) < 0, "Error scan order, last row= "
495              + Bytes.toString(lastRow) + ",this row=" + Bytes.toString(thisRow));
496          }
497          lastRow = thisRow;
498        }
499        assertEquals(insertNum, count);
500      }
501    }
502  }
503
504  /**
505   * Tests reversed scan under multi regions
506   */
507  @TestTemplate
508  public void testSmallReversedScanUnderMultiRegions() throws Exception {
509    // Test Initialization.
510    byte[][] splitRows = new byte[][] { Bytes.toBytes("000"), Bytes.toBytes("002"),
511      Bytes.toBytes("004"), Bytes.toBytes("006"), Bytes.toBytes("008"), Bytes.toBytes("010") };
512    TEST_UTIL.createTable(tableName, FAMILY, splitRows);
513    TEST_UTIL.waitUntilAllRegionsAssigned(tableName);
514    try (Connection conn = getConnection(); Table table = conn.getTable(tableName)) {
515      try (RegionLocator l = conn.getRegionLocator(tableName)) {
516        assertEquals(splitRows.length + 1, l.getAllRegionLocations().size());
517      }
518      for (byte[] splitRow : splitRows) {
519        Put put = new Put(splitRow);
520        put.addColumn(FAMILY, QUALIFIER, VALUE);
521        table.put(put);
522
523        byte[] nextRow = Bytes.copy(splitRow);
524        nextRow[nextRow.length - 1]++;
525
526        put = new Put(nextRow);
527        put.addColumn(FAMILY, QUALIFIER, VALUE);
528        table.put(put);
529      }
530
531      // scan forward
532      try (ResultScanner scanner = table.getScanner(new Scan())) {
533        int count = 0;
534        for (Result r : scanner) {
535          assertTrue(!r.isEmpty());
536          count++;
537        }
538        assertEquals(12, count);
539      }
540
541      reverseScanTest(table, ReadType.STREAM);
542      reverseScanTest(table, ReadType.PREAD);
543      reverseScanTest(table, ReadType.DEFAULT);
544    }
545  }
546
547  private void reverseScanTest(Table table, ReadType readType) throws IOException {
548    // scan backward
549    Scan scan = new Scan();
550    scan.setReversed(true);
551    try (ResultScanner scanner = table.getScanner(scan)) {
552      int count = 0;
553      byte[] lastRow = null;
554      for (Result r : scanner) {
555        assertTrue(!r.isEmpty());
556        count++;
557        byte[] thisRow = r.getRow();
558        if (lastRow != null) {
559          assertTrue(Bytes.compareTo(thisRow, lastRow) < 0, "Error scan order, last row= "
560            + Bytes.toString(lastRow) + ",this row=" + Bytes.toString(thisRow));
561        }
562        lastRow = thisRow;
563      }
564      assertEquals(12, count);
565    }
566
567    scan = new Scan();
568    scan.setReadType(readType);
569    scan.setReversed(true);
570    scan.withStartRow(Bytes.toBytes("002"));
571    try (ResultScanner scanner = table.getScanner(scan)) {
572      int count = 0;
573      byte[] lastRow = null;
574      for (Result r : scanner) {
575        assertTrue(!r.isEmpty());
576        count++;
577        byte[] thisRow = r.getRow();
578        if (lastRow != null) {
579          assertTrue(Bytes.compareTo(thisRow, lastRow) < 0, "Error scan order, last row= "
580            + Bytes.toString(lastRow) + ",this row=" + Bytes.toString(thisRow));
581        }
582        lastRow = thisRow;
583      }
584      assertEquals(3, count); // 000 001 002
585    }
586
587    scan = new Scan();
588    scan.setReadType(readType);
589    scan.setReversed(true);
590    scan.withStartRow(Bytes.toBytes("002"));
591    scan.withStopRow(Bytes.toBytes("000"));
592    try (ResultScanner scanner = table.getScanner(scan)) {
593      int count = 0;
594      byte[] lastRow = null;
595      for (Result r : scanner) {
596        assertFalse(r.isEmpty());
597        count++;
598        byte[] thisRow = r.getRow();
599        if (lastRow != null) {
600          assertTrue(Bytes.compareTo(thisRow, lastRow) < 0, "Error scan order, last row= "
601            + Bytes.toString(lastRow) + ",this row=" + Bytes.toString(thisRow));
602        }
603        lastRow = thisRow;
604      }
605      assertEquals(2, count); // 001 002
606    }
607
608    scan = new Scan();
609    scan.setReadType(readType);
610    scan.setReversed(true);
611    scan.withStartRow(Bytes.toBytes("001"));
612    try (ResultScanner scanner = table.getScanner(scan)) {
613      int count = 0;
614      byte[] lastRow = null;
615      for (Result r : scanner) {
616        assertFalse(r.isEmpty());
617        count++;
618        byte[] thisRow = r.getRow();
619        if (lastRow != null) {
620          assertTrue(Bytes.compareTo(thisRow, lastRow) < 0, "Error scan order, last row= "
621            + Bytes.toString(lastRow) + ",this row=" + Bytes.toString(thisRow));
622        }
623        lastRow = thisRow;
624      }
625      assertEquals(2, count); // 000 001
626    }
627
628    scan = new Scan();
629    scan.setReadType(readType);
630    scan.setReversed(true);
631    scan.withStartRow(Bytes.toBytes("000"));
632    try (ResultScanner scanner = table.getScanner(scan)) {
633      int count = 0;
634      byte[] lastRow = null;
635      for (Result r : scanner) {
636        assertFalse(r.isEmpty());
637        count++;
638        byte[] thisRow = r.getRow();
639        if (lastRow != null) {
640          assertTrue(Bytes.compareTo(thisRow, lastRow) < 0, "Error scan order, last row= "
641            + Bytes.toString(lastRow) + ",this row=" + Bytes.toString(thisRow));
642        }
643        lastRow = thisRow;
644      }
645      assertEquals(1, count); // 000
646    }
647
648    scan = new Scan();
649    scan.setReadType(readType);
650    scan.setReversed(true);
651    scan.withStartRow(Bytes.toBytes("006"));
652    scan.withStopRow(Bytes.toBytes("002"));
653    try (ResultScanner scanner = table.getScanner(scan)) {
654      int count = 0;
655      byte[] lastRow = null;
656      for (Result r : scanner) {
657        assertFalse(r.isEmpty());
658        count++;
659        byte[] thisRow = r.getRow();
660        if (lastRow != null) {
661          assertTrue(Bytes.compareTo(thisRow, lastRow) < 0, "Error scan order, last row= "
662            + Bytes.toString(lastRow) + ",this row=" + Bytes.toString(thisRow));
663        }
664        lastRow = thisRow;
665      }
666      assertEquals(4, count); // 003 004 005 006
667    }
668  }
669}