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}