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.assertFalse; 022import static org.junit.jupiter.api.Assertions.assertNotNull; 023import static org.junit.jupiter.api.Assertions.assertNull; 024import static org.junit.jupiter.api.Assertions.assertTrue; 025 026import org.apache.hadoop.hbase.Cell; 027import org.apache.hadoop.hbase.KeyValue; 028import org.apache.hadoop.hbase.KeyValueUtil; 029import org.apache.hadoop.hbase.testclassification.FilterTests; 030import org.apache.hadoop.hbase.testclassification.SmallTests; 031import org.apache.hadoop.hbase.util.Bytes; 032import org.junit.jupiter.api.BeforeEach; 033import org.junit.jupiter.api.Tag; 034import org.junit.jupiter.api.Test; 035 036@Tag(FilterTests.TAG) 037@Tag(SmallTests.TAG) 038public class TestPrefixFilter { 039 040 Filter mainFilter; 041 static final char FIRST_CHAR = 'a'; 042 static final char LAST_CHAR = 'e'; 043 static final String HOST_PREFIX = "org.apache.site-"; 044 045 @BeforeEach 046 public void setUp() throws Exception { 047 this.mainFilter = new PrefixFilter(Bytes.toBytes(HOST_PREFIX)); 048 } 049 050 @Test 051 public void testPrefixOnRow() throws Exception { 052 prefixRowTests(mainFilter); 053 } 054 055 @Test 056 public void testPrefixOnRowInsideWhileMatchRow() throws Exception { 057 prefixRowTests(new WhileMatchFilter(this.mainFilter), true); 058 } 059 060 @Test 061 public void testSerialization() throws Exception { 062 // Decompose mainFilter to bytes. 063 byte[] buffer = mainFilter.toByteArray(); 064 065 // Recompose filter. 066 Filter newFilter = PrefixFilter.parseFrom(buffer); 067 068 // Ensure the serialization preserved the filter by running all test. 069 prefixRowTests(newFilter); 070 } 071 072 private void prefixRowTests(Filter filter) throws Exception { 073 prefixRowTests(filter, false); 074 } 075 076 private void prefixRowTests(Filter filter, boolean lastFilterAllRemaining) throws Exception { 077 for (char c = FIRST_CHAR; c <= LAST_CHAR; c++) { 078 byte[] t = createRow(c); 079 assertFalse(filter.filterRowKey(KeyValueUtil.createFirstOnRow(t)), 080 "Failed with character " + c); 081 assertFalse(filter.filterAllRemaining()); 082 } 083 String yahooSite = "com.yahoo.www"; 084 byte[] yahooSiteBytes = Bytes.toBytes(yahooSite); 085 KeyValue yahooSiteCell = KeyValueUtil.createFirstOnRow(yahooSiteBytes); 086 assertFalse(filter.filterRowKey(yahooSiteCell), "Failed with character " + yahooSite); 087 assertEquals(Filter.ReturnCode.SEEK_NEXT_USING_HINT, filter.filterCell(yahooSiteCell)); 088 assertEquals(lastFilterAllRemaining, filter.filterAllRemaining()); 089 } 090 091 private byte[] createRow(final char c) { 092 return Bytes.toBytes(HOST_PREFIX + Character.toString(c)); 093 } 094 095 @Test 096 public void shouldProvideHintWhenKeyBefore() { 097 byte[] prefix = Bytes.toBytes("gg"); 098 PrefixFilter filter = new PrefixFilter(prefix); 099 100 KeyValue cell = KeyValueUtil.createFirstOnRow(Bytes.toBytes("aa")); 101 102 // Should include this row so that filterCell() will be invoked. 103 assertFalse(filter.filterRowKey(cell)); 104 assertEquals(Filter.ReturnCode.SEEK_NEXT_USING_HINT, filter.filterCell(cell)); 105 Cell actualCellHint = filter.getNextCellHint(cell); 106 assertNotNull(actualCellHint); 107 Cell expectedCellHint = KeyValueUtil.createFirstOnRow(prefix); 108 assertEquals(expectedCellHint, actualCellHint); 109 assertFalse(filter.filterAllRemaining()); 110 assertTrue(filter.filterRow()); 111 } 112 113 @Test 114 public void shouldProvideHintWhenKeyBeforeAndShorter() { 115 byte[] prefix = Bytes.toBytes("gggg"); 116 PrefixFilter filter = new PrefixFilter(prefix); 117 118 KeyValue cell = KeyValueUtil.createFirstOnRow(Bytes.toBytes("aa")); 119 120 // Should include this row so that filterCell() will be invoked. 121 assertFalse(filter.filterRowKey(cell)); 122 assertEquals(Filter.ReturnCode.SEEK_NEXT_USING_HINT, filter.filterCell(cell)); 123 Cell actualCellHint = filter.getNextCellHint(cell); 124 assertNotNull(actualCellHint); 125 Cell expectedCellHint = KeyValueUtil.createFirstOnRow(prefix); 126 assertEquals(expectedCellHint, actualCellHint); 127 assertFalse(filter.filterAllRemaining()); 128 assertTrue(filter.filterRow()); 129 } 130 131 @Test 132 public void shouldIncludeWhenKeyMatches() { 133 PrefixFilter filter = new PrefixFilter(Bytes.toBytes("gg")); 134 135 KeyValue matchingCell = KeyValueUtil.createFirstOnRow(Bytes.toBytes("gg")); 136 137 assertFalse(filter.filterRowKey(matchingCell)); 138 assertEquals(Filter.ReturnCode.INCLUDE, filter.filterCell(matchingCell)); 139 assertFalse(filter.filterAllRemaining()); 140 assertFalse(filter.filterRow()); 141 } 142 143 @Test 144 public void shouldReturnNextRowWhenKeyAfter() { 145 PrefixFilter filter = new PrefixFilter(Bytes.toBytes("gg")); 146 147 KeyValue afterCell = KeyValueUtil.createFirstOnRow(Bytes.toBytes("pp")); 148 149 assertTrue(filter.filterRowKey(afterCell)); 150 assertEquals(Filter.ReturnCode.NEXT_ROW, filter.filterCell(afterCell)); 151 assertTrue(filter.filterAllRemaining()); 152 assertTrue(filter.filterRow()); 153 } 154 155 @Test 156 public void shouldProvideHintWhenKeyBeforeReversed() { 157 PrefixFilter filter = new PrefixFilter(Bytes.toBytes("aa")); 158 filter.setReversed(true); 159 160 KeyValue cell = KeyValueUtil.createFirstOnRow(Bytes.toBytes("x")); 161 162 // Should include this row so that filterCell() will be invoked. 163 assertFalse(filter.filterRowKey(cell)); 164 assertEquals(Filter.ReturnCode.SEEK_NEXT_USING_HINT, filter.filterCell(cell)); 165 Cell actualCellHint = filter.getNextCellHint(cell); 166 assertNotNull(actualCellHint); 167 Cell expectedCellHint = KeyValueUtil.createFirstOnRow(Bytes.toBytes("ab")); 168 assertEquals(expectedCellHint, actualCellHint); 169 assertFalse(filter.filterAllRemaining()); 170 assertTrue(filter.filterRow()); 171 } 172 173 @Test 174 public void hintShouldIncreaseLastNonMaxByteWhenReversed() { 175 PrefixFilter filter = new PrefixFilter(new byte[] { 'a', 'a', Byte.MAX_VALUE }); 176 filter.setReversed(true); 177 178 KeyValue cell = KeyValueUtil.createFirstOnRow(Bytes.toBytes("x")); 179 180 // Should include this row so that filterCell() will be invoked. 181 assertFalse(filter.filterRowKey(cell)); 182 assertEquals(Filter.ReturnCode.SEEK_NEXT_USING_HINT, filter.filterCell(cell)); 183 Cell actualCellHint = filter.getNextCellHint(cell); 184 assertNotNull(actualCellHint); 185 Cell expectedCellHint = KeyValueUtil.createFirstOnRow(new byte[] { 'a', 'b', Byte.MAX_VALUE }); 186 assertEquals(expectedCellHint, actualCellHint); 187 assertFalse(filter.filterAllRemaining()); 188 assertTrue(filter.filterRow()); 189 } 190 191 @Test 192 public void shouldIncludeWhenKeyMatchesReversed() { 193 PrefixFilter filter = new PrefixFilter(Bytes.toBytes("aa")); 194 filter.setReversed(true); 195 196 KeyValue matchingCell = KeyValueUtil.createFirstOnRow(Bytes.toBytes("aa")); 197 198 assertFalse(filter.filterRowKey(matchingCell)); 199 assertEquals(Filter.ReturnCode.INCLUDE, filter.filterCell(matchingCell)); 200 assertFalse(filter.filterAllRemaining()); 201 assertFalse(filter.filterRow()); 202 } 203 204 @Test 205 public void shouldReturnNextRowWhenKeyAfterReversed() { 206 PrefixFilter filter = new PrefixFilter(Bytes.toBytes("dd")); 207 filter.setReversed(true); 208 209 KeyValue cell = KeyValueUtil.createFirstOnRow(Bytes.toBytes("aa")); 210 211 assertTrue(filter.filterRowKey(cell)); 212 assertEquals(Filter.ReturnCode.NEXT_ROW, filter.filterCell(cell)); 213 assertTrue(filter.filterAllRemaining()); 214 assertTrue(filter.filterRow()); 215 } 216 217 @Test 218 public void hintShouldNotIncreaseMaxBytesWhenReversed() { 219 PrefixFilter filter = 220 new PrefixFilter(new byte[] { Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE }); 221 filter.setReversed(true); 222 223 KeyValue cell = KeyValueUtil.createFirstOnRow(Bytes.toBytes("x")); 224 225 assertTrue(filter.filterRowKey(cell)); 226 assertEquals(Filter.ReturnCode.NEXT_ROW, filter.filterCell(cell)); 227 Cell actualCellHint = filter.getNextCellHint(cell); 228 assertNotNull(actualCellHint); 229 Cell expectedCellHint = 230 KeyValueUtil.createFirstOnRow(new byte[] { Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE }); 231 assertEquals(expectedCellHint, actualCellHint); 232 assertTrue(filter.filterAllRemaining()); 233 assertTrue(filter.filterRow()); 234 } 235 236 @Test 237 public void shouldNotThrowWhenCreatedWithNullPrefix() { 238 PrefixFilter filter = new PrefixFilter(null); 239 KeyValue cell = KeyValueUtil.createFirstOnRow(Bytes.toBytes("doesNotMatter")); 240 241 assertNull(filter.getNextCellHint(cell)); 242 filter.setReversed(true); 243 assertNull(filter.getNextCellHint(cell)); 244 } 245 246 @Test 247 public void shouldNotThrowWhenCreatedWithEmptyByteArrayPrefix() { 248 byte[] emptyPrefix = {}; 249 KeyValue emptyPrefixCell = KeyValueUtil.createFirstOnRow(emptyPrefix); 250 KeyValue cell = KeyValueUtil.createFirstOnRow(Bytes.toBytes("doesNotMatter")); 251 252 PrefixFilter filter = new PrefixFilter(emptyPrefix); 253 254 Cell forwardNextCellHint = filter.getNextCellHint(cell); 255 assertNotNull(forwardNextCellHint); 256 assertEquals(emptyPrefixCell, forwardNextCellHint); 257 258 filter.setReversed(true); 259 Cell reverseNextCellHint = filter.getNextCellHint(cell); 260 assertNotNull(reverseNextCellHint); 261 assertEquals(emptyPrefixCell, reverseNextCellHint); 262 } 263}