blob: 8234e2efcdd3412b4bd4d821b91f9f247613d80a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.phoenix.filter;
import java.io.IOException;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.filter.Filter.ReturnCode;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.phoenix.schema.PDatum;
import org.apache.phoenix.schema.RowKeySchema.RowKeySchemaBuilder;
import org.apache.phoenix.schema.SortOrder;
import org.apache.phoenix.schema.types.PChar;
import org.apache.phoenix.schema.types.PDataType;
import org.apache.phoenix.schema.types.PVarchar;
import org.apache.phoenix.util.ByteUtil;
import junit.framework.TestCase;
public class DistinctPrefixFilterTest extends TestCase {
private DistinctPrefixFilter createFilter(int[] widths, int prefixLength) {
RowKeySchemaBuilder builder = new RowKeySchemaBuilder(widths.length);
for (final int width : widths) {
builder.addField(
new PDatum() {
@Override
public boolean isNullable() {
return width <= 0;
}
@Override
public PDataType<?> getDataType() {
return width <= 0 ? PVarchar.INSTANCE : PChar.INSTANCE;
}
@Override
public Integer getMaxLength() {
return width <= 0 ? null : width;
}
@Override
public Integer getScale() {
return null;
}
@Override
public SortOrder getSortOrder() {
return SortOrder.getDefault();
}
}, width <= 0, SortOrder.getDefault());
}
return new DistinctPrefixFilter(builder.build(), prefixLength);
}
private void assertInclude(String next, Filter f) throws IOException {
assertInclude(Bytes.toBytes(next), f);
}
private void assertInclude(byte[] next, Filter f) throws IOException {
Cell c = new KeyValue(next, ByteUtil.EMPTY_BYTE_ARRAY, ByteUtil.EMPTY_BYTE_ARRAY, 0, ByteUtil.EMPTY_BYTE_ARRAY);
assertTrue(f.filterKeyValue(c) == ReturnCode.INCLUDE);
assertFalse(f.filterAllRemaining());
}
private void assertSeekAndHint(String next, Filter f, String rowHint) throws IOException {
assertSeekAndHint(next, f, rowHint, false);
}
private void assertSeekAndHint(String next, Filter f, String rowHint, boolean filterAll) throws IOException {
assertSeekAndHint(Bytes.toBytes(next), f, Bytes.toBytes(rowHint), filterAll);
}
private void assertSeekAndHint(byte[] next, Filter f, byte[] rowHint, boolean filterAll) throws IOException {
Cell c = new KeyValue(next, ByteUtil.EMPTY_BYTE_ARRAY, ByteUtil.EMPTY_BYTE_ARRAY, 0, ByteUtil.EMPTY_BYTE_ARRAY);
assertTrue(f.filterKeyValue(c) == ReturnCode.SEEK_NEXT_USING_HINT);
Cell h = f.getNextCellHint(c);
byte[] hintBytes = rowHint;
assertTrue(Bytes.equals(hintBytes, 0, hintBytes.length, h.getRowArray(), h.getRowOffset(), h.getRowLength()));
assertEquals(filterAll, f.filterAllRemaining());
}
public void testSingleFixedWidth() throws Exception {
Filter f = createFilter(new int[]{3}, 1);
assertInclude("000", f);
assertInclude("001", f);
assertSeekAndHint("001", f, "002");
assertInclude("003", f);
assertInclude("004", f);
assertInclude("005", f);
assertSeekAndHint("005", f, "006");
f = createFilter(new int[]{3}, 1);
f.setReversed(true);
assertInclude("005", f);
assertInclude("004", f);
assertSeekAndHint(new byte[]{'0','0','4'}, f, new byte[]{'0','0','4'}, false);
assertInclude("003", f);
assertInclude("002", f);
assertInclude("001", f);
assertSeekAndHint(new byte[]{'0','0','1'}, f, new byte[]{'0','0','1'}, false);
}
public void testMultiFixedWidth() throws Exception {
Filter f = createFilter(new int[]{5,4}, 1);
assertInclude("00000aaaa", f);
assertInclude("00001aaaa", f);
assertSeekAndHint("00001aaaa", f, "00002");
assertInclude("00003aaaa", f);
assertInclude("00004aaaa", f);
assertInclude("00005aaaa", f);
assertSeekAndHint("00005aaaa", f, "00006");
f = createFilter(new int[]{5,4}, 2);
assertInclude("00000aaaa", f);
assertInclude("00001aaaa", f);
assertSeekAndHint("00001aaaa", f, "00001aaab");
assertInclude("00003aaaa", f);
assertInclude("00004aaaa", f);
assertInclude("00005aaaa", f);
assertSeekAndHint("00005aaaa", f, "00005aaab");
f = createFilter(new int[]{3,2}, 1);
f.setReversed(true);
assertInclude("005aa", f);
assertInclude("004aa", f);
assertSeekAndHint(new byte[]{'0','0','4','a','a'}, f, new byte[]{'0','0','4'}, false);
assertInclude("003aa", f);
assertInclude("002aa", f);
assertInclude("001aa", f);
assertSeekAndHint(new byte[]{'0','0','1','a','a'}, f, new byte[]{'0','0','1'}, false);
f = createFilter(new int[]{3,2}, 2);
f.setReversed(true);
assertInclude("005bb", f);
assertInclude("004bb", f);
assertInclude("003bb", f);
assertSeekAndHint(new byte[]{'0','0','3','b','b'}, f, new byte[]{'0','0','3','b','b'}, false);
assertInclude("003ba", f);
assertInclude("002bb", f);
assertInclude("001bb", f);
assertSeekAndHint(new byte[]{'0','0','1','b','b'}, f, new byte[]{'0','0','1','b','b'}, false);
}
public void testSingleVariableWidth() throws Exception {
Filter f = createFilter(new int[]{-5}, 1);
assertInclude("00000", f);
assertInclude("00001", f);
assertSeekAndHint("00001", f, "00001\01");
assertInclude("00003", f);
assertInclude("00004", f);
assertInclude("00005", f);
assertSeekAndHint("00005", f, "00005\01");
}
public void testVariableWithNull() throws Exception {
Filter f = createFilter(new int[]{-2,-2}, 1);
assertInclude("\00aa", f);
assertSeekAndHint("\00aa", f, "\01");
assertSeekAndHint("\00aa", f, "\01");
f = createFilter(new int[]{-2,-2}, 2);
assertInclude("\00\00", f);
assertSeekAndHint("\00\00", f, "\00\00\01");
assertSeekAndHint("\00\00", f, "\00\00\01");
}
public void testMultiVariableWidth() throws Exception {
Filter f = createFilter(new int[]{-5,-4}, 1);
assertInclude("00000\00aaaa", f);
assertInclude("00001\00aaaa", f);
assertSeekAndHint("00001\00aaaa", f, "00001\01");
assertInclude("00003\00aaaa", f);
assertInclude("00004\00aaaa", f);
assertInclude("00005\00aaaa", f);
assertSeekAndHint("00005\00aaaa", f, "00005\01");
f = createFilter(new int[]{-5,-4}, 2);
assertInclude("00000\00aaaa", f);
assertInclude("00001\00aaaa", f);
assertSeekAndHint("00001\00aaaa", f, "00001\00aaaa\01");
assertInclude("00003\00aaaa", f);
assertInclude("00004\00aaaa", f);
assertInclude("00005\00aaaa", f);
assertSeekAndHint("00005\00aaaa", f, "00005\00aaaa\01");
f = createFilter(new int[]{-3,-2}, 1);
f.setReversed(true);
assertInclude("005\00aa", f);
assertInclude("004\00aa", f);
assertSeekAndHint(new byte[]{'0','0','4', 0, 'a', 'a'}, f,
new byte[] {'0','0','4'}, false);
f = createFilter(new int[]{-3,-2}, 2);
f.setReversed(true);
assertInclude("005\00bb", f);
assertInclude("004\00bb", f);
assertSeekAndHint(new byte[]{'0','0','4', 0, 'b', 'b'}, f,
new byte[]{'0','0','4', 0, 'b', 'b'}, false);
}
public void testFixedAfterVariable() throws Exception {
Filter f = createFilter(new int[]{-5,4}, 1);
assertInclude("00000\00aaaa", f);
assertInclude("00001\00aaaa", f);
assertSeekAndHint("00001\00aaaa", f, "00001\01");
assertInclude("00003\00aaaa", f);
assertInclude("00004\00aaaa", f);
assertInclude("00005\00aaaa", f);
assertSeekAndHint("00005\00aaaa", f, "00005\01");
f = createFilter(new int[]{-5,4}, 2);
assertInclude("00000\00aaaa", f);
assertInclude("00001\00aaaa", f);
assertSeekAndHint("00001\00aaaa", f, "00001\00aaab");
assertInclude("00003\00aaaa", f);
assertInclude("00004\00aaaa", f);
assertInclude("00005\00aaaa", f);
assertSeekAndHint("00005\00aaaa", f, "00005\00aaab");
}
public void testVariableAfterFixed() throws Exception {
Filter f = createFilter(new int[]{5,-4}, 1);
assertInclude("00000aaaa", f);
assertInclude("00001aaaa", f);
assertSeekAndHint("00001aaaa", f, "00002");
assertInclude("00003aaaa", f);
assertInclude("00004aaaa", f);
assertInclude("00005aaaa", f);
assertSeekAndHint("00005aaaa", f, "00006");
f = createFilter(new int[]{5,-4}, 2);
assertInclude("00000aaaa", f);
assertInclude("00001aaaa", f);
assertSeekAndHint("00001aaaa", f, "00001aaaa\01");
assertInclude("00003aaaa", f);
assertInclude("00004aaaa", f);
assertInclude("00005aaaa", f);
assertSeekAndHint("00005aaaa", f, "00005aaaa\01");
}
public void testNoNextKey() throws Exception {
Filter f = createFilter(new int[]{2,2}, 1);
assertInclude("00cc", f);
assertInclude(new byte[]{-1,-1,20,20}, f);
// make sure we end the scan when we cannot increase a fixed length prefix
assertSeekAndHint(new byte[]{-1,-1,20,20}, f, new byte[]{-1,-1}, true);
assertSeekAndHint(new byte[]{-1,-1,20,20}, f, new byte[]{-1,-1}, true);
f = createFilter(new int[]{2,2}, 1);
f.setReversed(true);
assertInclude(new byte[]{0,0,1,1}, f);
assertSeekAndHint(new byte[]{0,0,1,1}, f, new byte[]{0,0}, false);
assertSeekAndHint(new byte[]{0,0,1,1}, f, new byte[]{0,0}, false);
}
}