diff --git src/main/java/org/apache/hadoop/hbase/KeyValue.java src/main/java/org/apache/hadoop/hbase/KeyValue.java index b8c108a..4a39133 100644 --- src/main/java/org/apache/hadoop/hbase/KeyValue.java +++ src/main/java/org/apache/hadoop/hbase/KeyValue.java @@ -406,6 +406,121 @@ public class KeyValue implements Writable, HeapSize { } /** + * Constructs KeyValue structure filled with specified values. Uses the provided buffer as the data buffer. + *
+ * Column is split into two fields, family and qualifier.
+ *
+ * @param buffer the bytes buffer to use
+ * @param row row key
+ * @param roffset row offset
+ * @param rlength row length
+ * @param family family name
+ * @param foffset family offset
+ * @param flength family length
+ * @param qualifier column qualifier
+ * @param qoffset qualifier offset
+ * @param qlength qualifier length
+ * @param timestamp version timestamp
+ * @param type key type
+ * @param value column value
+ * @param voffset value offset
+ * @param vlength value length
+ * @throws IllegalArgumentException
+ */
+ public KeyValue(byte [] buffer,
+ final byte [] row, final int roffset, final int rlength,
+ final byte [] family, final int foffset, final int flength,
+ final byte [] qualifier, final int qoffset, final int qlength,
+ final long timestamp, final Type type,
+ final byte [] value, final int voffset, final int vlength) {
+
+ this.bytes = buffer;
+ this.length = writeByteArray(buffer, row, roffset, rlength,
+ family, foffset, flength, qualifier, qoffset, qlength,
+ timestamp, type, value, voffset, vlength);
+ this.offset = 0;
+ }
+
+ /**
+ * Write KeyValue format into the provided byte array.
+ *
+ * @param buffer the bytes buffer to use
+ * @param row row key
+ * @param roffset row offset
+ * @param rlength row length
+ * @param family family name
+ * @param foffset family offset
+ * @param flength family length
+ * @param qualifier column qualifier
+ * @param qoffset qualifier offset
+ * @param qlength qualifier length
+ * @param timestamp version timestamp
+ * @param type key type
+ * @param value column value
+ * @param voffset value offset
+ * @param vlength value length
+ * @return The number of useful bytes in the buffer.
+ */
+ static int writeByteArray(byte [] buffer,
+ final byte [] row, final int roffset, final int rlength,
+ final byte [] family, final int foffset, int flength,
+ final byte [] qualifier, final int qoffset, int qlength,
+ final long timestamp, final Type type,
+ final byte [] value, final int voffset, int vlength) {
+
+ if (rlength > Short.MAX_VALUE) {
+ throw new IllegalArgumentException("Row > " + Short.MAX_VALUE);
+ }
+ if (row == null) {
+ throw new IllegalArgumentException("Row is null");
+ }
+ // Family length
+ flength = family == null ? 0 : flength;
+ if (flength > Byte.MAX_VALUE) {
+ throw new IllegalArgumentException("Family > " + Byte.MAX_VALUE);
+ }
+ // Qualifier length
+ qlength = qualifier == null ? 0 : qlength;
+ if (qlength > Integer.MAX_VALUE - rlength - flength) {
+ throw new IllegalArgumentException("Qualifier > " + Integer.MAX_VALUE);
+ }
+ // Key length
+ long longkeylength = KEY_INFRASTRUCTURE_SIZE + rlength + flength + qlength;
+ if (longkeylength > Integer.MAX_VALUE) {
+ throw new IllegalArgumentException("keylength " + longkeylength + " > " +
+ Integer.MAX_VALUE);
+ }
+ int keylength = (int)longkeylength;
+ // Value length
+ vlength = value == null? 0 : vlength;
+ if (vlength > HConstants.MAXIMUM_VALUE_LENGTH) { // FindBugs INT_VACUOUS_COMPARISON
+ throw new IllegalArgumentException("Valuer > " +
+ HConstants.MAXIMUM_VALUE_LENGTH);
+ }
+
+ // Write key, value and key row length.
+ int pos = 0;
+ pos = Bytes.putInt(buffer, pos, keylength);
+ pos = Bytes.putInt(buffer, pos, vlength);
+ pos = Bytes.putShort(buffer, pos, (short)(rlength & 0x0000ffff));
+ pos = Bytes.putBytes(buffer, pos, row, roffset, rlength);
+ pos = Bytes.putByte(buffer, pos, (byte)(flength & 0x0000ff));
+ if(flength != 0) {
+ pos = Bytes.putBytes(buffer, pos, family, foffset, flength);
+ }
+ if(qlength != 0) {
+ pos = Bytes.putBytes(buffer, pos, qualifier, qoffset, qlength);
+ }
+ pos = Bytes.putLong(buffer, pos, timestamp);
+ pos = Bytes.putByte(buffer, pos, type.getCode());
+ if (value != null && value.length > 0) {
+ pos = Bytes.putBytes(buffer, pos, value, voffset, vlength);
+ }
+
+ return KEYVALUE_INFRASTRUCTURE_SIZE + keylength + vlength;
+ }
+
+ /**
* Write KeyValue format into a byte array.
*
* @param row row key
@@ -1189,6 +1304,34 @@ public class KeyValue implements Writable, HeapSize {
}
/**
+ * Checks if column matches.
+ *
+ * @param family family name
+ * @param foffset family offset
+ * @param flength family length
+ * @param qualifier column qualifier
+ * @param qoffset qualifier offset
+ * @param qlength qualifier length
+ * @return True if column matches
+ */
+ public boolean matchingColumn(final byte [] family, final int foffset, final int flength,
+ final byte [] qualifier, final int qoffset, final int qlength) {
+
+ int rl = getRowLength();
+ int o = getFamilyOffset(rl);
+ int fl = getFamilyLength(o);
+ int ql = getQualifierLength(rl,fl);
+
+ if (!Bytes.equals(family, foffset, flength, this.bytes, o, fl))
+ return false;
+
+ if (qualifier == null || qlength == 0)
+ return (ql == 0);
+
+ return Bytes.equals(qualifier, qoffset, qlength, this.bytes, o + fl, ql);
+ }
+
+ /**
* @param left
* @param loffset
* @param llength
diff --git src/main/java/org/apache/hadoop/hbase/client/Result.java src/main/java/org/apache/hadoop/hbase/client/Result.java
index 57f5ecb..e93eaab 100644
--- src/main/java/org/apache/hadoop/hbase/client/Result.java
+++ src/main/java/org/apache/hadoop/hbase/client/Result.java
@@ -23,6 +23,7 @@ package org.apache.hadoop.hbase.client;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
@@ -31,6 +32,7 @@ import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
+import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.KeyValue.SplitKeyValue;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
@@ -76,6 +78,9 @@ public class Result implements Writable, WritableWithSize {
private transient byte [] row = null;
private ImmutableBytesWritable bytes = null;
+ // never use directly
+ private static byte [] buffer = new byte[1024];
+
/**
* Constructor used for Writable.
*/
@@ -224,6 +229,74 @@ public class Result implements Writable, WritableWithSize {
}
/**
+ * Searches for the latest value for the specified column.
+ *
+ * @param kvs the array to search
+ * @param family family name
+ * @param foffset family offset
+ * @param flength family length
+ * @param qualifier column qualifier
+ * @param qoffset qualifier offset
+ * @param qlength qualifier length
+ *
+ * @return the index where the value was found, or -1 otherwise
+ */
+ protected int binarySearch(final KeyValue [] kvs,
+ final byte [] family, final int foffset, final int flength,
+ final byte [] qualifier, final int qoffset, final int qlength) {
+
+ KeyValue searchTerm = getSearchTerm(kvs[0].getRow(), family, foffset, flength, qualifier, qoffset, qlength);
+
+ // pos === ( -(insertion point) - 1)
+ int pos = Arrays.binarySearch(kvs, searchTerm, KeyValue.COMPARATOR);
+ // never will exact match
+ if (pos < 0) {
+ pos = (pos+1) * -1;
+ // pos is now insertion point
+ }
+ if (pos == kvs.length) {
+ return -1; // doesn't exist
+ }
+ return pos;
+ }
+
+ /**
+ * Generates a dummy KeyValue object used for searching.
+ *
+ * @param row the value key
+ * @param family family name
+ * @param foffset family offset
+ * @param flength family length
+ * @param qualifier column qualifier
+ * @param qoffset qualifier offset
+ * @param qlength qualifier length
+ *
+ * @return a dummy KeyValue object
+ */
+ protected static KeyValue getSearchTerm(final byte [] row,
+ final byte [] family, final int foffset, final int flength,
+ final byte [] qualifier, final int qoffset, final int qlength) {
+
+ long lLength = KeyValue.KEY_INFRASTRUCTURE_SIZE +
+ row.length + flength + qlength +
+ KeyValue.KEYVALUE_INFRASTRUCTURE_SIZE;
+
+ if (lLength > Integer.MAX_VALUE)
+ throw new IllegalArgumentException("KeyValue length " + lLength + " > " + Integer.MAX_VALUE);
+
+ int iLength = (int) lLength;
+ if (buffer.length < iLength)
+ buffer = new byte[iLength];
+
+ return new KeyValue(buffer,
+ row, 0, row.length,
+ family, foffset, flength,
+ qualifier, qoffset, qlength,
+ HConstants.LATEST_TIMESTAMP, KeyValue.Type.Maximum,
+ null, 0, 0);
+ }
+
+ /**
* The KeyValue for the most recent for a given column. If the column does
* not exist in the result set - if it wasn't selected in the query (Get/Scan)
* or just does not exist in the row the return value is null.
@@ -249,6 +322,38 @@ public class Result implements Writable, WritableWithSize {
}
/**
+ * The KeyValue for the most recent for a given column. If the column does
+ * not exist in the result set - if it wasn't selected in the query (Get/Scan)
+ * or just does not exist in the row the return value is null.
+ *
+ * @param family family name
+ * @param foffset family offset
+ * @param flength family length
+ * @param qualifier column qualifier
+ * @param qoffset qualifier offset
+ * @param qlength qualifier length
+ *
+ * @return KeyValue for the column or null
+ */
+ public KeyValue getColumnLatest(byte [] family, int foffset, int flength,
+ byte [] qualifier, int qoffset, int qlength) {
+
+ KeyValue [] kvs = raw(); // side effect possibly.
+ if (kvs == null || kvs.length == 0)
+ return null;
+
+ int pos = binarySearch(kvs, family, foffset, flength, qualifier, qoffset, qlength);
+ if (pos == -1)
+ return null;
+
+ KeyValue kv = kvs[pos];
+ if (kv.matchingColumn(family, foffset, flength, qualifier, qoffset, qlength))
+ return kv;
+
+ return null;
+ }
+
+ /**
* Get the latest version of the specified column.
* @param family family name
* @param qualifier column qualifier
@@ -263,6 +368,100 @@ public class Result implements Writable, WritableWithSize {
}
/**
+ * Loads the latest version of the specified column into the provided ByteBuffer.
+ *
+ * Does not clear or flip the buffer.
+ *
+ * @param family family name
+ * @param foffset family offset
+ * @param flength family length
+ * @param qualifier column qualifier
+ * @param qoffset qualifier offset
+ * @param qlength qualifier length
+ * @param dst the buffer where to write the value
+ *
+ * @return true if a value was found, false otherwise
+ */
+ public boolean loadValue(byte [] family, int foffset, int flength,
+ byte [] qualifier, int qoffset, int qlength,
+ ByteBuffer dst) {
+
+ KeyValue kv = getColumnLatest(family, foffset, flength, qualifier, qoffset, qlength);
+
+ if (kv == null)
+ return false;
+
+ dst.put(kv.getBuffer(), kv.getValueOffset(), kv.getValueLength());
+ return true;
+ }
+
+ /**
+ * Checks if the specified column contains a non-empty value.
+ *
+ * @param family family name
+ * @param qualifier column qualifier
+ *
+ * @return whether or not a latest value exists and is not empty
+ */
+ public boolean containsNonEmptyColumn(byte [] family, byte [] qualifier) {
+
+ return containsNonEmptyColumn(family, 0, family.length, qualifier, 0, qualifier.length);
+ }
+
+ /**
+ * Checks if the specified column contains a non-empty value.
+ *
+ * @param family family name
+ * @param foffset family offset
+ * @param flength family length
+ * @param qualifier column qualifier
+ * @param qoffset qualifier offset
+ * @param qlength qualifier length
+ *
+ * @return whether or not a latest value exists and is not empty
+ */
+ public boolean containsNonEmptyColumn(byte [] family, int foffset, int flength,
+ byte [] qualifier, int qoffset, int qlength) {
+
+ KeyValue kv = getColumnLatest(family, foffset, flength, qualifier, qoffset, qlength);
+
+ return (kv != null) && (kv.getValueLength() > 0);
+ }
+
+ /**
+ * Checks if the specified column contains an empty value.
+ *
+ * @param family family name
+ * @param qualifier column qualifier
+ *
+ * @return whether or not a latest value exists and is empty
+ */
+ public boolean containsEmptyColumn(byte [] family, byte [] qualifier) {
+
+ return containsEmptyColumn(family, 0, family.length, qualifier, 0, qualifier.length);
+ }
+
+ /**
+ * Checks if the specified column contains an empty value.
+ *
+ * @param family family name
+ * @param foffset family offset
+ * @param flength family length
+ * @param qualifier column qualifier
+ * @param qoffset qualifier offset
+ * @param qlength qualifier length
+ *
+ * @return whether or not a latest value exists and is empty
+ */
+ public boolean containsEmptyColumn(byte [] family, int foffset, int flength,
+ byte [] qualifier, int qoffset, int qlength) {
+
+ KeyValue kv = getColumnLatest(family, foffset, flength, qualifier, qoffset, qlength);
+
+ return (kv != null) && (kv.getValueLength() == 0);
+ }
+
+ /**
* Checks for existence of the specified column.
* @param family family name
* @param qualifier column qualifier
@@ -274,6 +473,24 @@ public class Result implements Writable, WritableWithSize {
}
/**
+ * Checks for existence of the specified column.
+ *
+ * @param family family name
+ * @param foffset family offset
+ * @param flength family length
+ * @param qualifier column qualifier
+ * @param qoffset qualifier offset
+ * @param qlength qualifier length
+ *
+ * @return true if at least one value exists in the result, false if not
+ */
+ public boolean containsColumn(byte [] family, int foffset, int flength,
+ byte [] qualifier, int qoffset, int qlength) {
+
+ return getColumnLatest(family, foffset, flength, qualifier, qoffset, qlength) != null;
+ }
+
+ /**
* Map of families to all versions of its qualifiers and values.
*
* Returns a three level Map of the form: