hbase-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From apurt...@apache.org
Subject svn commit: r785076 [2/18] - in /hadoop/hbase/trunk_on_hadoop-0.18.3: ./ bin/ conf/ src/java/ src/java/org/apache/hadoop/hbase/ src/java/org/apache/hadoop/hbase/client/ src/java/org/apache/hadoop/hbase/filter/ src/java/org/apache/hadoop/hbase/io/ src/j...
Date Tue, 16 Jun 2009 04:34:02 GMT
Modified: hadoop/hbase/trunk_on_hadoop-0.18.3/src/java/org/apache/hadoop/hbase/KeyValue.java
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk_on_hadoop-0.18.3/src/java/org/apache/hadoop/hbase/KeyValue.java?rev=785076&r1=785075&r2=785076&view=diff
==============================================================================
--- hadoop/hbase/trunk_on_hadoop-0.18.3/src/java/org/apache/hadoop/hbase/KeyValue.java (original)
+++ hadoop/hbase/trunk_on_hadoop-0.18.3/src/java/org/apache/hadoop/hbase/KeyValue.java Tue Jun 16 04:33:56 2009
@@ -25,11 +25,11 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hbase.io.HeapSize;
 import org.apache.hadoop.hbase.io.hfile.HFile;
 import org.apache.hadoop.hbase.util.Bytes;
-import org.apache.hadoop.hbase.io.HeapSize;
-import org.apache.hadoop.io.Writable;
 import org.apache.hadoop.io.RawComparator;
+import org.apache.hadoop.io.Writable;
 
 /**
  * An HBase Key/Value.  Instances of this class are immutable.  They are not
@@ -59,6 +59,8 @@
    * Colon character in UTF-8
    */
   public static final char COLUMN_FAMILY_DELIMITER = ':';
+
+  public static final byte[] COLUMN_FAMILY_DELIM_ARRAY = new byte[]{COLUMN_FAMILY_DELIMITER};
   
   /**
    * Comparator for plain key/values; i.e. non-catalog table key/values.
@@ -118,26 +120,45 @@
         return compare(a, 0, a.length, b, 0, b.length);
       }
     };
+  
+  /**
+   * Get the appropriate row comparator for the specified table.
+   * 
+   * Hopefully we can get rid of this, I added this here because it's replacing
+   * something in HSK.  We should move completely off of that.
+   * 
+   * @param tableName  The table name.
+   * @return The comparator.
+   */
+  public static KeyComparator getRowComparator(byte [] tableName) {
+    if(Bytes.equals(HTableDescriptor.ROOT_TABLEDESC.getName(),tableName)) {
+      return ROOT_COMPARATOR.getRawComparator();
+    }
+    if(Bytes.equals(HTableDescriptor.META_TABLEDESC.getName(), tableName)) {
+      return META_COMPARATOR.getRawComparator();
+    }
+    return COMPARATOR.getRawComparator();
+  }
 
   // Size of the timestamp and type byte on end of a key -- a long + a byte.
-  private static final int TIMESTAMP_TYPE_SIZE =
+  public static final int TIMESTAMP_TYPE_SIZE =
     Bytes.SIZEOF_LONG /* timestamp */ +
     Bytes.SIZEOF_BYTE /*keytype*/;
 
   // Size of the length shorts and bytes in key.
-  private static final int KEY_INFRASTRUCTURE_SIZE =
+  public static final int KEY_INFRASTRUCTURE_SIZE =
     Bytes.SIZEOF_SHORT /*rowlength*/ +
     Bytes.SIZEOF_BYTE /*columnfamilylength*/ +
     TIMESTAMP_TYPE_SIZE;
 
   // How far into the key the row starts at. First thing to read is the short
   // that says how long the row is.
-  private static final int ROW_OFFSET =
+  public static final int ROW_OFFSET =
     Bytes.SIZEOF_INT /*keylength*/ +
     Bytes.SIZEOF_INT /*valuelength*/;
 
   // Size of the length ints in a KeyValue datastructure.
-  private static final int KEYVALUE_INFRASTRUCTURE_SIZE = ROW_OFFSET;
+  public static final int KEYVALUE_INFRASTRUCTURE_SIZE = ROW_OFFSET;
 
   /**
    * Key type.
@@ -145,10 +166,13 @@
    * enum ordinals . They change if item is removed or moved.  Do our own codes.
    */
   public static enum Type {
+    Minimum((byte)0),
     Put((byte)4),
+
     Delete((byte)8),
     DeleteColumn((byte)12),
     DeleteFamily((byte)14),
+
     // Maximum is used when searching; you look from maximum on down.
     Maximum((byte)255);
     
@@ -233,194 +257,200 @@
     this.offset = offset;
     this.length = length;
   }
+
+  /** Temporary constructors until 880/1249 is committed to remove deps */
   
   /**
-   * Constructs KeyValue structure filled with null value.
-   * @param row - row key (arbitrary byte array)
-   * @param timestamp
+   * Temporary.
    */
-  public KeyValue(final String row, final long timestamp) {
-    this(Bytes.toBytes(row), timestamp);
+  public KeyValue(final byte [] row, final byte [] column) {
+    this(row, column, HConstants.LATEST_TIMESTAMP, null);
   }
-
+  
+  public KeyValue(final byte [] row, final byte [] column, long ts) {
+    this(row, column, ts, null);
+  }
+  
+  public KeyValue(final byte [] row, final byte [] column, long ts,
+      byte [] value) {
+    this(row, column, ts, Type.Put, value);
+  }
+  
+  public KeyValue(final byte [] row, final byte [] column, long ts, Type type,
+      byte [] value) {
+    int rlength = row == null ? 0 : row.length;
+    int vlength = value == null ? 0 : value.length;
+    int clength = column == null ? 0 : column.length;
+    this.bytes = createByteArray(row, 0, rlength, column, 0, clength,
+        ts, type, value, 0, vlength);
+    this.length = this.bytes.length;
+    this.offset = 0;
+  }
+  
+  /** Constructors that build a new backing byte array from fields */
+  
   /**
    * Constructs KeyValue structure filled with null value.
    * @param row - row key (arbitrary byte array)
    * @param timestamp
    */
   public KeyValue(final byte [] row, final long timestamp) {
-    this(row, null, timestamp, Type.Put, null);
-  }
-
-  /**
-   * Constructs KeyValue structure filled with null value.
-   * @param row - row key (arbitrary byte array)
-   * @param column Column with delimiter between family and qualifier
-   */
-  public KeyValue(final String row, final String column) {
-    this(row, column, null);
+    this(row, timestamp, Type.Put);
   }
 
   /**
    * Constructs KeyValue structure filled with null value.
    * @param row - row key (arbitrary byte array)
-   * @param column Column with delimiter between family and qualifier
-   */
-  public KeyValue(final byte [] row, final byte [] column) {
-    this(row, column, null);
-  }
-
-  /**
-   * Constructs KeyValue structure filled with specified value.
-   * @param row - row key (arbitrary byte array)
-   * @param column Column with delimiter between family and qualifier
-   * @param value
-   */
-  public KeyValue(final String row, final String column, final byte [] value) {
-    this(Bytes.toBytes(row), Bytes.toBytes(column), value);
-  }
-
-  /**
-   * Constructs KeyValue structure filled with specified value.
-   * @param row - row key (arbitrary byte array)
-   * @param column Column with delimiter between family and qualifier
-   * @param value
+   * @param timestamp
    */
-  public KeyValue(final byte [] row, final byte [] column, final byte [] value) {
-    this(row, column, HConstants.LATEST_TIMESTAMP, value);
+  public KeyValue(final byte [] row, final long timestamp, Type type) {
+    this(row, null, null, timestamp, type, null);
   }
 
-
   /**
    * Constructs KeyValue structure filled with null value.
    * @param row - row key (arbitrary byte array)
-   * @param column Column with delimiter between family and qualifier
-   * @param ts
+   * @param family family name
+   * @param qualifier column qualifier
    */
-  public KeyValue(final String row, final String column, final long ts) {
-    this(row, column, ts, null);
+  public KeyValue(final byte [] row, final byte [] family, 
+      final byte [] qualifier) {
+    this(row, family, qualifier, HConstants.LATEST_TIMESTAMP, Type.Put);
   }
 
   /**
    * Constructs KeyValue structure filled with null value.
    * @param row - row key (arbitrary byte array)
-   * @param column Column with delimiter between family and qualifier
-   * @param ts
-   */
-  public KeyValue(final byte [] row, final byte [] column, final long ts) {
-    this(row, column, ts, Type.Put);
-  }
-
-  /**
-   * Constructs KeyValue structure filled with specified value.
-   * @param row - row key (arbitrary byte array)
-   * @param column Column with delimiter between family and qualifier
-   * @param timestamp
-   * @param value
+   * @param family family name
+   * @param qualifier column qualifier
    */
-  public KeyValue(final String row, final String column,
-    final long timestamp, final byte [] value) {
-    this(Bytes.toBytes(row),
-      column == null? HConstants.EMPTY_BYTE_ARRAY: Bytes.toBytes(column),
-      timestamp, value);
+  public KeyValue(final byte [] row, final byte [] family, 
+      final byte [] qualifier, final byte [] value) {
+    this(row, family, qualifier, HConstants.LATEST_TIMESTAMP, Type.Put, value);
   }
 
   /**
-   * Constructs KeyValue structure filled with specified value.
-   * @param row - row key (arbitrary byte array)
-   * @param column Column with delimiter between family and qualifier
-   * @param timestamp
-   * @param value
+   * Constructs KeyValue structure filled with specified values.
+   * @param row row key
+   * @param family family name
+   * @param qualifier column qualifier
+   * @param timestamp version timestamp
+   * @param type key type
+   * @throws IllegalArgumentException
    */
-  public KeyValue(final byte [] row, final byte [] column,
-     final long timestamp, final byte [] value) {
-    this(row, column, timestamp, Type.Put, value);
+  public KeyValue(final byte[] row, final byte[] family,
+      final byte[] qualifier, final long timestamp, Type type) {
+    this(row, family, qualifier, timestamp, type, null);
   }
-
+  
   /**
-   * Constructs KeyValue structure filled with specified value.
-   * @param row - row key (arbitrary byte array)
-   * @param column Column with delimiter between family and qualifier
-   * @param timestamp
-   * @param type
-   * @param value
+   * Constructs KeyValue structure filled with specified values.
+   * @param row row key
+   * @param family family name
+   * @param qualifier column qualifier
+   * @param timestamp version timestamp
+   * @param value column value
+   * @throws IllegalArgumentException
    */
-  public KeyValue(final String row, final String column,
-     final long timestamp, final Type type, final byte [] value) {
-    this(Bytes.toBytes(row), Bytes.toBytes(column), timestamp, type,
-      value);
+  public KeyValue(final byte[] row, final byte[] family,
+      final byte[] qualifier, final long timestamp, final byte[] value) {
+    this(row, family, qualifier, timestamp, Type.Put, value);
   }
-
+  
   /**
-   * Constructs KeyValue structure filled with null value.
-   * @param row - row key (arbitrary byte array)
-   * @param column Column with delimiter between family and qualifier
-   * @param timestamp
-   * @param type
+   * Constructs KeyValue structure filled with specified values.
+   * @param row row key
+   * @param family family name
+   * @param qualifier column qualifier
+   * @param timestamp version timestamp
+   * @param type key type
+   * @param value column value
+   * @throws IllegalArgumentException
    */
-  public KeyValue(final byte [] row, final byte [] column,
-      final long timestamp, final Type type) {
-    this(row, 0, row.length, column, 0, column == null? 0: column.length,
-      timestamp, type, null, 0, -1);
-  }
-
-  /**
-   * Constructs KeyValue structure filled with specified value.
-   * @param row - row key (arbitrary byte array)
-   * @param column Column with delimiter between family and qualifier
-   * @param timestamp
-   * @param type
-   * @param value
+  public KeyValue(final byte[] row, final byte[] family,
+      final byte[] qualifier, final long timestamp, Type type,
+      final byte[] value) {
+    this(row, family, qualifier, 0, qualifier==null ? 0 : qualifier.length, 
+        timestamp, type, value, 0, value==null ? 0 : value.length);
+  } 
+
+  /**
+   * Constructs KeyValue structure filled with specified values.
+   * @param row row key
+   * @param family family name
+   * @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(final byte [] row, final byte [] column,
-      final long timestamp, final Type type, final byte [] value) {
-    this(row, 0, row.length, column, 0, column == null? 0: column.length,
-      timestamp, type, value, 0, value == null? 0: value.length);
+  public KeyValue(byte [] row, byte [] family, 
+      byte [] qualifier, int qoffset, int qlength, long timestamp, Type type, 
+      byte [] value, int voffset, int vlength) {
+    this(row, 0, row==null ? 0 : row.length, 
+        family, 0, family==null ? 0 : family.length,
+        qualifier, qoffset, qlength, timestamp, type, 
+        value, voffset, vlength);
   }
 
   /**
-   * Constructs KeyValue structure filled with specified value.
-   * @param row - row key (arbitrary byte array)
-   * @param roffset
-   * @param rlength
-   * @param column Column with delimiter between family and qualifier
-   * @param coffset Where to start reading the column.
-   * @param clength How long column is (including the family/qualifier delimiter.
-   * @param timestamp
-   * @param type
-   * @param value
-   * @param voffset
-   * @param vlength
+   * Constructs KeyValue structure filled with specified values.
+   * <p>
+   * Column is split into two fields, family and qualifier.
+   * @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(final byte [] row, final int roffset, final int rlength,
-      final byte [] column, final int coffset, int clength,
+      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, int vlength) {
-    this.bytes = createByteArray(row, roffset, rlength, column, coffset,
-      clength, timestamp, type, value, voffset, vlength);
+      final byte [] value, final int voffset, final int vlength) {
+    this.bytes = createByteArray(row, roffset, rlength, 
+        family, foffset, flength, qualifier, qoffset, qlength,
+        timestamp, type, value, voffset, vlength);
     this.length = bytes.length;
     this.offset = 0;
   }
 
   /**
    * Write KeyValue format into a byte array.
-   * @param row - row key (arbitrary byte array)
-   * @param roffset
-   * @param rlength
-   * @param column
-   * @param coffset
-   * @param clength
-   * @param timestamp
-   * @param type
-   * @param value
-   * @param voffset
-   * @param vlength
-   * @return
+   * 
+   * @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 newly created byte array. 
    */
   static byte [] createByteArray(final byte [] row, final int roffset,
-        final int rlength,
-      final byte [] column, final int coffset, int clength,
+      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) {
@@ -429,24 +459,30 @@
     if (row == null) {
       throw new IllegalArgumentException("Row is null");
     }
-    // If column is non-null, figure where the delimiter is at.
-    int delimiteroffset = 0;
-    if (column != null && column.length > 0) {
-      delimiteroffset = getFamilyDelimiterIndex(column, coffset, clength);
-      if (delimiteroffset > Byte.MAX_VALUE) {
-        throw new IllegalArgumentException("Family > " + Byte.MAX_VALUE);
-      }
+    // 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);
     }
-    // Value length
-    vlength = value == null? 0: vlength;
-    // Column length - minus delimiter
-    clength = column == null || column.length == 0? 0: clength - 1;
-    long longkeylength = KEY_INFRASTRUCTURE_SIZE + rlength + clength;
+    // 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) {
+      throw new IllegalArgumentException("Valuer > " + 
+          HConstants.MAXIMUM_VALUE_LENGTH);
+    }
+    
     // Allocate right-sized byte array.
     byte [] bytes = new byte[KEYVALUE_INFRASTRUCTURE_SIZE + keylength + vlength];
     // Write key, value and key row length.
@@ -455,15 +491,12 @@
     pos = Bytes.putInt(bytes, pos, vlength);
     pos = Bytes.putShort(bytes, pos, (short)(rlength & 0x0000ffff));
     pos = Bytes.putBytes(bytes, pos, row, roffset, rlength);
-    // Write out column family length.
-    pos = Bytes.putByte(bytes, pos, (byte)(delimiteroffset & 0x0000ff));
-    if (column != null && column.length != 0) {
-      // Write family.
-      pos = Bytes.putBytes(bytes, pos, column, coffset, delimiteroffset);
-      // Write qualifier.
-      delimiteroffset++;
-      pos = Bytes.putBytes(bytes, pos, column, coffset + delimiteroffset,
-        column.length - delimiteroffset);
+    pos = Bytes.putByte(bytes, pos, (byte)(flength & 0x0000ff));
+    if(flength != 0) {
+      pos = Bytes.putBytes(bytes, pos, family, foffset, flength);
+    }
+    if(qlength != 0) {
+      pos = Bytes.putBytes(bytes, pos, qualifier, qoffset, qlength);
     }
     pos = Bytes.putLong(bytes, pos, timestamp);
     pos = Bytes.putByte(bytes, pos, type.getCode());
@@ -472,6 +505,46 @@
     }
     return bytes;
   }
+  
+  /**
+   * Write KeyValue format into a byte array.
+   * <p>
+   * Takes column in the form <code>family:qualifier</code>
+   * @param row - row key (arbitrary byte array)
+   * @param roffset
+   * @param rlength
+   * @param column
+   * @param coffset
+   * @param clength
+   * @param timestamp
+   * @param type
+   * @param value
+   * @param voffset
+   * @param vlength
+   * @return The newly created byte array. 
+   */
+  static byte [] createByteArray(final byte [] row, final int roffset,
+        final int rlength,
+      final byte [] column, final int coffset, int clength,
+      final long timestamp, final Type type,
+      final byte [] value, final int voffset, int vlength) {
+    // If column is non-null, figure where the delimiter is at.
+    int delimiteroffset = 0;
+    if (column != null && column.length > 0) {
+      delimiteroffset = getFamilyDelimiterIndex(column, coffset, clength);
+      if (delimiteroffset > Byte.MAX_VALUE) {
+        throw new IllegalArgumentException("Family > " + Byte.MAX_VALUE);
+      }
+    } else {
+      return createByteArray(row,roffset,rlength,null,0,0,null,0,0,timestamp,
+          type,value,voffset,vlength);
+    }
+    int flength = delimiteroffset-coffset;
+    int qlength = clength - flength - 1;
+    return createByteArray(row, roffset, rlength, column, coffset,
+        flength, column, delimiteroffset+1, qlength, timestamp, type,
+        value, voffset, vlength);
+  }
 
   // Needed doing 'contains' on List.  Only compares the key portion, not the
   // value.
@@ -485,37 +558,36 @@
     return result;
   }
 
+  //---------------------------------------------------------------------------
+  //
+  //  KeyValue cloning
+  //
+  //---------------------------------------------------------------------------
+  
   /**
-   * @param timestamp
+   * Clones a row.
+   * 
+   * @param timestamp  The new time stamp for the row.
    * @return Clone of bb's key portion with only the row and timestamp filled in.
-   * @throws IOException
    */
   public KeyValue cloneRow(final long timestamp) {
     return new KeyValue(getBuffer(), getRowOffset(), getRowLength(),
-      null, 0, 0, timestamp, Type.codeToType(getType()), null, 0, 0);
-  }
-
-  /**
-   * @return Clone of bb's key portion with type set to Type.Delete.
-   * @throws IOException
-   */
-  public KeyValue cloneDelete() {
-    return createKey(Type.Delete);
+        null, 0, 0, null, 0, 0, 
+        timestamp, Type.codeToType(getType()), null, 0, 0);
   }
 
   /**
    * @return Clone of bb's key portion with type set to Type.Maximum. Use this
    * doing lookups where you are doing getClosest.  Using Maximum, you'll be
    * sure to trip over all of the other key types since Maximum sorts first.
-   * @throws IOException
    */
   public KeyValue cloneMaximum() {
      return createKey(Type.Maximum);
   }
 
-  /*
-   * Make a clone with the new type.
-   * Does not copy value.
+  /**
+   * Make a clone with the new type. Does not copy value.
+   * 
    * @param newtype New type to set on clone of this key.
    * @return Clone of this key with type set to <code>newtype</code>
    */
@@ -531,6 +603,12 @@
     return new KeyValue(other, 0, other.length);
   }
 
+  //---------------------------------------------------------------------------
+  //
+  //  String representation
+  //
+  //---------------------------------------------------------------------------
+  
   public String toString() {
     return keyToString(this.bytes, this.offset + ROW_OFFSET, getKeyLength()) +
       "/vlen=" + getValueLength();
@@ -545,6 +623,7 @@
   }
 
   /**
+   * Use for logging.
    * @param b Key portion of a KeyValue.
    * @param o Offset to start of key
    * @param l Length of key.
@@ -552,7 +631,7 @@
    */
   public static String keyToString(final byte [] b, final int o, final int l) {
     int rowlength = Bytes.toShort(b, o);
-    String row = Bytes.toString(b, o + Bytes.SIZEOF_SHORT, rowlength);
+    String row = Bytes.toStringBinary(b, o + Bytes.SIZEOF_SHORT, rowlength);
     int columnoffset = o + Bytes.SIZEOF_SHORT + 1 + rowlength;
     int familylength = b[columnoffset - 1];
     int columnlength = l - ((columnoffset - o) + TIMESTAMP_TYPE_SIZE);
@@ -563,11 +642,20 @@
       columnlength - familylength);
     long timestamp = Bytes.toLong(b, o + (l - TIMESTAMP_TYPE_SIZE));
     byte type = b[o + l - 1];
+//    return row + "/" + family +
+//      (family != null && family.length() > 0? COLUMN_FAMILY_DELIMITER: "") +
+//      qualifier + "/" + timestamp + "/" + Type.codeToType(type);
     return row + "/" + family +
-      (family != null && family.length() > 0? COLUMN_FAMILY_DELIMITER: "") +
+      (family != null && family.length() > 0? ":" :"") +
       qualifier + "/" + timestamp + "/" + Type.codeToType(type);
   }
 
+  //---------------------------------------------------------------------------
+  //
+  //  Public Member Accessors
+  //
+  //---------------------------------------------------------------------------
+  
   /**
    * @return The byte array backing this KeyValue.
    */
@@ -589,7 +677,13 @@
     return length;
   }
 
-  /*
+  //---------------------------------------------------------------------------
+  //
+  //  Length and Offset Calculators
+  //
+  //---------------------------------------------------------------------------
+  
+  /**
    * Determines the total length of the KeyValue stored in the specified
    * byte array and offset.  Includes all headers.
    * @param bytes byte array
@@ -603,41 +697,189 @@
   }
 
   /**
-   * @return Copy of the key portion only.  Used compacting and testing.
+   * @return Key offset in backing buffer..
    */
-  public byte [] getKey() {
-    int keylength = getKeyLength();
-    byte [] key = new byte[keylength];
-    System.arraycopy(getBuffer(), getKeyOffset(), key, 0, keylength);
-    return key;
+  public int getKeyOffset() {
+    return this.offset + ROW_OFFSET;
   }
 
   public String getKeyString() {
-    return Bytes.toString(getBuffer(), getKeyOffset(), getKeyLength());
+    return Bytes.toStringBinary(getBuffer(), getKeyOffset(), getKeyLength());
   }
 
   /**
-   * @return Key offset in backing buffer..
+   * @return Length of key portion.
    */
-  public int getKeyOffset() {
-    return this.offset + ROW_OFFSET;
+  public int getKeyLength() {
+    return Bytes.toInt(this.bytes, this.offset);
   }
 
   /**
-   * @return Row length.
+   * @return Value offset
    */
-  public short getRowLength() {
-    return Bytes.toShort(this.bytes, getKeyOffset());
+  public int getValueOffset() {
+    return getKeyOffset() + getKeyLength();
+  }
+  
+  /**
+   * @return Value length
+   */
+  public int getValueLength() {
+    return Bytes.toInt(this.bytes, this.offset + Bytes.SIZEOF_INT);
   }
 
   /**
-   * @return Offset into backing buffer at which row starts.
+   * @return Row offset
    */
   public int getRowOffset() {
     return getKeyOffset() + Bytes.SIZEOF_SHORT;
   }
+  
+  /**
+   * @return Row length
+   */
+  public short getRowLength() {
+    return Bytes.toShort(this.bytes, getKeyOffset());
+  }
+
+  /**
+   * @return Family offset
+   */
+  public int getFamilyOffset() {
+    return getFamilyOffset(getRowLength());
+  }
+  
+  /**
+   * @return Family offset
+   */
+  public int getFamilyOffset(int rlength) {
+    return this.offset + ROW_OFFSET + Bytes.SIZEOF_SHORT + rlength + Bytes.SIZEOF_BYTE;
+  }
+  
+  /**
+   * @return Family length
+   */
+  public byte getFamilyLength() {
+    return getFamilyLength(getFamilyOffset());
+  }
+  
+  /**
+   * @return Family length
+   */
+  public byte getFamilyLength(int foffset) {
+    return this.bytes[foffset-1];
+  }
+
+  /**
+   * @return Qualifier offset
+   */
+  public int getQualifierOffset() {
+    return getQualifierOffset(getFamilyOffset());
+  }
+  
+  /**
+   * @return Qualifier offset
+   */
+  public int getQualifierOffset(int foffset) {
+    return foffset + getFamilyLength(foffset);
+  }
+  
+  /**
+   * @return Qualifier length
+   */
+  public int getQualifierLength() {
+    return getQualifierLength(getRowLength(),getFamilyLength());
+  }
+  
+  /**
+   * @return Qualifier length
+   */
+  public int getQualifierLength(int rlength, int flength) {
+    return getKeyLength() - 
+      (KEY_INFRASTRUCTURE_SIZE + rlength + flength);
+  }
+  
+  /**
+   * @return Column (family + qualifier) length
+   */
+  public int getTotalColumnLength() {
+    int rlength = getRowLength();
+    int foffset = getFamilyOffset(rlength);
+    return getTotalColumnLength(rlength,foffset);
+  }
+  
+  /**
+   * @return Column (family + qualifier) length
+   */
+  public int getTotalColumnLength(int rlength, int foffset) {
+    int flength = getFamilyLength(foffset);
+    int qlength = getQualifierLength(rlength,flength);
+    return flength + qlength;
+  }
+  
+  /**
+   * @return Timestamp offset
+   */
+  public int getTimestampOffset() {
+    return getTimestampOffset(getKeyLength());
+  }
+  
+  /**
+   * @param keylength Pass if you have it to save on a int creation.
+   * @return Timestamp offset
+   */
+  public int getTimestampOffset(final int keylength) {
+    return getKeyOffset() + keylength - TIMESTAMP_TYPE_SIZE;
+  }
 
   /**
+   * @return True if this KeyValue has a LATEST_TIMESTAMP timestamp.
+   */
+  public boolean isLatestTimestamp() {
+    return  Bytes.compareTo(getBuffer(), getTimestampOffset(), Bytes.SIZEOF_LONG, 
+      HConstants.LATEST_TIMESTAMP_BYTES, 0, Bytes.SIZEOF_LONG) == 0;
+  }
+
+  public boolean updateLatestStamp(final byte [] now) {
+    int tsOffset = getTimestampOffset();
+    if(Bytes.compareTo(now, 0, Bytes.SIZEOF_LONG, 
+        this.bytes, tsOffset, Bytes.SIZEOF_LONG) < 0) {
+      System.arraycopy(now, 0, this.bytes, tsOffset, Bytes.SIZEOF_LONG);
+      return true;
+    }
+    return false;
+  }
+  
+  //---------------------------------------------------------------------------
+  //
+  //  Methods that return copies of fields
+  //
+  //---------------------------------------------------------------------------
+  
+  /**
+   * @return Copy of the key portion only.  Used compacting and testing.
+   */
+  public byte [] getKey() {
+    int keylength = getKeyLength();
+    byte [] key = new byte[keylength];
+    System.arraycopy(getBuffer(), getKeyOffset(), key, 0, keylength);
+    return key;
+  }
+  
+  /**
+   * Do not use unless you have to.  Use {@link #getBuffer()} with appropriate
+   * offset and lengths instead.
+   * @return Value in a new byte array.
+   */
+  public byte [] getValue() {
+    int o = getValueOffset();
+    int l = getValueLength();
+    byte [] result = new byte[l];
+    System.arraycopy(getBuffer(), o, result, 0, l);
+    return result;
+  }
+  
+  /**
    * Do not use this unless you have to.
    * Use {@link #getBuffer()} with appropriate offsets and lengths instead.
    * @return Row in a new byte array.
@@ -667,24 +909,9 @@
   }
 
   /**
-   * @param keylength Pass if you have it to save on a int creation.
-   * @return Offset into backing buffer at which timestamp starts.
-   */
-  int getTimestampOffset(final int keylength) {
-    return getKeyOffset() + keylength - TIMESTAMP_TYPE_SIZE;
-  }
-
-  /**
-   * @return True if a {@link Type#Delete}.
-   */
-  public boolean isDeleteType() {
-    return getType() == Type.Delete.getCode();
-  }
-
-  /**
    * @return Type of this KeyValue.
    */
-  byte getType() {
+  public byte getType() {
     return getType(getKeyLength());
   }
 
@@ -697,99 +924,170 @@
   }
 
   /**
-   * @return Length of key portion.
+   * @return True if Delete KeyValue type.
    */
-  public int getKeyLength() {
-    return Bytes.toInt(this.bytes, this.offset);
+  public boolean isDeleteType() {
+    return getType() == Type.Delete.code;
   }
 
   /**
-   * @return Value length
+   * @return True if DeleteColumn KeyValue type.
    */
-  public int getValueLength() {
-    return Bytes.toInt(this.bytes, this.offset + Bytes.SIZEOF_INT);
+  public boolean isDeleteColumnType() {
+    return getType() == Type.DeleteColumn.code;
   }
 
   /**
-   * @return Offset into backing buffer at which value starts.
+   * Do not use this unless you have to.
+   * Use {@link #getBuffer()} with appropriate offsets and lengths instead.
+   * @return Returns column. Makes a copy.  Inserts delimiter.
    */
-  public int getValueOffset() {
-    return getKeyOffset() + getKeyLength();
+  public byte [] getColumn() {
+    int fo = getFamilyOffset();
+    int fl = getFamilyLength(fo);
+    int ql = getQualifierLength();
+    byte [] result = new byte[fl + 1 + ql];
+    System.arraycopy(this.bytes, fo, result, 0, fl);
+    result[fl] = COLUMN_FAMILY_DELIMITER;
+    System.arraycopy(this.bytes, fo + fl, result,
+      fl + 1, ql);
+    return result;
   }
 
   /**
-   * Do not use unless you have to.  Use {@link #getBuffer()} with appropriate
-   * offset and lengths instead.
-   * @return Value in a new byte array.
+   * Do not use this unless you have to.
+   * Use {@link #getBuffer()} with appropriate offsets and lengths instead.
+   * @return Returns family. Makes a copy.
    */
-  public byte [] getValue() {
-    int o = getValueOffset();
-    int l = getValueLength();
+  public byte [] getFamily() {
+    int o = getFamilyOffset();
+    int l = getFamilyLength(o);
     byte [] result = new byte[l];
-    System.arraycopy(getBuffer(), o, result, 0, l);
+    System.arraycopy(this.bytes, o, result, 0, l);
     return result;
   }
 
   /**
-   * @return Offset into backing buffer at which the column begins
-   */
-  public int getColumnOffset() {
-    return getColumnOffset(getRowLength());
-  }
-
-  /**
-   * @param rowlength - length of row.
-   * @return Offset into backing buffer at which the column begins
-   */
-  public int getColumnOffset(final int rowlength) {
-    return getRowOffset() + rowlength + 1;
-  }
-
-  /**
-   * @param columnoffset Pass if you have it to save on an int creation.
-   * @return Length of family portion of column.
+   * Do not use this unless you have to.
+   * Use {@link #getBuffer()} with appropriate offsets and lengths instead.
+   * @return Returns qualifier. Makes a copy.
    */
-  int getFamilyLength(final int columnoffset) {
-    return this.bytes[columnoffset - 1];
+  public byte [] getQualifier() {
+    int o = getQualifierOffset();
+    int l = getQualifierLength();
+    byte [] result = new byte[l];
+    System.arraycopy(this.bytes, o, result, 0, l);
+    return result;
   }
 
+  //---------------------------------------------------------------------------
+  //
+  //  KeyValue splitter
+  //
+  //---------------------------------------------------------------------------
+  
   /**
-   * @param columnoffset Pass if you have it to save on an int creation.
-   * @return Length of column.
-   */
-  public int getColumnLength(final int columnoffset) {
-    return getColumnLength(columnoffset, getKeyLength());
+   * Utility class that splits a KeyValue buffer into separate byte arrays.
+   * <p>
+   * Should get rid of this if we can, but is very useful for debugging.
+   */
+  public static class SplitKeyValue {
+    private byte [][] split;
+    SplitKeyValue() {
+      this.split = new byte[6][];
+    }
+    public void setRow(byte [] value) { this.split[0] = value; }
+    public void setFamily(byte [] value) { this.split[1] = value; }
+    public void setQualifier(byte [] value) { this.split[2] = value; }
+    public void setTimestamp(byte [] value) { this.split[3] = value; }
+    public void setType(byte [] value) { this.split[4] = value; }
+    public void setValue(byte [] value) { this.split[5] = value; }
+    public byte [] getRow() { return this.split[0]; }
+    public byte [] getFamily() { return this.split[1]; }
+    public byte [] getQualifier() { return this.split[2]; }
+    public byte [] getTimestamp() { return this.split[3]; }
+    public byte [] getType() { return this.split[4]; }
+    public byte [] getValue() { return this.split[5]; }
   }
-
-  int getColumnLength(final int columnoffset, final int keylength) {
-    return (keylength + ROW_OFFSET) - (columnoffset - this.offset) -
-      TIMESTAMP_TYPE_SIZE;
+  
+  public SplitKeyValue split() {
+    SplitKeyValue split = new SplitKeyValue();
+    int splitOffset = this.offset;
+    int keyLen = Bytes.toInt(bytes, splitOffset);
+    splitOffset += Bytes.SIZEOF_INT;
+    int valLen = Bytes.toInt(bytes, splitOffset);
+    splitOffset += Bytes.SIZEOF_INT;
+    short rowLen = Bytes.toShort(bytes, splitOffset);
+    splitOffset += Bytes.SIZEOF_SHORT;
+    byte [] row = new byte[rowLen];
+    System.arraycopy(bytes, splitOffset, row, 0, rowLen);
+    splitOffset += rowLen;
+    split.setRow(row);
+    byte famLen = bytes[splitOffset];
+    splitOffset += Bytes.SIZEOF_BYTE;
+    byte [] family = new byte[famLen];
+    System.arraycopy(bytes, splitOffset, family, 0, famLen);
+    splitOffset += famLen;
+    split.setFamily(family);
+    int colLen = keyLen -
+      (rowLen + famLen + Bytes.SIZEOF_SHORT + Bytes.SIZEOF_BYTE +
+      Bytes.SIZEOF_LONG + Bytes.SIZEOF_BYTE);
+    byte [] qualifier = new byte[colLen];
+    System.arraycopy(bytes, splitOffset, qualifier, 0, colLen);
+    splitOffset += colLen;
+    split.setQualifier(qualifier);
+    byte [] timestamp = new byte[Bytes.SIZEOF_LONG];
+    System.arraycopy(bytes, splitOffset, timestamp, 0, Bytes.SIZEOF_LONG);
+    splitOffset += Bytes.SIZEOF_LONG;
+    split.setTimestamp(timestamp);
+    byte [] type = new byte[1];
+    type[0] = bytes[splitOffset];
+    splitOffset += Bytes.SIZEOF_BYTE;
+    split.setType(type);
+    byte [] value = new byte[valLen];
+    System.arraycopy(bytes, splitOffset, value, 0, valLen);
+    split.setValue(value);
+    return split;
   }
-
+  
+  //---------------------------------------------------------------------------
+  //
+  //  Compare specified fields against those contained in this KeyValue 
+  //
+  //---------------------------------------------------------------------------
+  
   /**
    * @param family
    * @return True if matching families.
    */
   public boolean matchingFamily(final byte [] family) {
-    int o = getColumnOffset();
-    // Family length byte is just before the column starts.
-    int l = this.bytes[o - 1];
+    int o = getFamilyOffset();
+    int l = getFamilyLength(o);
     return Bytes.compareTo(family, 0, family.length, this.bytes, o, l) == 0;
   }
 
   /**
+   * @param qualifier
+   * @return True if matching qualifiers.
+   */
+  public boolean matchingQualifier(final byte [] qualifier) {
+    int o = getQualifierOffset();
+    int l = getQualifierLength();
+    return Bytes.compareTo(qualifier, 0, qualifier.length, 
+        this.bytes, o, l) == 0;
+  }
+
+  /**
    * @param column Column minus its delimiter
-   * @param familylength Length of family in passed <code>column</code>
    * @return True if column matches.
    * @see #matchingColumn(byte[])
    */
-  public boolean matchingColumnNoDelimiter(final byte [] column,
-      final int familylength) {
-    int o = getColumnOffset();
-    int l = getColumnLength(o);
-    int f = getFamilyLength(o);
-    return compareColumns(getBuffer(), o, l, f,
-      column, 0, column.length, familylength) == 0;
+  public boolean matchingColumnNoDelimiter(final byte [] column) {
+    int rl = getRowLength();
+    int o = getFamilyOffset(rl);
+    int fl = getFamilyLength(o);
+    int l = fl + getQualifierLength(rl,fl);
+    return Bytes.compareTo(column, 0, column.length, this.bytes, o, l) == 0;
   }
 
   /**
@@ -798,14 +1096,40 @@
    */
   public boolean matchingColumn(final byte [] column) {
     int index = getFamilyDelimiterIndex(column, 0, column.length);
-    int o = getColumnOffset();
-    int l = getColumnLength(o);
-    int result = Bytes.compareTo(getBuffer(), o, index, column, 0, index);
-    if (result != 0) {
+    int rl = getRowLength();
+    int o = getFamilyOffset(rl);
+    int fl = getFamilyLength(o);
+    int ql = getQualifierLength(rl,fl);
+    if(Bytes.compareTo(column, 0, index, this.bytes, o, fl) != 0) {
+      return false;
+    }
+    return Bytes.compareTo(column, index + 1, column.length - (index + 1),
+        this.bytes, o + fl, ql) == 0;
+  }
+
+  /**
+   *
+   * @param family column family
+   * @param qualifier column qualifier
+   * @return True if column matches
+   */
+  public boolean matchingColumn(final byte[] family, final byte[] qualifier) {
+    int rl = getRowLength();
+    int o = getFamilyOffset(rl);
+    int fl = getFamilyLength(o);
+    int ql = getQualifierLength(rl,fl);
+    if(Bytes.compareTo(family, 0, family.length, this.bytes, o, family.length)
+        != 0) {
       return false;
     }
-    return Bytes.compareTo(getBuffer(), o + index, l - index,
-      column, index + 1, column.length - (index + 1)) == 0;
+    if(qualifier == null || qualifier.length == 0) {
+      if(ql == 0) {
+        return true;
+      }
+      return false;
+    }
+    return Bytes.compareTo(qualifier, 0, qualifier.length,
+        this.bytes, o + fl, ql) == 0;
   }
 
   /**
@@ -817,7 +1141,7 @@
    * @param roffset
    * @param rlength
    * @param rfamilylength Offset of family delimiter in right column.
-   * @return
+   * @return The result of the comparison.
    */
   static int compareColumns(final byte [] left, final int loffset,
       final int llength, final int lfamilylength,
@@ -843,41 +1167,33 @@
   }
 
   /**
-   * @return Returns column String with delimiter added back. Expensive!
+   * @return True if column is empty.
    */
-  public String getColumnString() {
-    int o = getColumnOffset();
-    int l = getColumnLength(o);
-    int familylength = getFamilyLength(o);
-    return Bytes.toString(this.bytes, o, familylength) +
-      COLUMN_FAMILY_DELIMITER + Bytes.toString(this.bytes,
-       o + familylength, l - familylength);
+  public boolean isEmptyColumn() {
+    return getQualifierLength() == 0;
   }
 
   /**
-   * Do not use this unless you have to.
-   * Use {@link #getBuffer()} with appropriate offsets and lengths instead.
-   * @return Returns column. Makes a copy.  Inserts delimiter.
-   */
-  public byte [] getColumn() {
-    int o = getColumnOffset();
-    int l = getColumnLength(o);
-    int familylength = getFamilyLength(o);
-    byte [] result = new byte[l + 1];
-    System.arraycopy(getBuffer(), o, result, 0, familylength);
-    result[familylength] = COLUMN_FAMILY_DELIMITER;
-    System.arraycopy(getBuffer(), o + familylength, result,
-      familylength + 1, l - familylength);
+   * Splits a column in family:qualifier form into separate byte arrays.
+   * 
+   * @param c  The column.
+   * @return The parsed column.
+   */
+  public static byte [][] parseColumn(byte [] c) {
+    final byte [][] result = new byte [2][];
+    final int index = getFamilyDelimiterIndex(c, 0, c.length);
+    if (index == -1) {
+      throw new IllegalArgumentException("Impossible column name: " + c);
+    }
+    result[0] = new byte [index];
+    System.arraycopy(c, 0, result[0], 0, index);
+    final int len = c.length - (index + 1);
+    result[1] = new byte[len];
+    System.arraycopy(c, index + 1 /*Skip delimiter*/, result[1], 0,
+      len);
     return result;
   }
-
-  /**
-   * @return True if column is empty.
-   */
-  public boolean isEmptyColumn() {
-    return getColumnLength(getColumnOffset()) == 0;
-  }
-
+  
   /**
    * @param b
    * @return Index of the family-qualifier colon delimiter character in passed
@@ -1026,7 +1342,8 @@
      * @return Result comparing rows.
      */
     public int compareRows(final KeyValue left, final KeyValue right) {
-      return compareRows(left, left.getRowLength(), right, right.getRowLength());
+      return compareRows(left, left.getRowLength(), right, 
+          right.getRowLength());
     }
 
     /**
@@ -1058,28 +1375,27 @@
       return getRawComparator().compareRows(left, loffset, llength,
         right, roffset, rlength);
     }
-
+    
     public int compareColumns(final KeyValue left, final byte [] right,
         final int roffset, final int rlength, final int rfamilyoffset) {
-      int offset = left.getColumnOffset();
-      int length = left.getColumnLength(offset);
+      int offset = left.getFamilyOffset();
+      int length = left.getFamilyLength() + left.getQualifierLength();
       return getRawComparator().compareColumns(left.getBuffer(), offset, length,
         left.getFamilyLength(offset),
         right, roffset, rlength, rfamilyoffset);
     }
 
     int compareColumns(final KeyValue left, final short lrowlength,
-        final int lkeylength, final KeyValue right, final short rrowlength,
-        final int rkeylength) {
-      int loffset = left.getColumnOffset(lrowlength);
-      int roffset = right.getColumnOffset(rrowlength);
-      int llength = left.getColumnLength(loffset, lkeylength);
-      int rlength = right.getColumnLength(roffset, rkeylength);
-      int lfamilylength = left.getFamilyLength(loffset);
-      int rfamilylength = right.getFamilyLength(roffset);
-      return getRawComparator().compareColumns(left.getBuffer(), loffset,
-          llength, lfamilylength,
-        right.getBuffer(), roffset, rlength, rfamilylength);
+        final KeyValue right, final short rrowlength) {
+      int lfoffset = left.getFamilyOffset(lrowlength);
+      int rfoffset = right.getFamilyOffset(rrowlength);
+      int lclength = left.getTotalColumnLength(lrowlength,lfoffset);
+      int rclength = right.getTotalColumnLength(rrowlength, rfoffset);
+      int lfamilylength = left.getFamilyLength(lfoffset);
+      int rfamilylength = right.getFamilyLength(rfoffset);
+      return getRawComparator().compareColumns(left.getBuffer(), lfoffset,
+          lclength, lfamilylength,
+        right.getBuffer(), rfoffset, rclength, rfamilylength);
     }
 
     /**
@@ -1095,10 +1411,7 @@
       if (!matchingRows(left, lrowlength, right, rrowlength)) {
         return false;
       }
-      int lkeylength = left.getKeyLength();
-      int rkeylength = right.getKeyLength();
-      return compareColumns(left, lrowlength, lkeylength,
-        right, rrowlength, rkeylength) == 0;
+      return compareColumns(left, lrowlength, right, rrowlength) == 0;
     }
 
     /**
@@ -1140,7 +1453,8 @@
     public boolean matchingRows(final byte [] left, final int loffset,
         final int llength,
         final byte [] right, final int roffset, final int rlength) {
-      int compare = compareRows(left, loffset, llength, right, roffset, rlength);
+      int compare = compareRows(left, loffset, llength, 
+          right, roffset, rlength);
       if (compare != 0) {
         return false;
       }
@@ -1171,7 +1485,6 @@
  
     /**
      * @return Comparator that ignores timestamps; useful counting versions.
-     * @throws IOException
      */
     public KVComparator getComparatorIgnoringTimestamps() {
       KVComparator c = null;
@@ -1214,20 +1527,44 @@
    */
   public static KeyValue createFirstOnRow(final byte [] row,
       final long ts) {
-    return createFirstOnRow(row, null, ts);
+    return new KeyValue(row, null, null, ts, Type.Maximum);
   }
 
   /**
    * @param row - row key (arbitrary byte array)
    * @param ts - timestamp
-   * @return First possible key on passed <code>row</code>, column and timestamp.
+   * @return First possible key on passed <code>row</code>, column and timestamp
    */
   public static KeyValue createFirstOnRow(final byte [] row, final byte [] c,
       final long ts) {
-    return new KeyValue(row, c, ts, Type.Maximum);
+    byte [][] split = parseColumn(c);
+    return new KeyValue(row, split[0], split[1], ts, Type.Maximum);
+  }
+
+  /**
+   * @param row - row key (arbitrary byte array)
+   * @param f - family name
+   * @param q - column qualifier
+   * @return First possible key on passed <code>row</code>, and column.
+   */
+  public static KeyValue createFirstOnRow(final byte [] row, final byte [] f,
+      final byte [] q) {
+    return new KeyValue(row, f, q, HConstants.LATEST_TIMESTAMP, Type.Maximum);
   }
 
   /**
+   * @param row - row key (arbitrary byte array)
+   * @param f - family name
+   * @param q - column qualifier
+   * @param ts - timestamp
+   * @return First possible key on passed <code>row</code>, column and timestamp
+   */
+  public static KeyValue createFirstOnRow(final byte [] row, final byte [] f,
+      final byte [] q, final long ts) {
+    return new KeyValue(row, f, q, ts, Type.Maximum);
+  }
+  
+  /**
    * @param b
    * @param o
    * @param l
@@ -1255,7 +1592,8 @@
       //          "---" + Bytes.toString(right, roffset, rlength));
       final int metalength = 7; // '.META.' length
       int lmetaOffsetPlusDelimiter = loffset + metalength;
-      int leftFarDelimiter = getDelimiterInReverse(left, lmetaOffsetPlusDelimiter,
+      int leftFarDelimiter = getDelimiterInReverse(left, 
+          lmetaOffsetPlusDelimiter,
           llength - metalength, HRegionInfo.DELIMITER);
       int rmetaOffsetPlusDelimiter = roffset + metalength;
       int rightFarDelimiter = getDelimiterInReverse(right,
@@ -1363,6 +1701,9 @@
         return compare;
       }
 
+      // if row matches, and no column in the 'left' AND put type is 'minimum',
+      // then return that left is larger than right.
+
       // Compare column family.  Start compare past row and family length.
       int lcolumnoffset = Bytes.SIZEOF_SHORT + lrowlength + 1 + loffset;
       int rcolumnoffset = Bytes.SIZEOF_SHORT + rrowlength + 1 + roffset;
@@ -1370,12 +1711,24 @@
         (lcolumnoffset - loffset);
       int rcolumnlength = rlength - TIMESTAMP_TYPE_SIZE -
         (rcolumnoffset - roffset);
+
+      // This supports 'last key on a row' - the magic is if there is no column in the
+      // left operand, and the left operand has a type of '0' - magical value,
+      // then we say the left is bigger.  This will let us seek to the last key in
+      // a row.
+
+      byte ltype = left[loffset + (llength - 1)];
+
+      if (lcolumnlength == 0 && ltype == Type.Minimum.getCode()) {
+        return 1; // left is bigger.
+      }
+
       compare = Bytes.compareTo(left, lcolumnoffset, lcolumnlength, right,
           rcolumnoffset, rcolumnlength);
       if (compare != 0) {
         return compare;
       }
-
+      
       if (!this.ignoreTimestamp) {
         // Get timestamps.
         long ltimestamp = Bytes.toLong(left,
@@ -1391,7 +1744,8 @@
       if (!this.ignoreType) {
         // Compare types. Let the delete types sort ahead of puts; i.e. types
         // of higher numbers sort before those of lesser numbers
-        byte ltype = left[loffset + (llength - 1)];
+
+        // ltype is defined above
         byte rtype = right[roffset + (rlength - 1)];
         return (0xff & rtype) - (0xff & ltype);
       }
@@ -1402,7 +1756,7 @@
       return compare(left, 0, left.length, right, 0, right.length);
     }
 
-    protected int compareRows(byte [] left, int loffset, int llength,
+    public int compareRows(byte [] left, int loffset, int llength,
         byte [] right, int roffset, int rlength) {
       return Bytes.compareTo(left, loffset, llength, right, roffset, rlength);
     }
@@ -1430,7 +1784,9 @@
   
   // HeapSize
   public long heapSize() {
-    return this.length;
+    int dataLen = bytes.length + (bytes.length % 8);
+    return HeapSize.OBJECT + HeapSize.BYTE_ARRAY + dataLen +
+      (2 * HeapSize.INT);
   }
   
   // Writable

Modified: hadoop/hbase/trunk_on_hadoop-0.18.3/src/java/org/apache/hadoop/hbase/RegionHistorian.java
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk_on_hadoop-0.18.3/src/java/org/apache/hadoop/hbase/RegionHistorian.java?rev=785076&r1=785075&r2=785076&view=diff
==============================================================================
--- hadoop/hbase/trunk_on_hadoop-0.18.3/src/java/org/apache/hadoop/hbase/RegionHistorian.java (original)
+++ hadoop/hbase/trunk_on_hadoop-0.18.3/src/java/org/apache/hadoop/hbase/RegionHistorian.java Tue Jun 16 04:33:56 2009
@@ -29,8 +29,10 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hbase.client.Get;
 import org.apache.hadoop.hbase.client.HTable;
-import org.apache.hadoop.hbase.io.BatchUpdate;
+import org.apache.hadoop.hbase.client.Put;
+import org.apache.hadoop.hbase.client.Result;
 import org.apache.hadoop.hbase.io.Cell;
 import org.apache.hadoop.hbase.util.Bytes;
 
@@ -57,17 +59,17 @@
   "EEE, d MMM yyyy HH:mm:ss");
 
   
-  private static enum HistorianColumnKey  {
-    REGION_CREATION ( Bytes.toBytes(COLUMN_FAMILY_HISTORIAN_STR+"creation")),
-    REGION_OPEN ( Bytes.toBytes(COLUMN_FAMILY_HISTORIAN_STR+"open")),
-    REGION_SPLIT ( Bytes.toBytes(COLUMN_FAMILY_HISTORIAN_STR+"split")),
-    REGION_COMPACTION ( Bytes.toBytes(COLUMN_FAMILY_HISTORIAN_STR+"compaction")),
-    REGION_FLUSH ( Bytes.toBytes(COLUMN_FAMILY_HISTORIAN_STR+"flush")),
-    REGION_ASSIGNMENT ( Bytes.toBytes(COLUMN_FAMILY_HISTORIAN_STR+"assignment"));
+  private static enum HistorianQualifierKey  {
+    REGION_CREATION ( Bytes.toBytes("creation")),
+    REGION_OPEN ( Bytes.toBytes("open")),
+    REGION_SPLIT ( Bytes.toBytes("split")),
+    REGION_COMPACTION ( Bytes.toBytes("compaction")),
+    REGION_FLUSH ( Bytes.toBytes("flush")),
+    REGION_ASSIGNMENT ( Bytes.toBytes("assignment"));
 
-  byte[] key;
+    byte[] key;
 
-    HistorianColumnKey(byte[] key) {
+    HistorianQualifierKey(byte[] key) {
       this.key = key;
     }
   } 
@@ -101,7 +103,7 @@
    *          Region name as a string
    * @return List of RegionHistoryInformation or null if we're offline.
    */
-  public List<RegionHistoryInformation> getRegionHistory(String regionName) {
+  public List<RegionHistoryInformation> getRegionHistory(byte [] regionName) {
     if (!isOnline()) {
       return null;
     }
@@ -113,15 +115,17 @@
        * moment to retrieve all version and to have the column key information.
        * To be changed when HTable.getRow handles versions.
        */
-      for (HistorianColumnKey keyEnu : HistorianColumnKey.values()) {
+      for (HistorianQualifierKey keyEnu : HistorianQualifierKey.values()) {
         byte[] columnKey = keyEnu.key;
-        Cell[] cells = this.metaTable.get(Bytes.toBytes(regionName),
-            columnKey, ALL_VERSIONS);
-        if (cells != null) {
-          for (Cell cell : cells) {
-            informations.add(historian.new RegionHistoryInformation(cell
-                .getTimestamp(), Bytes.toString(columnKey).split(":")[1], Bytes
-                .toString(cell.getValue())));
+        Get get = new Get(regionName);
+        get.addColumn(CATALOG_HISTORIAN_FAMILY, columnKey);
+        get.setMaxVersions(ALL_VERSIONS);
+        Result result = this.metaTable.get(get);
+
+        if (result != null) {
+          for(KeyValue kv : result.raw()) {
+            informations.add(historian.new RegionHistoryInformation(
+                kv.getTimestamp(), columnKey, kv.getValue()));
           }
         }
       }
@@ -138,7 +142,7 @@
    * @param serverName
    */
   public void addRegionAssignment(HRegionInfo info, String serverName) {
-    add(HistorianColumnKey.REGION_ASSIGNMENT.key, "Region assigned to server "
+    add(HistorianQualifierKey.REGION_ASSIGNMENT.key, "Region assigned to server "
         + serverName, info);
   }
 
@@ -147,7 +151,7 @@
    * @param info
    */
   public void addRegionCreation(HRegionInfo info) {
-    add(HistorianColumnKey.REGION_CREATION.key, "Region creation", info);
+    add(HistorianQualifierKey.REGION_CREATION.key, "Region creation", info);
   }
 
   /**
@@ -156,7 +160,7 @@
    * @param address
    */
   public void addRegionOpen(HRegionInfo info, HServerAddress address) {
-    add(HistorianColumnKey.REGION_OPEN.key, "Region opened on server : "
+    add(HistorianQualifierKey.REGION_OPEN.key, "Region opened on server : "
         + address.getHostname(), info);
   }
 
@@ -171,7 +175,7 @@
      HRegionInfo newInfo2) {
     HRegionInfo[] infos = new HRegionInfo[] { newInfo1, newInfo2 };
     for (HRegionInfo info : infos) {
-      add(HistorianColumnKey.REGION_SPLIT.key, SPLIT_PREFIX +
+      add(HistorianQualifierKey.REGION_SPLIT.key, SPLIT_PREFIX +
         oldInfo.getRegionNameAsString(), info);
     }
   }
@@ -188,7 +192,7 @@
     // such danger compacting; compactions are not allowed when
     // Flusher#flushSomeRegions is run.
     if (LOG.isDebugEnabled()) {
-      add(HistorianColumnKey.REGION_COMPACTION.key,
+      add(HistorianQualifierKey.REGION_COMPACTION.key,
         "Region compaction completed in " + timeTaken, info);
     }
   }
@@ -211,9 +215,8 @@
    * @param text
    * @param info
    */
-  private void add(byte[] column,
-      String text, HRegionInfo info) {
-    add(column, text, info, LATEST_TIMESTAMP);
+  private void add(byte [] qualifier, String text, HRegionInfo info) {
+    add(qualifier, text, info, LATEST_TIMESTAMP);
   }
 
   /**
@@ -223,18 +226,19 @@
    * @param info
    * @param timestamp
    */
-  private void add(byte[] column,
-      String text, HRegionInfo info, long timestamp) {
+  private void add(byte [] qualifier, String text, HRegionInfo info,
+      long timestamp) {
     if (!isOnline()) {
       // Its a noop
       return;
     }
     if (!info.isMetaRegion()) {
-      BatchUpdate batch = new BatchUpdate(info.getRegionName());
-      batch.setTimestamp(timestamp);
-      batch.put(column, Bytes.toBytes(text));
+      Put put = new Put(info.getRegionName());
+      put.setTimeStamp(timestamp);
+      put.add(HConstants.CATALOG_HISTORIAN_FAMILY, qualifier,
+          Bytes.toBytes(text));
       try {
-        this.metaTable.commit(batch);
+        this.metaTable.put(put);
       } catch (IOException ioe) {
         LOG.warn("Unable to '" + text + "'", ioe);
       }
@@ -252,34 +256,35 @@
 
     private long timestamp;
 
-    private String event;
+    private byte [] event = null;
 
-    private String description;
+    private byte [] description = null;
 
     /**
      * @param timestamp
      * @param event
      * @param description
      */
-    public RegionHistoryInformation(long timestamp, String event,
-        String description) {
+    public RegionHistoryInformation(long timestamp, byte [] event,
+        byte [] description) {
       this.timestamp = timestamp;
       this.event = event;
       this.description = description;
     }
-
+    
+    
     public int compareTo(RegionHistoryInformation otherInfo) {
       return -1 * Long.valueOf(timestamp).compareTo(otherInfo.getTimestamp());
     }
 
     /** @return the event */
     public String getEvent() {
-      return event;
+      return Bytes.toString(event);
     }
 
     /** @return the description */
     public String getDescription() {
-      return description;
+      return Bytes.toString(description);
     }
 
     /** @return the timestamp */

Modified: hadoop/hbase/trunk_on_hadoop-0.18.3/src/java/org/apache/hadoop/hbase/WritableComparator.java
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk_on_hadoop-0.18.3/src/java/org/apache/hadoop/hbase/WritableComparator.java?rev=785076&r1=785075&r2=785076&view=diff
==============================================================================
--- hadoop/hbase/trunk_on_hadoop-0.18.3/src/java/org/apache/hadoop/hbase/WritableComparator.java (original)
+++ hadoop/hbase/trunk_on_hadoop-0.18.3/src/java/org/apache/hadoop/hbase/WritableComparator.java Tue Jun 16 04:33:56 2009
@@ -23,6 +23,7 @@
 
 import org.apache.hadoop.io.Writable;
 
-public interface WritableComparator<T> extends Writable, Comparator<T> {
-// No methods, just bring the two interfaces together
-}
+/**
+ * Interface that brings writable and comparable together
+ */
+public interface WritableComparator<T> extends Writable, Comparator<T> {}

Added: hadoop/hbase/trunk_on_hadoop-0.18.3/src/java/org/apache/hadoop/hbase/client/Delete.java
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk_on_hadoop-0.18.3/src/java/org/apache/hadoop/hbase/client/Delete.java?rev=785076&view=auto
==============================================================================
--- hadoop/hbase/trunk_on_hadoop-0.18.3/src/java/org/apache/hadoop/hbase/client/Delete.java (added)
+++ hadoop/hbase/trunk_on_hadoop-0.18.3/src/java/org/apache/hadoop/hbase/client/Delete.java Tue Jun 16 04:33:56 2009
@@ -0,0 +1,313 @@
+/*
+ * Copyright 2009 The Apache Software Foundation
+ *
+ * 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.hadoop.hbase.client;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.hadoop.io.Writable;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.KeyValue;
+import org.apache.hadoop.hbase.util.Bytes;
+
+/**
+ * Used to perform Delete operations on a single row.
+ * <p>
+ * To delete an entire row, instantiate a Delete object with the row 
+ * to delete.  To further define the scope of what to delete, perform
+ * additional methods as outlined below.
+ * <p>
+ * To delete specific families, execute {@link #deleteFamily(byte []) deleteFamily}
+ * for each family to delete.
+ * <p>
+ * To delete multiple versions of specific columns, execute
+ * {@link #deleteColumns(byte [],byte []) deleteColumns}
+ * for each column to delete.  
+ * <p>
+ * To delete specific versions of specific columns, execute
+ * {@link #deleteColumn(byte [],byte [],long) deleteColumn}
+ * for each column version to delete.
+ * <p>
+ * Specifying timestamps calling constructor, deleteFamily, and deleteColumns
+ * will delete all versions with a timestamp less than or equal to that
+ * specified.  Specifying a timestamp to deleteColumn will delete versions
+ * only with a timestamp equal to that specified.
+ * <p>The timestamp passed to the constructor is only used ONLY for delete of
+ * rows.  For anything less -- a deleteColumn, deleteColumns or
+ * deleteFamily -- then you need to use the method overrides that take a
+ * timestamp.  The constructor timestamp is not referenced.
+ */
+public class Delete implements Writable {
+  private byte [] row = null;
+  // This ts is only used when doing a deleteRow.  Anything less, 
+  private long ts;
+  private long lockId = -1L;
+  private final Map<byte [], List<KeyValue>> familyMap = 
+    new TreeMap<byte [], List<KeyValue>>(Bytes.BYTES_COMPARATOR);
+
+  /** Constructor for Writable.  DO NOT USE */
+  public Delete() {
+    this(null);
+  }
+
+  /**
+   * Create a Delete operation for the specified row.
+   * <p>
+   * If no further operations are done, this will delete everything
+   * associated with the specified row (all versions of all columns in all
+   * families).
+   * @param row row key
+   */
+  public Delete(byte [] row) {
+    this(row, HConstants.LATEST_TIMESTAMP, null);
+  }
+
+  /**
+   * Create a Delete operation for the specified row and timestamp, using
+   * an optional row lock.
+   * <p>
+   * If no further operations are done, this will delete all columns in all
+   * families of the specified row with a timestamp less than or equal to the 
+   * specified timestamp.
+   * @param row row key
+   * @param timestamp maximum version timestamp
+   * @param rowLock previously acquired row lock, or null
+   */
+  public Delete(byte [] row, long timestamp, RowLock rowLock) {
+    this.row = row;
+    this.ts = timestamp;
+    if (rowLock != null) {
+    	this.lockId = rowLock.getLockId();
+    }
+  }
+
+  /**
+   * Delete all versions of all columns of the specified family.
+   * <p>
+   * Overrides previous calls to deleteColumn and deleteColumns for the
+   * specified family.
+   * @param family family name
+   */
+  public void deleteFamily(byte [] family) {
+	this.deleteFamily(family, HConstants.LATEST_TIMESTAMP);
+  }
+
+  /**
+   * Delete all columns of the specified family with a timestamp less than
+   * or equal to the specified timestamp.
+   * <p>
+   * Overrides previous calls to deleteColumn and deleteColumns for the
+   * specified family.
+   * @param family family name
+   * @param timestamp maximum version timestamp
+   */
+  public void deleteFamily(byte [] family, long timestamp) {
+    List<KeyValue> list = familyMap.get(family);
+    if(list == null) {
+      list = new ArrayList<KeyValue>();
+    } else if(!list.isEmpty()) {
+      list.clear();
+    }
+    list.add(new KeyValue(row, family, null, timestamp, KeyValue.Type.DeleteFamily));
+    familyMap.put(family, list);
+  }
+  
+  /**
+   * Delete all versions of the specified column.
+   * @param family family name
+   * @param qualifier column qualifier
+   */
+  public void deleteColumns(byte [] family, byte [] qualifier) {
+    this.deleteColumns(family, qualifier, HConstants.LATEST_TIMESTAMP);
+  }
+  
+  /**
+   * Delete all versions of the specified column with a timestamp less than
+   * or equal to the specified timestamp.
+   * @param family family name
+   * @param qualifier column qualifier
+   * @param timestamp maximum version timestamp
+   */
+  public void deleteColumns(byte [] family, byte [] qualifier, long timestamp) {
+    List<KeyValue> list = familyMap.get(family);
+    if (list == null) {
+      list = new ArrayList<KeyValue>();
+    }
+    list.add(new KeyValue(this.row, family, qualifier, timestamp,
+      KeyValue.Type.DeleteColumn));
+    familyMap.put(family, list);
+  }
+  
+  /**
+   * Delete the latest version of the specified column.
+   * This is an expensive call in that on the server-side, it first does a
+   * get to find the latest versions timestamp.  Then it adds a delete using
+   * the fetched cells timestamp.
+   * @param family family name
+   * @param qualifier column qualifier
+   */
+  public void deleteColumn(byte [] family, byte [] qualifier) {
+    this.deleteColumn(family, qualifier, HConstants.LATEST_TIMESTAMP);
+  }
+  
+  /**
+   * Delete the specified version of the specified column.
+   * @param family family name
+   * @param qualifier column qualifier
+   * @param timestamp version timestamp
+   */
+  public void deleteColumn(byte [] family, byte [] qualifier, long timestamp) {
+    List<KeyValue> list = familyMap.get(family);
+    if(list == null) {
+      list = new ArrayList<KeyValue>();
+    }
+    list.add(new KeyValue(
+        this.row, family, qualifier, timestamp, KeyValue.Type.Delete));
+    familyMap.put(family, list);
+  }
+  
+  /**
+   * Delete the latest version of the specified column, given in
+   * <code>family:qualifier</code> notation.
+   * @param column colon-delimited family and qualifier 
+   */
+  public void deleteColumn(byte [] column) {
+    byte [][] parts = KeyValue.parseColumn(column);
+    this.deleteColumn(parts[0], parts[1], HConstants.LATEST_TIMESTAMP);
+  }
+  
+  /**
+   * Method for retrieving the delete's familyMap 
+   * @return familyMap
+   */
+  public Map<byte [], List<KeyValue>> getFamilyMap() {
+    return this.familyMap;
+  }
+  
+  /**
+   *  Method for retrieving the delete's row
+   * @return row
+   */
+  public byte [] getRow() {
+    return this.row;
+  }
+  
+  /**
+   * Method for retrieving the delete's RowLock
+   * @return RowLock
+   */
+  public RowLock getRowLock() {
+    return new RowLock(this.row, this.lockId);
+  }
+  
+  /**
+   * Method for retrieving the delete's lock ID.
+   * 
+   * @return The lock ID.
+   */
+  public long getLockId() {
+	return this.lockId;
+  }
+  
+  /**
+   * Method for retrieving the delete's timestamp
+   * @return timestamp
+   */
+  public long getTimeStamp() {
+    return this.ts;
+  }
+  
+  /**
+   * @return string
+   */
+  @Override
+  public String toString() {
+    StringBuffer sb = new StringBuffer();
+    sb.append("row=");
+    sb.append(Bytes.toString(this.row));
+    sb.append(", ts=");
+    sb.append(this.ts);
+    sb.append(", families={");
+    boolean moreThanOne = false;
+    for(Map.Entry<byte [], List<KeyValue>> entry : this.familyMap.entrySet()) {
+      if(moreThanOne) {
+        sb.append(", ");
+      } else {
+        moreThanOne = true;
+      }
+      sb.append("(family=");
+      sb.append(Bytes.toString(entry.getKey()));
+      sb.append(", keyvalues=(");
+      boolean moreThanOneB = false;
+      for(KeyValue kv : entry.getValue()) {
+        if(moreThanOneB) {
+          sb.append(", ");
+        } else {
+          moreThanOneB = true;
+        }
+        sb.append(kv.toString());
+      }
+      sb.append(")");
+    }
+    sb.append("}");
+    return sb.toString();
+  }
+  
+  //Writable
+  public void readFields(final DataInput in) throws IOException {
+    this.row = Bytes.readByteArray(in);
+    this.ts = in.readLong();
+    this.lockId = in.readLong();
+    this.familyMap.clear();
+    int numFamilies = in.readInt();
+    for(int i=0;i<numFamilies;i++) {
+      byte [] family = Bytes.readByteArray(in);
+      int numColumns = in.readInt();
+      List<KeyValue> list = new ArrayList<KeyValue>(numColumns);
+      for(int j=0;j<numColumns;j++) {
+    	KeyValue kv = new KeyValue();
+    	kv.readFields(in);
+    	list.add(kv);
+      }
+      this.familyMap.put(family, list);
+    }
+  }  
+  
+  public void write(final DataOutput out) throws IOException {
+    Bytes.writeByteArray(out, this.row);
+    out.writeLong(this.ts);
+    out.writeLong(this.lockId);
+    out.writeInt(familyMap.size());
+    for(Map.Entry<byte [], List<KeyValue>> entry : familyMap.entrySet()) {
+      Bytes.writeByteArray(out, entry.getKey());
+      List<KeyValue> list = entry.getValue();
+      out.writeInt(list.size());
+      for(KeyValue kv : list) {
+        kv.write(out);
+      }
+    }
+  }
+}
\ No newline at end of file

Added: hadoop/hbase/trunk_on_hadoop-0.18.3/src/java/org/apache/hadoop/hbase/client/Get.java
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk_on_hadoop-0.18.3/src/java/org/apache/hadoop/hbase/client/Get.java?rev=785076&view=auto
==============================================================================
--- hadoop/hbase/trunk_on_hadoop-0.18.3/src/java/org/apache/hadoop/hbase/client/Get.java (added)
+++ hadoop/hbase/trunk_on_hadoop-0.18.3/src/java/org/apache/hadoop/hbase/client/Get.java Tue Jun 16 04:33:56 2009
@@ -0,0 +1,398 @@
+/**
+ * Copyright 2009 The Apache Software Foundation
+ *
+ * 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.hadoop.hbase.client;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Map;
+import java.util.NavigableSet;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import org.apache.hadoop.hbase.KeyValue;
+import org.apache.hadoop.hbase.filter.RowFilterInterface;
+import org.apache.hadoop.hbase.io.HbaseObjectWritable;
+import org.apache.hadoop.hbase.io.TimeRange;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.io.Writable;
+
+/**
+ * Used to perform Get operations on a single row.
+ * <p>
+ * To get everything for a row, instantiate a Get object with the row to get.
+ * To further define the scope of what to get, perform additional methods as 
+ * outlined below.
+ * <p>
+ * To get all columns from specific families, execute {@link #addFamily(byte[]) addFamily}
+ * for each family to retrieve.
+ * <p>
+ * To get specific columns, execute {@link #addColumn(byte[], byte[]) addColumn}
+ * for each column to retrieve.
+ * <p>
+ * To only retrieve columns within a specific range of version timestamps,
+ * execute {@link #setTimeRange(long, long) setTimeRange}.
+ * <p>
+ * To only retrieve columns with a specific timestamp, execute
+ * {@link #setTimeStamp(long) setTimestamp}.
+ * <p>
+ * To limit the number of versions of each column to be returned, execute
+ * {@link #setMaxVersions(int) setMaxVersions}.
+ * <p>
+ * To add a filter, execute {@link #setFilter(RowFilterInterface) setFilter}.
+ */
+public class Get implements Writable {
+  private byte [] row = null;
+  private long lockId = -1L;
+  private int maxVersions = 1;
+  private RowFilterInterface filter = null;
+  private TimeRange tr = new TimeRange();
+  private Map<byte [], NavigableSet<byte []>> familyMap = 
+    new TreeMap<byte [], NavigableSet<byte []>>(Bytes.BYTES_COMPARATOR);
+
+  /** Constructor for Writable.  DO NOT USE */
+  public Get() {}
+
+  /**
+   * Create a Get operation for the specified row.
+   * <p>
+   * If no further operations are done, this will get the latest version of
+   * all columns in all families of the specified row.
+   * @param row row key
+   */
+  public Get(byte [] row) {
+    this(row, null);
+  }
+
+  /**
+   * Create a Get operation for the specified row, using an existing row lock.
+   * <p>
+   * If no further operations are done, this will get the latest version of
+   * all columns in all families of the specified row.
+   * @param row row key
+   * @param rowLock previously acquired row lock, or null
+   */
+  public Get(byte [] row, RowLock rowLock) {
+    this.row = row;
+    if(rowLock != null) {
+      this.lockId = rowLock.getLockId();
+    }
+  }
+
+  /**
+   * Get all columns from the specified family.
+   * <p>
+   * Overrides previous calls to addColumn for this family.
+   * @param family family name
+   * @return the Get object
+   */
+  public Get addFamily(byte [] family) {
+    familyMap.remove(family);
+    familyMap.put(family, null);
+    return this;
+  }
+
+  /**
+   * Get the column from the specific family with the specified qualifier.
+   * <p>
+   * Overrides previous calls to addFamily for this family.
+   * @param family family name
+   * @param qualifier column qualifier
+   * @return the Get objec
+   */
+  public Get addColumn(byte [] family, byte [] qualifier) {
+    NavigableSet<byte []> set = familyMap.get(family);
+    if(set == null) {
+      set = new TreeSet<byte []>(Bytes.BYTES_COMPARATOR);
+    }
+    set.add(qualifier);
+    familyMap.put(family, set);
+    return this;
+  }
+
+  /**
+   * Adds an array of columns specified the old format, family:qualifier.
+   * <p>
+   * Overrides previous calls to addFamily for any families in the input.
+   * @param columns array of columns, formatted as <pre>family:qualifier</pre>
+   */
+  public Get addColumns(byte [][] columns) {
+    if (columns == null) return this;
+    for(int i = 0; i < columns.length; i++) {
+      try {
+        addColumn(columns[i]);
+      } catch(Exception e) {}
+    }
+    return this;
+  }
+
+  /**
+   * @param column Old format column.
+   * @return This.
+   */
+  public Get addColumn(final byte [] column) {
+    if (column == null) return this;
+    byte [][] split = KeyValue.parseColumn(column);
+    addColumn(split[0], split[1]);
+    return this;
+  }
+
+  /**
+   * Get versions of columns only within the specified timestamp range,
+   * [minStamp, maxStamp).
+   * @param minStamp minimum timestamp value, inclusive
+   * @param maxStamp maximum timestamp value, exclusive
+   * @throws IOException if invalid time range
+   */
+  public Get setTimeRange(long minStamp, long maxStamp)
+  throws IOException {
+    tr = new TimeRange(minStamp, maxStamp);
+    return this;
+  }
+
+  /**
+   * Get versions of columns with the specified timestamp.
+   * @param timestamp version timestamp  
+   */
+  public Get setTimeStamp(long timestamp) {
+    try {
+      tr = new TimeRange(timestamp, timestamp+1);
+    } catch(IOException e) {
+      // Will never happen
+    }
+    return this;
+  }
+
+  /**
+   * Get all available versions.
+   */
+  public Get setMaxVersions() {
+    this.maxVersions = Integer.MAX_VALUE;
+    return this;
+  }
+
+  /**
+   * Get up to the specified number of versions of each column.
+   * @param maxVersions maximum versions for each column
+   * @throws IOException if invalid number of versions
+   */
+  public Get setMaxVersions(int maxVersions) throws IOException {
+    if(maxVersions <= 0) {
+      throw new IOException("maxVersions must be positive");
+    }
+    this.maxVersions = maxVersions;
+    return this;
+  }
+
+  /**
+   * Apply the specified server-side filter when performing the Get.
+   * @param filter filter to run on the server
+   */
+  public Get setFilter(RowFilterInterface filter) {
+    this.filter = filter;
+    return this;
+  }
+
+  /** Accessors */
+
+  /**
+   * Method for retrieving the get's row
+   * @return row 
+   */
+  public byte [] getRow() {
+    return this.row;
+  }
+
+  /**
+   * Method for retrieving the get's RowLock
+   * @return RowLock
+   */
+  public RowLock getRowLock() {
+    return new RowLock(this.row, this.lockId);
+  }
+
+  /**
+   * Method for retrieving the get's lockId
+   * @return lockId
+   */
+  public long getLockId() {
+    return this.lockId;
+  }
+
+  /**
+   * Method for retrieving the get's maximum number of version
+   * @return the maximum number of version to fetch for this get
+   */
+  public int getMaxVersions() {
+    return this.maxVersions;
+  } 
+
+  /**
+   * Method for retrieving the get's TimeRange
+   * @return timeRange
+   */
+  public TimeRange getTimeRange() {
+    return this.tr;
+  }
+
+  /**
+   * Method for retrieving the keys in the familyMap
+   * @return keys in the current familyMap
+   */
+  public Set<byte[]> familySet() {
+    return this.familyMap.keySet();
+  }
+
+  /**
+   * Method for retrieving the number of families to get from
+   * @return number of families
+   */
+  public int numFamilies() {
+    return this.familyMap.size();
+  }
+
+  /**
+   * Method for checking if any families have been inserted into this Get
+   * @return true if familyMap is non empty false otherwise
+   */
+  public boolean hasFamilies() {
+    return !this.familyMap.isEmpty();
+  }
+
+  /**
+   * Method for retrieving the get's familyMap
+   * @return familyMap
+   */
+  public Map<byte[],NavigableSet<byte[]>> getFamilyMap() {
+    return this.familyMap;
+  }
+
+  /**
+   * @return String
+   */
+  @Override
+  public String toString() {
+    StringBuffer sb = new StringBuffer();
+    sb.append("row=");
+    sb.append(Bytes.toString(this.row));
+    sb.append(", maxVersions=");
+    sb.append("" + this.maxVersions);
+    sb.append(", timeRange=");
+    sb.append("[" + this.tr.getMin() + "," + this.tr.getMax() + ")");
+    sb.append(", families=");
+    if(this.familyMap.size() == 0) {
+      sb.append("ALL");
+      return sb.toString();
+    }
+    boolean moreThanOne = false;
+    for(Map.Entry<byte [], NavigableSet<byte[]>> entry : 
+      this.familyMap.entrySet()) {
+      if(moreThanOne) {
+        sb.append("), ");
+      } else {
+        moreThanOne = true;
+        sb.append("{");
+      }
+      sb.append("(family=");
+      sb.append(Bytes.toString(entry.getKey()));
+      sb.append(", columns=");
+      if(entry.getValue() == null) {
+        sb.append("ALL");
+      } else {
+        sb.append("{");
+        boolean moreThanOneB = false;
+        for(byte [] column : entry.getValue()) {
+          if(moreThanOneB) {
+            sb.append(", ");
+          } else {
+            moreThanOneB = true;
+          }
+          sb.append(Bytes.toString(column));
+        }
+        sb.append("}");
+      }
+    }
+    sb.append("}");
+    return sb.toString();
+  }
+
+  //Writable
+  public void readFields(final DataInput in)
+  throws IOException {
+    this.row = Bytes.readByteArray(in);
+    this.lockId = in.readLong();
+    this.maxVersions = in.readInt();
+    boolean hasFilter = in.readBoolean();
+    if(hasFilter) {
+      this.filter = 
+        (RowFilterInterface)HbaseObjectWritable.readObject(in, null);
+    }
+    this.tr = new TimeRange();
+    tr.readFields(in);
+    int numFamilies = in.readInt();
+    this.familyMap = 
+      new TreeMap<byte [],NavigableSet<byte []>>(Bytes.BYTES_COMPARATOR);
+    for(int i=0; i<numFamilies; i++) {
+      byte [] family = Bytes.readByteArray(in);
+      boolean hasColumns = in.readBoolean();
+      NavigableSet<byte []> set = null;
+      if(hasColumns) {
+        int numColumns = in.readInt();
+        set = new TreeSet<byte []>(Bytes.BYTES_COMPARATOR);
+        for(int j=0; j<numColumns; j++) {
+          byte [] qualifier = Bytes.readByteArray(in);
+          set.add(qualifier);
+        }
+      }
+      this.familyMap.put(family, set);
+    }
+  }
+
+  public void write(final DataOutput out)
+  throws IOException {
+    Bytes.writeByteArray(out, this.row);
+    out.writeLong(this.lockId);
+    out.writeInt(this.maxVersions);
+    if(this.filter == null) {
+      out.writeBoolean(false);
+    } else {
+      out.writeBoolean(true);
+      HbaseObjectWritable.writeObject(out, this.filter, 
+          RowFilterInterface.class, null);
+    }
+    tr.write(out);
+    out.writeInt(familyMap.size());
+    for(Map.Entry<byte [], NavigableSet<byte []>> entry : 
+      familyMap.entrySet()) {
+      Bytes.writeByteArray(out, entry.getKey());
+      NavigableSet<byte []> columnSet = entry.getValue();
+      if(columnSet == null) {
+        out.writeBoolean(false);
+      } else {
+        out.writeBoolean(true);
+        out.writeInt(columnSet.size());
+        for(byte [] qualifier : columnSet) {
+          Bytes.writeByteArray(out, qualifier);
+        }
+      }
+    }
+  }
+}

Modified: hadoop/hbase/trunk_on_hadoop-0.18.3/src/java/org/apache/hadoop/hbase/client/HBaseAdmin.java
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk_on_hadoop-0.18.3/src/java/org/apache/hadoop/hbase/client/HBaseAdmin.java?rev=785076&r1=785075&r2=785076&view=diff
==============================================================================
--- hadoop/hbase/trunk_on_hadoop-0.18.3/src/java/org/apache/hadoop/hbase/client/HBaseAdmin.java (original)
+++ hadoop/hbase/trunk_on_hadoop-0.18.3/src/java/org/apache/hadoop/hbase/client/HBaseAdmin.java Tue Jun 16 04:33:56 2009
@@ -21,6 +21,7 @@
 
 import java.io.IOException;
 import java.util.Map;
+import java.util.NavigableMap;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -34,8 +35,9 @@
 import org.apache.hadoop.hbase.MasterNotRunningException;
 import org.apache.hadoop.hbase.RegionException;
 import org.apache.hadoop.hbase.RemoteExceptionHandler;
-import org.apache.hadoop.hbase.io.Cell;
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.hadoop.hbase.client.Scan;
+import org.apache.hadoop.hbase.client.Result;
 import org.apache.hadoop.hbase.io.RowResult;
 import org.apache.hadoop.hbase.ipc.HMasterInterface;
 import org.apache.hadoop.hbase.ipc.HRegionInterface;
@@ -51,7 +53,8 @@
  */
 public class HBaseAdmin {
   private final Log LOG = LogFactory.getLog(this.getClass().getName());
-  private final HConnection connection;
+//  private final HConnection connection;
+  final HConnection connection;
   private volatile HBaseConfiguration conf;
   private final long pause;
   private final int numRetries;
@@ -121,11 +124,13 @@
     return this.connection.listTables();
   }
 
-  public HTableDescriptor getTableDescriptor(final String tableName)
-  throws IOException {
-    return getTableDescriptor(Bytes.toBytes(tableName));
-  }
   
+  /**
+   * Method for getting the tableDescriptor
+   * @param tableName as a byte []
+   * @return the tableDescriptor
+   * @throws IOException
+   */
   public HTableDescriptor getTableDescriptor(final byte [] tableName)
   throws IOException {
     return this.connection.getHTableDescriptor(tableName);
@@ -238,19 +243,22 @@
     for (int tries = 0; tries < numRetries; tries++) {
       long scannerId = -1L;
       try {
-        scannerId =
-          server.openScanner(firstMetaServer.getRegionInfo().getRegionName(),
-            HConstants.COL_REGIONINFO_ARRAY, tableName,
-            HConstants.LATEST_TIMESTAMP, null);
-        RowResult values = server.next(scannerId);
+        Scan scan = new Scan().addColumn(HConstants.CATALOG_FAMILY,
+            HConstants.REGIONINFO_QUALIFIER);
+        
+        scannerId = server.openScanner(
+            firstMetaServer.getRegionInfo().getRegionName(), 
+            scan);
+        Result values = server.next(scannerId);
         if (values == null || values.size() == 0) {
           break;
         }
         boolean found = false;
-        for (Map.Entry<byte [], Cell> e: values.entrySet()) {
-          if (Bytes.equals(e.getKey(), HConstants.COL_REGIONINFO)) {
+        NavigableMap<byte[], byte[]> infoValues = values.getFamilyMap(HConstants.CATALOG_FAMILY);
+        for (Map.Entry<byte [], byte []> e: infoValues.entrySet()) {
+          if (Bytes.equals(e.getKey(), HConstants.REGIONINFO_QUALIFIER)) {
             info = (HRegionInfo) Writables.getWritable(
-              e.getValue().getValue(), info);
+              e.getValue(), info);
             
             if (Bytes.equals(info.getTableDesc().getName(), tableName)) {
               found = true;
@@ -566,7 +574,7 @@
         newargs[i + xtraArgsCount] = args[i];
       }
     }
-    modifyTable(HConstants.META_TABLE_NAME, HConstants.MODIFY_CLOSE_REGION,
+    modifyTable(HConstants.META_TABLE_NAME, HConstants.Modify.CLOSE_REGION,
       newargs);
   }
   
@@ -589,7 +597,7 @@
    * @throws IOException
    */
   public void flush(final byte [] tableNameOrRegionName) throws IOException {
-    modifyTable(tableNameOrRegionName, HConstants.MODIFY_TABLE_FLUSH);
+    modifyTable(tableNameOrRegionName, HConstants.Modify.TABLE_FLUSH);
   }
 
   /**
@@ -611,7 +619,7 @@
    * @throws IOException
    */
   public void compact(final byte [] tableNameOrRegionName) throws IOException {
-    modifyTable(tableNameOrRegionName, HConstants.MODIFY_TABLE_COMPACT);
+    modifyTable(tableNameOrRegionName, HConstants.Modify.TABLE_COMPACT);
   }
   
   /**
@@ -635,7 +643,7 @@
    */
   public void majorCompact(final byte [] tableNameOrRegionName)
   throws IOException {
-    modifyTable(tableNameOrRegionName, HConstants.MODIFY_TABLE_MAJOR_COMPACT);
+    modifyTable(tableNameOrRegionName, HConstants.Modify.TABLE_MAJOR_COMPACT);
   }
 
   /**
@@ -657,7 +665,7 @@
    * @throws IOException
    */
   public void split(final byte [] tableNameOrRegionName) throws IOException {
-    modifyTable(tableNameOrRegionName, HConstants.MODIFY_TABLE_SPLIT);
+    modifyTable(tableNameOrRegionName, HConstants.Modify.TABLE_SPLIT);
   }
 
   /*
@@ -667,7 +675,8 @@
    * @param op
    * @throws IOException
    */
-  private void modifyTable(final byte [] tableNameOrRegionName, final int op)
+  private void modifyTable(final byte [] tableNameOrRegionName, 
+      final HConstants.Modify op)
   throws IOException {
     if (tableNameOrRegionName == null) {
       throw new IllegalArgumentException("Pass a table name or region name");
@@ -689,7 +698,7 @@
    */
   public void modifyTable(final byte [] tableName, HTableDescriptor htd) 
   throws IOException {
-    modifyTable(tableName, HConstants.MODIFY_TABLE_SET_HTD, htd);
+    modifyTable(tableName, HConstants.Modify.TABLE_SET_HTD, htd);
   }
 
   /**
@@ -702,7 +711,8 @@
    * @param args operation specific arguments
    * @throws IOException
    */
-  public void modifyTable(final byte [] tableName, int op, Object... args)
+  public void modifyTable(final byte [] tableName, HConstants.Modify op, 
+      Object... args)
       throws IOException {
     if (this.master == null) {
       throw new MasterNotRunningException("master has been shut down");
@@ -715,7 +725,7 @@
     Writable[] arr = null;
     try {
       switch (op) {
-      case HConstants.MODIFY_TABLE_SET_HTD:
+      case TABLE_SET_HTD:
         if (args == null || args.length < 1 || 
             !(args[0] instanceof HTableDescriptor)) {
           throw new IllegalArgumentException("SET_HTD requires a HTableDescriptor");
@@ -725,10 +735,10 @@
         this.master.modifyTable(tableName, op, arr);
         break;
 
-      case HConstants.MODIFY_TABLE_COMPACT:
-      case HConstants.MODIFY_TABLE_SPLIT:
-      case HConstants.MODIFY_TABLE_MAJOR_COMPACT:
-      case HConstants.MODIFY_TABLE_FLUSH:
+      case TABLE_COMPACT:
+      case TABLE_SPLIT:
+      case TABLE_MAJOR_COMPACT:
+      case TABLE_FLUSH:
         if (args != null && args.length > 0) {
           arr = new Writable[1];
           if (args[0] instanceof byte[]) {
@@ -745,7 +755,7 @@
         this.master.modifyTable(tableName, op, arr);
         break;
 
-      case HConstants.MODIFY_CLOSE_REGION:
+      case CLOSE_REGION:
         if (args == null || args.length < 1) {
           throw new IllegalArgumentException("Requires at least a region name");
         }



Mime
View raw message