Index: src/main/java/org/apache/hadoop/hbase/KeyValue.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/KeyValue.java (revision 1470106) +++ src/main/java/org/apache/hadoop/hbase/KeyValue.java (working copy) @@ -1883,6 +1883,21 @@ } /** + * Similar to {@link #createLastOnRow(byte[], int, int, byte[], int, int, byte[], int, int)} + * but create the last ket on the row of this KV + * (the value part of the returned KV is always empty). Used in creating + * "fake keys" to skip the row we already know is not in the file. + * @return the last key on the row of the given key-value pair + */ + public KeyValue createLastOnRoW() { + return new KeyValue( + bytes, getRowOffset(), getRowLength(), + null, 0, 0, + null, 0, 0, + HConstants.OLDEST_TIMESTAMP, Type.Minimum, null, 0, 0); + } + + /** * Creates the first KV with the row/family/qualifier of this KV and the * given timestamp. Uses the "maximum" KV type that guarantees that the new * KV is the lowest possible for this combination of row, family, qualifier, Index: src/main/java/org/apache/hadoop/hbase/regionserver/StoreFileScanner.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/regionserver/StoreFileScanner.java (revision 1470106) +++ src/main/java/org/apache/hadoop/hbase/regionserver/StoreFileScanner.java (working copy) @@ -276,10 +276,15 @@ } boolean haveToSeek = true; + boolean skipRow = false; if (useBloom) { - // check ROWCOL Bloom filter first. - if (reader.getBloomFilterType() == StoreFile.BloomType.ROWCOL) { + // check ROW or ROWCOL Bloom filter first. + if (reader.getBloomFilterType() == StoreFile.BloomType.ROW) { haveToSeek = reader.passesGeneralBloomFilter(kv.getBuffer(), + kv.getRowOffset(), kv.getRowLength(), null, 0, 0); + skipRow = !haveToSeek; + } else if (reader.getBloomFilterType() == StoreFile.BloomType.ROWCOL) { + haveToSeek = reader.passesGeneralBloomFilter(kv.getBuffer(), kv.getRowOffset(), kv.getRowLength(), kv.getBuffer(), kv.getQualifierOffset(), kv.getQualifierLength()); } else if (this.matcher != null && !matcher.hasNullColumnInQuery() && @@ -320,12 +325,12 @@ // Multi-column Bloom filter optimization. // Create a fake key/value, so that this scanner only bubbles up to the top - // of the KeyValueHeap in StoreScanner after we scanned this row/column in + // of the KeyValueHeap in StoreScanner after we scanned this row or row/column in // all other store files. The query matcher will then just skip this fake - // key/value and the store scanner will progress to the next column. This + // key/value and the store scanner will progress to the next row/column. This // is obviously not a "real real" seek, but unlike the fake KV earlier in // this method, we want this to be propagated to ScanQueryMatcher. - cur = kv.createLastOnRowCol(); + cur = skipRow ? kv.createLastOnRoW() : kv.createLastOnRowCol(); realSeekDone = true; return true;