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}