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