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.assertEquals;
021import static org.junit.jupiter.api.Assertions.assertTrue;
022
023import java.io.IOException;
024import java.util.ArrayList;
025import java.util.Arrays;
026import java.util.List;
027import org.apache.hadoop.hbase.Cell;
028import org.apache.hadoop.hbase.CellComparator;
029import org.apache.hadoop.hbase.CompareOperator;
030import org.apache.hadoop.hbase.HBaseTestingUtil;
031import org.apache.hadoop.hbase.HConstants;
032import org.apache.hadoop.hbase.TableName;
033import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
034import org.apache.hadoop.hbase.client.Durability;
035import org.apache.hadoop.hbase.client.Put;
036import org.apache.hadoop.hbase.client.RegionInfo;
037import org.apache.hadoop.hbase.client.RegionInfoBuilder;
038import org.apache.hadoop.hbase.client.Scan;
039import org.apache.hadoop.hbase.client.TableDescriptor;
040import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
041import org.apache.hadoop.hbase.regionserver.HRegion;
042import org.apache.hadoop.hbase.regionserver.InternalScanner;
043import org.apache.hadoop.hbase.testclassification.FilterTests;
044import org.apache.hadoop.hbase.testclassification.SmallTests;
045import org.apache.hadoop.hbase.util.Bytes;
046import org.junit.jupiter.api.AfterEach;
047import org.junit.jupiter.api.BeforeEach;
048import org.junit.jupiter.api.Tag;
049import org.junit.jupiter.api.Test;
050import org.slf4j.Logger;
051import org.slf4j.LoggerFactory;
052
053/**
054 * Test qualifierFilter with empty qualifier column
055 */
056@Tag(FilterTests.TAG)
057@Tag(SmallTests.TAG)
058public class TestQualifierFilterWithEmptyQualifier {
059
060  private final static Logger LOG =
061    LoggerFactory.getLogger(TestQualifierFilterWithEmptyQualifier.class);
062  private final static HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
063  private HRegion region;
064
065  private static final byte[][] ROWS = { Bytes.toBytes("testRowOne-0"),
066    Bytes.toBytes("testRowOne-1"), Bytes.toBytes("testRowOne-2"), Bytes.toBytes("testRowOne-3") };
067  private static final byte[] FAMILY = Bytes.toBytes("testFamily");
068  private static final byte[][] QUALIFIERS =
069    { HConstants.EMPTY_BYTE_ARRAY, Bytes.toBytes("testQualifier") };
070  private static final byte[] VALUE = Bytes.toBytes("testValueOne");
071  private long numRows = (long) ROWS.length;
072
073  @BeforeEach
074  public void setUp() throws Exception {
075    TableDescriptor htd =
076      TableDescriptorBuilder.newBuilder(TableName.valueOf("TestQualifierFilter"))
077        .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(FAMILY).build()).build();
078    RegionInfo info = RegionInfoBuilder.newBuilder(htd.getTableName()).build();
079    this.region = HBaseTestingUtil.createRegionAndWAL(info, TEST_UTIL.getDataTestDir(),
080      TEST_UTIL.getConfiguration(), htd);
081
082    // Insert data
083    for (byte[] ROW : ROWS) {
084      Put p = new Put(ROW);
085      p.setDurability(Durability.SKIP_WAL);
086      for (byte[] QUALIFIER : QUALIFIERS) {
087        p.addColumn(FAMILY, QUALIFIER, VALUE);
088      }
089      this.region.put(p);
090    }
091
092    // Flush
093    this.region.flush(true);
094  }
095
096  @AfterEach
097  public void tearDown() throws Exception {
098    HBaseTestingUtil.closeRegionAndWAL(region);
099  }
100
101  @Test
102  public void testQualifierFilterWithEmptyColumn() throws IOException {
103    long colsPerRow = 2;
104    long expectedKeys = colsPerRow / 2;
105    Filter f = new QualifierFilter(CompareOperator.EQUAL, new BinaryComparator(QUALIFIERS[0]));
106    Scan s = new Scan();
107    s.setFilter(f);
108    verifyScanNoEarlyOut(s, this.numRows, expectedKeys);
109
110    expectedKeys = colsPerRow / 2;
111    f = new QualifierFilter(CompareOperator.EQUAL, new BinaryComparator(QUALIFIERS[1]));
112    s = new Scan();
113    s.setFilter(f);
114    verifyScanNoEarlyOut(s, this.numRows, expectedKeys);
115
116    expectedKeys = colsPerRow / 2;
117    f = new QualifierFilter(CompareOperator.GREATER, new BinaryComparator(QUALIFIERS[0]));
118    s = new Scan();
119    s.setFilter(f);
120    verifyScanNoEarlyOut(s, this.numRows, expectedKeys);
121
122    expectedKeys = colsPerRow;
123    f = new QualifierFilter(CompareOperator.GREATER_OR_EQUAL, new BinaryComparator(QUALIFIERS[0]));
124    s = new Scan();
125    s.setFilter(f);
126    verifyScanNoEarlyOut(s, this.numRows, expectedKeys);
127  }
128
129  private void verifyScanNoEarlyOut(Scan s, long expectedRows, long expectedKeys)
130    throws IOException {
131    InternalScanner scanner = this.region.getScanner(s);
132    List<Cell> results = new ArrayList<>();
133    int i = 0;
134    for (boolean done = true; done; i++) {
135      done = scanner.next(results);
136      Arrays.sort(results.toArray(new Cell[results.size()]), CellComparator.getInstance());
137      LOG.info("counter=" + i + ", " + results);
138      if (results.isEmpty()) {
139        break;
140      }
141      assertTrue(expectedRows > i, "Scanned too many rows! Only expected " + expectedRows
142        + " total but already scanned " + (i + 1));
143      assertEquals(expectedKeys, results.size(),
144        "Expected " + expectedKeys + " keys per row but " + "returned " + results.size());
145      results.clear();
146    }
147    assertEquals(i, expectedRows, "Expected " + expectedRows + " rows but scanned " + i + " rows");
148  }
149}