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