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
048 * the columns that are 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
058      .toBytes("invocationrecord");
059  private static final byte[] FAMILY_NAME_BYTES = Bytes.toBytes("mycf");
060
061  private static final byte[] ROW_BYTES = Bytes.toBytes("row");
062  private static final String QUALIFIER_PREFIX = "qualifier";
063  private static final String VALUE_PREFIX = "value";
064
065  private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
066  private HRegion region;
067
068  @Before
069  public void setUp() throws Exception {
070    HTableDescriptor htd = new HTableDescriptor(
071        TableName.valueOf(TABLE_NAME_BYTES));
072    htd.addFamily(new HColumnDescriptor(FAMILY_NAME_BYTES));
073    HRegionInfo info = new HRegionInfo(htd.getTableName(), null, null, false);
074    this.region = HBaseTestingUtility.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,
123      Integer[] expectedQualifiers) throws Exception {
124    Get get = new Get(ROW_BYTES);
125    for (int i = 0; i < selectQualifiers.length; i++) {
126      get.addColumn(FAMILY_NAME_BYTES,
127          Bytes.toBytes(QUALIFIER_PREFIX + selectQualifiers[i]));
128    }
129
130    get.setFilter(new InvocationRecordFilter());
131
132    List<KeyValue> expectedValues = new ArrayList<>();
133    for (int i = 0; i < expectedQualifiers.length; i++) {
134      expectedValues.add(new KeyValue(ROW_BYTES, FAMILY_NAME_BYTES, Bytes
135          .toBytes(QUALIFIER_PREFIX + expectedQualifiers[i]),
136          expectedQualifiers[i], Bytes.toBytes(VALUE_PREFIX
137              + expectedQualifiers[i])));
138    }
139
140    Scan scan = new Scan(get);
141    List<Cell> actualValues = new ArrayList<>();
142    List<Cell> temp = new ArrayList<>();
143    InternalScanner scanner = this.region.getScanner(scan);
144    while (scanner.next(temp)) {
145      actualValues.addAll(temp);
146      temp.clear();
147    }
148    actualValues.addAll(temp);
149    Assert.assertTrue("Actual values " + actualValues
150        + " differ from the expected values:" + expectedValues,
151        expectedValues.equals(actualValues));
152  }
153
154  @After
155  public void tearDown() throws Exception {
156    WAL wal = ((HRegion)region).getWAL();
157    ((HRegion)region).close();
158    wal.close();
159  }
160
161  /**
162   * Filter which gives the list of keyvalues for which the filter is invoked.
163   */
164  private static class InvocationRecordFilter extends FilterBase {
165
166    private List<Cell> visitedKeyValues = new ArrayList<>();
167
168    @Override
169    public void reset() {
170      visitedKeyValues.clear();
171    }
172
173    @Override
174    public ReturnCode filterCell(final Cell ignored) {
175      visitedKeyValues.add(ignored);
176      return ReturnCode.INCLUDE;
177    }
178
179    @Override
180    public void filterRowCells(List<Cell> kvs) {
181      kvs.clear();
182      kvs.addAll(visitedKeyValues);
183    }
184
185    @Override
186    public boolean hasFilterRow() {
187      return true;
188    }
189  }
190}