diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/CellUtil.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/CellUtil.java index ff6ad90..cb538bc 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/CellUtil.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/CellUtil.java @@ -18,6 +18,8 @@ package org.apache.hadoop.hbase; +import static org.apache.hadoop.hbase.HConstants.EMPTY_BYTE_ARRAY; + import java.io.DataOutputStream; import java.io.IOException; import java.nio.ByteBuffer; @@ -26,9 +28,9 @@ import java.util.List; import java.util.Map.Entry; import java.util.NavigableMap; +import org.apache.hadoop.hbase.KeyValue.Type; import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.classification.InterfaceStability; -import org.apache.hadoop.hbase.KeyValue.Type; import org.apache.hadoop.hbase.io.HeapSize; import org.apache.hadoop.hbase.util.ByteBufferUtils; import org.apache.hadoop.hbase.util.ByteRange; @@ -943,4 +945,335 @@ public final class CellUtil { public static boolean matchingType(Cell a, Cell b) { return a.getTypeByte() == b.getTypeByte(); } + + /** + * Create a Cell that is smaller than all other possible Cells for the given + * Cell's rk:cf and passed qualifier. + * + * @param cell + * @param qArray + * @param qoffest + * @param qlength + * @return Last possible Cell on passed Cell's rk:cf and passed qualifier. + */ + public static Cell createFirstOnRowCol(final Cell cell, byte[] qArray, + int qoffest, int qlength) { + return new FirstOnRowColCell(cell.getRowArray(), cell.getRowOffset(), + cell.getRowLength(), cell.getFamilyArray(), cell.getFamilyOffset(), + cell.getFamilyLength(), qArray, qoffest, qlength); + } + + /** + * Create a Cell that is larger than all other possible Cells for the given + * Cell's rk:cf:q. Used in creating "fake keys" for the multi-column Bloom + * filter optimization to skip the row/column we already know is not in the + * file. + * + * @param cell + * @return Last possible Cell on passed Cell's rk:cf:q. + */ + public static Cell createLastOnRowCol(final Cell cell) { + return new LastOnRowColCell(cell.getRowArray(), cell.getRowOffset(), + cell.getRowLength(), cell.getFamilyArray(), cell.getFamilyOffset(), + cell.getFamilyLength(), cell.getQualifierArray(), + cell.getQualifierOffset(), cell.getQualifierLength()); + } + + @InterfaceAudience.Private + private static class FirstOnRowColCell extends FirstOnRowCell { + private final byte[] fArray; + private final int foffset; + private final byte flength; + private final byte[] qArray; + private final int qoffset; + private final int qlength; + + public FirstOnRowColCell(byte[] rArray, int roffset, short rlength, + byte[] fArray, int foffset, byte flength, byte[] qArray, int qoffset, + int qlength) { + super(rArray, roffset, rlength); + this.fArray = fArray; + this.foffset = foffset; + this.flength = flength; + this.qArray = qArray; + this.qoffset = qoffset; + this.qlength = qlength; + } + + @Override + public byte[] getFamilyArray() { + return this.fArray; + } + + @Override + public int getFamilyOffset() { + return this.foffset; + } + + @Override + public byte getFamilyLength() { + return this.flength; + } + + @Override + public byte[] getQualifierArray() { + return this.qArray; + } + + @Override + public int getQualifierOffset() { + return this.qoffset; + } + + @Override + public int getQualifierLength() { + return this.qlength; + } + } + + @InterfaceAudience.Private + private static class FirstOnRowCell extends EmptyCell { + private final byte[] rowArray; + private final int roffset; + private final short rlength; + + public FirstOnRowCell(final byte[] row, int roffset, short rlength) { + this.rowArray = row; + this.roffset = roffset; + this.rlength = rlength; + } + + @Override + public byte[] getRowArray() { + return this.rowArray; + } + + @Override + public int getRowOffset() { + return this.roffset; + } + + @Override + public short getRowLength() { + return this.rlength; + } + + @Override + public long getTimestamp() { + return HConstants.LATEST_TIMESTAMP; + } + + @Override + public byte getTypeByte() { + return Type.Maximum.getCode(); + } + } + + @InterfaceAudience.Private + private static class LastOnRowColCell extends LastOnRowCell { + private final byte[] fArray; + private final int foffset; + private final byte flength; + private final byte[] qArray; + private final int qoffset; + private final int qlength; + + public LastOnRowColCell(byte[] rArray, int roffset, short rlength, + byte[] fArray, int foffset, byte flength, byte[] qArray, int qoffset, + int qlength) { + super(rArray, roffset, rlength); + this.fArray = fArray; + this.foffset = foffset; + this.flength = flength; + this.qArray = qArray; + this.qoffset = qoffset; + this.qlength = qlength; + } + + @Override + public byte[] getFamilyArray() { + return this.fArray; + } + + @Override + public int getFamilyOffset() { + return this.foffset; + } + + @Override + public byte getFamilyLength() { + return this.flength; + } + + @Override + public byte[] getQualifierArray() { + return this.qArray; + } + + @Override + public int getQualifierOffset() { + return this.qoffset; + } + + @Override + public int getQualifierLength() { + return this.qlength; + } + } + + @InterfaceAudience.Private + private static class LastOnRowCell extends EmptyCell { + private final byte[] rowArray; + private final int roffset; + private final short rlength; + + public LastOnRowCell(byte[] row, int roffset, short rlength) { + this.rowArray = row; + this.roffset = roffset; + this.rlength = rlength; + } + + @Override + public byte[] getRowArray() { + return this.rowArray; + } + + @Override + public int getRowOffset() { + return this.roffset; + } + + @Override + public short getRowLength() { + return this.rlength; + } + + @Override + public long getTimestamp() { + return HConstants.OLDEST_TIMESTAMP; + } + + @Override + public byte getTypeByte() { + return Type.Minimum.getCode(); + } + } + + @InterfaceAudience.Private + /** + * These cells are used in reseeks/seeks to improve the read performance. + * They are not real cells that are returned back to the clients + */ + private static abstract class EmptyCell implements Cell, SettableSequenceId { + + @Override + public void setSequenceId(long seqId) { + // Fake cells don't need seqId, so leaving it as a noop. + } + + @Override + public byte[] getRowArray() { + return EMPTY_BYTE_ARRAY; + } + + @Override + public int getRowOffset() { + return 0; + } + + @Override + public short getRowLength() { + return 0; + } + + @Override + public byte[] getFamilyArray() { + return EMPTY_BYTE_ARRAY; + } + + @Override + public int getFamilyOffset() { + return 0; + } + + @Override + public byte getFamilyLength() { + return 0; + } + + @Override + public byte[] getQualifierArray() { + return EMPTY_BYTE_ARRAY; + } + + @Override + public int getQualifierOffset() { + return 0; + } + + @Override + public int getQualifierLength() { + return 0; + } + + @Override + public long getSequenceId() { + return 0; + } + + @Override + public byte[] getValueArray() { + return EMPTY_BYTE_ARRAY; + } + + @Override + public int getValueOffset() { + return 0; + } + + @Override + public int getValueLength() { + return 0; + } + + @Override + public byte[] getTagsArray() { + return EMPTY_BYTE_ARRAY; + } + + @Override + public int getTagsOffset() { + return 0; + } + + @Override + public int getTagsLength() { + return 0; + } + + @Override + public long getMvccVersion() { + return getSequenceId(); + } + + @Override + public byte[] getValue() { + return getValueArray(); + } + + @Override + public byte[] getFamily() { + return getFamilyArray(); + } + + @Override + public byte[] getQualifier() { + return getQualifierArray(); + } + + @Override + public byte[] getRow() { + return getRowArray(); + } + } } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/querymatcher/ScanQueryMatcher.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/querymatcher/ScanQueryMatcher.java index 127e07b..d0b6e51 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/querymatcher/ScanQueryMatcher.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/querymatcher/ScanQueryMatcher.java @@ -20,6 +20,7 @@ package org.apache.hadoop.hbase.regionserver.querymatcher; import java.io.IOException; import org.apache.hadoop.hbase.Cell; +import org.apache.hadoop.hbase.CellUtil; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.KeyValue.KVComparator; import org.apache.hadoop.hbase.KeyValue.Type; @@ -254,13 +255,10 @@ public abstract class ScanQueryMatcher { public Cell getKeyForNextColumn(Cell cell) { ColumnCount nextColumn = columns.getColumnHint(); if (nextColumn == null) { - return KeyValueUtil.createLastOnRow(cell.getRowArray(), cell.getRowOffset(), - cell.getRowLength(), cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength(), - cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength()); + return CellUtil.createLastOnRowCol(cell); } else { - return KeyValueUtil.createFirstOnRow(cell.getRowArray(), cell.getRowOffset(), - cell.getRowLength(), cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength(), - nextColumn.getBuffer(), nextColumn.getOffset(), nextColumn.getLength()); + return CellUtil.createFirstOnRowCol(cell, nextColumn.getBuffer(), + nextColumn.getOffset(), nextColumn.getLength()); } }