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