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 java.util.ArrayList;
021import java.util.List;
022import org.apache.hadoop.hbase.Cell;
023import org.apache.hadoop.hbase.HBaseClassTestRule;
024import org.apache.hadoop.hbase.HBaseTestingUtil;
025import org.apache.hadoop.hbase.KeyValue;
026import org.apache.hadoop.hbase.TableName;
027import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
028import org.apache.hadoop.hbase.client.Get;
029import org.apache.hadoop.hbase.client.Put;
030import org.apache.hadoop.hbase.client.RegionInfo;
031import org.apache.hadoop.hbase.client.RegionInfoBuilder;
032import org.apache.hadoop.hbase.client.Scan;
033import org.apache.hadoop.hbase.client.TableDescriptor;
034import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
035import org.apache.hadoop.hbase.regionserver.HRegion;
036import org.apache.hadoop.hbase.regionserver.InternalScanner;
037import org.apache.hadoop.hbase.testclassification.FilterTests;
038import org.apache.hadoop.hbase.testclassification.SmallTests;
039import org.apache.hadoop.hbase.util.Bytes;
040import org.apache.hadoop.hbase.wal.WAL;
041import org.junit.After;
042import org.junit.Assert;
043import org.junit.Before;
044import org.junit.ClassRule;
045import org.junit.Test;
046import org.junit.experimental.categories.Category;
047
048/**
049 * Test the invocation logic of the filters. A filter must be invoked only for the columns that are
050 * requested for.
051 */
052@Category({ FilterTests.class, SmallTests.class })
053public class TestInvocationRecordFilter {
054
055  @ClassRule
056  public static final HBaseClassTestRule CLASS_RULE =
057    HBaseClassTestRule.forClass(TestInvocationRecordFilter.class);
058
059  private static final byte[] TABLE_NAME_BYTES = Bytes.toBytes("invocationrecord");
060  private static final byte[] FAMILY_NAME_BYTES = Bytes.toBytes("mycf");
061
062  private static final byte[] ROW_BYTES = Bytes.toBytes("row");
063  private static final String QUALIFIER_PREFIX = "qualifier";
064  private static final String VALUE_PREFIX = "value";
065
066  private final static HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
067  private HRegion region;
068
069  @Before
070  public void setUp() throws Exception {
071    TableDescriptor htd = TableDescriptorBuilder.newBuilder(TableName.valueOf(TABLE_NAME_BYTES))
072      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY_NAME_BYTES)).build();
073    RegionInfo info = RegionInfoBuilder.newBuilder(htd.getTableName()).build();
074    this.region = HBaseTestingUtil.createRegionAndWAL(info, TEST_UTIL.getDataTestDir(),
075      TEST_UTIL.getConfiguration(), htd);
076
077    Put put = new Put(ROW_BYTES);
078    for (int i = 0; i < 10; i += 2) {
079      // puts 0, 2, 4, 6 and 8
080      put.addColumn(FAMILY_NAME_BYTES, Bytes.toBytes(QUALIFIER_PREFIX + i), (long) i,
081        Bytes.toBytes(VALUE_PREFIX + i));
082    }
083    this.region.put(put);
084    this.region.flush(true);
085  }
086
087  @Test
088  public void testFilterInvocation() throws Exception {
089    List<Integer> selectQualifiers = new ArrayList<>();
090    List<Integer> expectedQualifiers = new ArrayList<>();
091
092    selectQualifiers.add(-1);
093    verifyInvocationResults(selectQualifiers.toArray(new Integer[selectQualifiers.size()]),
094      expectedQualifiers.toArray(new Integer[expectedQualifiers.size()]));
095
096    selectQualifiers.clear();
097
098    selectQualifiers.add(0);
099    expectedQualifiers.add(0);
100    verifyInvocationResults(selectQualifiers.toArray(new Integer[selectQualifiers.size()]),
101      expectedQualifiers.toArray(new Integer[expectedQualifiers.size()]));
102
103    selectQualifiers.add(3);
104    verifyInvocationResults(selectQualifiers.toArray(new Integer[selectQualifiers.size()]),
105      expectedQualifiers.toArray(new Integer[expectedQualifiers.size()]));
106
107    selectQualifiers.add(4);
108    expectedQualifiers.add(4);
109    verifyInvocationResults(selectQualifiers.toArray(new Integer[selectQualifiers.size()]),
110      expectedQualifiers.toArray(new Integer[expectedQualifiers.size()]));
111
112    selectQualifiers.add(5);
113    verifyInvocationResults(selectQualifiers.toArray(new Integer[selectQualifiers.size()]),
114      expectedQualifiers.toArray(new Integer[expectedQualifiers.size()]));
115
116    selectQualifiers.add(8);
117    expectedQualifiers.add(8);
118    verifyInvocationResults(selectQualifiers.toArray(new Integer[selectQualifiers.size()]),
119      expectedQualifiers.toArray(new Integer[expectedQualifiers.size()]));
120  }
121
122  public void verifyInvocationResults(Integer[] selectQualifiers, Integer[] expectedQualifiers)
123    throws Exception {
124    Get get = new Get(ROW_BYTES);
125    for (int i = 0; i < selectQualifiers.length; i++) {
126      get.addColumn(FAMILY_NAME_BYTES, Bytes.toBytes(QUALIFIER_PREFIX + selectQualifiers[i]));
127    }
128
129    get.setFilter(new InvocationRecordFilter());
130
131    List<KeyValue> expectedValues = new ArrayList<>();
132    for (int i = 0; i < expectedQualifiers.length; i++) {
133      expectedValues.add(new KeyValue(ROW_BYTES, FAMILY_NAME_BYTES,
134        Bytes.toBytes(QUALIFIER_PREFIX + expectedQualifiers[i]), expectedQualifiers[i],
135        Bytes.toBytes(VALUE_PREFIX + expectedQualifiers[i])));
136    }
137
138    Scan scan = new Scan(get);
139    List<Cell> actualValues = new ArrayList<>();
140    List<Cell> temp = new ArrayList<>();
141    InternalScanner scanner = this.region.getScanner(scan);
142    while (scanner.next(temp)) {
143      actualValues.addAll(temp);
144      temp.clear();
145    }
146    actualValues.addAll(temp);
147    Assert.assertTrue(
148      "Actual values " + actualValues + " differ from the expected values:" + expectedValues,
149      expectedValues.equals(actualValues));
150  }
151
152  @After
153  public void tearDown() throws Exception {
154    WAL wal = ((HRegion) region).getWAL();
155    ((HRegion) region).close();
156    wal.close();
157  }
158
159  /**
160   * Filter which gives the list of keyvalues for which the filter is invoked.
161   */
162  private static class InvocationRecordFilter extends FilterBase {
163
164    private List<Cell> visitedKeyValues = new ArrayList<>();
165
166    @Override
167    public void reset() {
168      visitedKeyValues.clear();
169    }
170
171    @Override
172    public ReturnCode filterCell(final Cell ignored) {
173      visitedKeyValues.add(ignored);
174      return ReturnCode.INCLUDE;
175    }
176
177    @Override
178    public void filterRowCells(List<Cell> kvs) {
179      kvs.clear();
180      kvs.addAll(visitedKeyValues);
181    }
182
183    @Override
184    public boolean hasFilterRow() {
185      return true;
186    }
187  }
188}