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.io.encoding;
019
020import static org.junit.Assert.assertEquals;
021
022import java.util.ArrayList;
023import java.util.Arrays;
024import java.util.List;
025import org.apache.hadoop.fs.Path;
026import org.apache.hadoop.hbase.Cell;
027import org.apache.hadoop.hbase.HBaseClassTestRule;
028import org.apache.hadoop.hbase.HBaseTestingUtility;
029import org.apache.hadoop.hbase.HColumnDescriptor;
030import org.apache.hadoop.hbase.HConstants;
031import org.apache.hadoop.hbase.HRegionInfo;
032import org.apache.hadoop.hbase.HTableDescriptor;
033import org.apache.hadoop.hbase.TableName;
034import org.apache.hadoop.hbase.client.Put;
035import org.apache.hadoop.hbase.client.Scan;
036import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter;
037import org.apache.hadoop.hbase.regionserver.HRegion;
038import org.apache.hadoop.hbase.regionserver.RegionScanner;
039import org.apache.hadoop.hbase.testclassification.IOTests;
040import org.apache.hadoop.hbase.testclassification.SmallTests;
041import org.apache.hadoop.hbase.util.Bytes;
042import org.junit.After;
043import org.junit.Before;
044import org.junit.ClassRule;
045import org.junit.Test;
046import org.junit.experimental.categories.Category;
047
048@Category({ IOTests.class, SmallTests.class })
049public class TestSeekBeforeWithReverseScan {
050
051  @ClassRule
052  public static final HBaseClassTestRule CLASS_RULE =
053    HBaseClassTestRule.forClass(TestSeekBeforeWithReverseScan.class);
054
055  private final HBaseTestingUtility testUtil = new HBaseTestingUtility();
056
057  private HRegion region;
058
059  private byte[] cfName = Bytes.toBytes("a");
060  private byte[] cqName = Bytes.toBytes("b");
061
062  @Before
063  public void setUp() throws Exception {
064    TableName tableName = TableName.valueOf(getClass().getSimpleName());
065    HTableDescriptor htd = new HTableDescriptor(tableName);
066    htd.addFamily(new HColumnDescriptor(cfName).setDataBlockEncoding(DataBlockEncoding.FAST_DIFF));
067    HRegionInfo info = new HRegionInfo(tableName, null, null, false);
068    Path path = testUtil.getDataTestDir(getClass().getSimpleName());
069    region = HBaseTestingUtility.createRegionAndWAL(info, path, testUtil.getConfiguration(), htd);
070  }
071
072  @After
073  public void tearDown() throws Exception {
074    HBaseTestingUtility.closeRegionAndWAL(region);
075    testUtil.cleanupTestDir();
076  }
077
078  @Test
079  public void testReverseScanWithoutPadding() throws Exception {
080    byte[] row1 = Bytes.toBytes("a");
081    byte[] row2 = Bytes.toBytes("ab");
082    byte[] row3 = Bytes.toBytes("b");
083
084    Put put1 = new Put(row1);
085    put1.addColumn(cfName, cqName, HConstants.EMPTY_BYTE_ARRAY);
086    Put put2 = new Put(row2);
087    put2.addColumn(cfName, cqName, HConstants.EMPTY_BYTE_ARRAY);
088    Put put3 = new Put(row3);
089    put3.addColumn(cfName, cqName, HConstants.EMPTY_BYTE_ARRAY);
090
091    region.put(put1);
092    region.put(put2);
093    region.put(put3);
094    region.flush(true);
095    Scan scan = new Scan();
096    scan.setCacheBlocks(false);
097    scan.setReversed(true);
098    scan.setFilter(new FirstKeyOnlyFilter());
099    scan.addFamily(cfName);
100    RegionScanner scanner = region.getScanner(scan);
101    List<Cell> res = new ArrayList<>();
102    int count = 1;
103    while (scanner.next(res)) {
104      count++;
105    }
106    assertEquals("b", Bytes.toString(res.get(0).getRowArray(), res.get(0).getRowOffset(),
107      res.get(0).getRowLength()));
108    assertEquals("ab", Bytes.toString(res.get(1).getRowArray(), res.get(1).getRowOffset(),
109      res.get(1).getRowLength()));
110    assertEquals("a", Bytes.toString(res.get(2).getRowArray(), res.get(2).getRowOffset(),
111      res.get(2).getRowLength()));
112    assertEquals(3, count);
113  }
114
115  @Test
116  public void testReverseScanWithPadding() throws Exception {
117    byte[] terminator = new byte[] { -1 };
118    byte[] row1 = Bytes.add(invert(Bytes.toBytes("a")), terminator);
119    byte[] row2 = Bytes.add(invert(Bytes.toBytes("ab")), terminator);
120    byte[] row3 = Bytes.add(invert(Bytes.toBytes("b")), terminator);
121
122    Put put1 = new Put(row1);
123    put1.addColumn(cfName, cqName, HConstants.EMPTY_BYTE_ARRAY);
124    Put put2 = new Put(row2);
125    put2.addColumn(cfName, cqName, HConstants.EMPTY_BYTE_ARRAY);
126    Put put3 = new Put(row3);
127    put3.addColumn(cfName, cqName, HConstants.EMPTY_BYTE_ARRAY);
128
129    region.put(put1);
130    region.put(put2);
131    region.put(put3);
132    region.flush(true);
133    Scan scan = new Scan();
134    scan.setCacheBlocks(false);
135    scan.setReversed(true);
136    scan.setFilter(new FirstKeyOnlyFilter());
137    scan.addFamily(cfName);
138    RegionScanner scanner = region.getScanner(scan);
139    List<Cell> res = new ArrayList<>();
140    int count = 1;
141    while (scanner.next(res)) {
142      count++;
143    }
144    assertEquals(3, count);
145  }
146
147  private byte[] invert(byte[] bytes) {
148    byte[] newBytes = Arrays.copyOf(bytes, bytes.length);
149    for (int i = 0; i < newBytes.length; i++) {
150      newBytes[i] = (byte) (newBytes[i] ^ 0xFF);
151    }
152    return newBytes;
153  }
154}