Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@
@InterfaceStability.Evolving
public class FuzzyRowFilter extends FilterBase {
private static final boolean UNSAFE_UNALIGNED = UnsafeAvailChecker.unaligned();

// the wildcard byte is 1 on the user side. but the filter converts it internally
// in preprocessMask. This was changed in HBASE-15676 due to a bug with using 0.
// in v1, the 1 byte gets converted to 0
// in v2, the 1 byte gets converted to 2.
// we support both here to ensure backwards compatibility between client and server
static final byte V1_PROCESSED_WILDCARD_MASK = 0;
static final byte V2_PROCESSED_WILDCARD_MASK = 2;

private final byte processedWildcardMask;
private List<Pair<byte[], byte[]>> fuzzyKeysData;
private boolean done = false;

Expand All @@ -73,7 +83,18 @@ public class FuzzyRowFilter extends FilterBase {
*/
private RowTracker tracker;

// this client side constructor ensures that all client-constructed
// FuzzyRowFilters use the new v2 mask.
public FuzzyRowFilter(List<Pair<byte[], byte[]>> fuzzyKeysData) {
this(fuzzyKeysData, V2_PROCESSED_WILDCARD_MASK);
}

// This constructor is only used internally here, when parsing from protos on the server side.
// It exists to enable seamless migration from v1 to v2.
// Additionally used in tests, but never used on client side.
FuzzyRowFilter(List<Pair<byte[], byte[]>> fuzzyKeysData, byte processedWildcardMask) {
this.processedWildcardMask = processedWildcardMask;

List<Pair<byte[], byte[]>> fuzzyKeyDataCopy = new ArrayList<>(fuzzyKeysData.size());

for (Pair<byte[], byte[]> aFuzzyKeysData : fuzzyKeysData) {
Expand All @@ -88,7 +109,7 @@ public FuzzyRowFilter(List<Pair<byte[], byte[]>> fuzzyKeysData) {
p.setFirst(Arrays.copyOf(aFuzzyKeysData.getFirst(), aFuzzyKeysData.getFirst().length));
p.setSecond(Arrays.copyOf(aFuzzyKeysData.getSecond(), aFuzzyKeysData.getSecond().length));

// update mask ( 0 -> -1 (0xff), 1 -> 2)
// update mask ( 0 -> -1 (0xff), 1 -> [0 or 2 depending on processedWildcardMask value])
p.setSecond(preprocessMask(p.getSecond()));
preprocessSearchKey(p);

Expand All @@ -106,7 +127,7 @@ private void preprocessSearchKey(Pair<byte[], byte[]> p) {
byte[] mask = p.getSecond();
for (int i = 0; i < mask.length; i++) {
// set non-fixed part of a search key to 0.
if (mask[i] == 2) {
if (mask[i] == processedWildcardMask) {
key[i] = 0;
}
}
Expand All @@ -127,15 +148,15 @@ private byte[] preprocessMask(byte[] mask) {
if (mask[i] == 0) {
mask[i] = -1; // 0 -> -1
} else if (mask[i] == 1) {
mask[i] = 2;// 1 -> 2
mask[i] = processedWildcardMask;// 1 -> 0 or 2 depending on mask version
}
}
return mask;
}

private boolean isPreprocessedMask(byte[] mask) {
for (int i = 0; i < mask.length; i++) {
if (mask[i] != -1 && mask[i] != 2) {
if (mask[i] != -1 && mask[i] != processedWildcardMask) {
return false;
}
}
Expand All @@ -149,10 +170,7 @@ public ReturnCode filterKeyValue(Cell c) {
for (int i = startIndex; i < size + startIndex; i++) {
final int index = i % size;
Pair<byte[], byte[]> fuzzyData = fuzzyKeysData.get(index);
// This shift is idempotent - always end up with 0 and -1 as mask values.
for (int j = 0; j < fuzzyData.getSecond().length; j++) {
fuzzyData.getSecond()[j] >>= 2;
}
idempotentMaskShift(fuzzyData.getSecond());
SatisfiesCode satisfiesCode =
satisfies(isReversed(), c.getRowArray(), c.getRowOffset(), c.getRowLength(),
fuzzyData.getFirst(), fuzzyData.getSecond());
Expand All @@ -165,7 +183,15 @@ public ReturnCode filterKeyValue(Cell c) {
lastFoundIndex = -1;

return ReturnCode.SEEK_NEXT_USING_HINT;
}

static void idempotentMaskShift(byte[] mask) {
// This shift is idempotent - always end up with 0 and -1 as mask values.
// This works regardless of mask version, because both 0 >> 2 and 2 >> 2
// result in 0.
for (int j = 0; j < mask.length; j++) {
mask[j] >>= 2;
}
}

@Override
Expand Down Expand Up @@ -257,7 +283,9 @@ public boolean filterAllRemaining() {
*/
@Override
public byte[] toByteArray() {
FilterProtos.FuzzyRowFilter.Builder builder = FilterProtos.FuzzyRowFilter.newBuilder();
FilterProtos.FuzzyRowFilter.Builder builder = FilterProtos.FuzzyRowFilter
.newBuilder()
.setIsMaskV2(processedWildcardMask == V2_PROCESSED_WILDCARD_MASK);
for (Pair<byte[], byte[]> fuzzyData : fuzzyKeysData) {
BytesBytesPair.Builder bbpBuilder = BytesBytesPair.newBuilder();
bbpBuilder.setFirst(ByteStringer.wrap(fuzzyData.getFirst()));
Expand Down Expand Up @@ -288,7 +316,10 @@ public static FuzzyRowFilter parseFrom(final byte[] pbBytes) throws Deserializat
byte[] keyMeta = current.getSecond().toByteArray();
fuzzyKeysData.add(new Pair<byte[], byte[]>(keyBytes, keyMeta));
}
return new FuzzyRowFilter(fuzzyKeysData);
byte processedWildcardMask = proto.hasIsMaskV2() && proto.getIsMaskV2()
? V2_PROCESSED_WILDCARD_MASK
: V1_PROCESSED_WILDCARD_MASK;
return new FuzzyRowFilter(fuzzyKeysData, processedWildcardMask);
}

@Override
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions hbase-protocol/src/main/protobuf/Filter.proto
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ message FirstKeyValueMatchingQualifiersFilter {

message FuzzyRowFilter {
repeated BytesBytesPair fuzzy_keys_data = 1;
optional bool is_mask_v2 = 2;
}

message InclusiveStopFilter {
Expand Down
Loading