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