You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hive.apache.org by se...@apache.org on 2014/05/14 22:53:32 UTC

svn commit: r1594713 [2/3] - in /hive/trunk: common/src/java/org/apache/hadoop/hive/conf/ conf/ hbase-handler/src/java/org/apache/hadoop/hive/hbase/ hbase-handler/src/test/org/apache/hadoop/hive/hbase/ ql/src/java/org/apache/hadoop/hive/ql/ ql/src/java...

Added: hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/persistence/MapJoinBytesTableContainer.java
URL: http://svn.apache.org/viewvc/hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/persistence/MapJoinBytesTableContainer.java?rev=1594713&view=auto
==============================================================================
--- hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/persistence/MapJoinBytesTableContainer.java (added)
+++ hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/persistence/MapJoinBytesTableContainer.java Wed May 14 20:53:30 2014
@@ -0,0 +1,495 @@
+package org.apache.hadoop.hive.ql.exec.persistence;
+
+
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hive.conf.HiveConf;
+import org.apache.hadoop.hive.ql.debug.Utils;
+import org.apache.hadoop.hive.ql.exec.ExprNodeEvaluator;
+import org.apache.hadoop.hive.ql.exec.vector.VectorHashKeyWrapper;
+import org.apache.hadoop.hive.ql.exec.vector.VectorHashKeyWrapperBatch;
+import org.apache.hadoop.hive.ql.exec.vector.expressions.VectorExpressionWriter;
+import org.apache.hadoop.hive.ql.metadata.HiveException;
+import org.apache.hadoop.hive.serde2.SerDe;
+import org.apache.hadoop.hive.serde2.SerDeException;
+import org.apache.hadoop.hive.serde2.ByteStream.Output;
+import org.apache.hadoop.hive.serde2.ByteStream.RandomAccessOutput;
+import org.apache.hadoop.hive.serde2.WriteBuffers;
+import org.apache.hadoop.hive.serde2.binarysortable.BinarySortableSerDe;
+import org.apache.hadoop.hive.serde2.io.ShortWritable;
+import org.apache.hadoop.hive.serde2.lazy.ByteArrayRef;
+import org.apache.hadoop.hive.serde2.lazybinary.LazyBinaryFactory;
+import org.apache.hadoop.hive.serde2.lazybinary.LazyBinarySerDe;
+import org.apache.hadoop.hive.serde2.lazybinary.LazyBinaryStruct;
+import org.apache.hadoop.hive.serde2.lazybinary.LazyBinaryUtils;
+import org.apache.hadoop.hive.serde2.lazybinary.objectinspector.LazyBinaryObjectInspectorFactory;
+import org.apache.hadoop.hive.serde2.lazybinary.objectinspector.LazyBinaryStructObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.StructField;
+import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.primitive.ShortObjectInspector;
+import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo;
+import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoUtils;
+import org.apache.hadoop.io.BinaryComparable;
+import org.apache.hadoop.io.Writable;
+
+/**
+ * Table container that serializes keys and values using LazyBinarySerDe into
+ * BytesBytesMultiHashMap, with very low memory overhead. However,
+ * there may be some perf overhead when retrieving rows.
+ */
+public class MapJoinBytesTableContainer implements MapJoinTableContainer {
+  private static final Log LOG = LogFactory.getLog(MapJoinTableContainer.class);
+
+  private final BytesBytesMultiHashMap hashMap;
+  /** The OI used to deserialize values. We never deserialize keys. */
+  private LazyBinaryStructObjectInspector internalValueOi;
+  /**
+   * This is kind of hacky. Currently we get BinarySortableSerDe-serialized keys; we could
+   * re-serialize them into LazyBinarySerDe, but instead we just reuse the bytes. However, to
+   * compare the large table keys correctly when we do, we need to serialize them with correct
+   * ordering. Hence, remember the ordering here; it is null if we do use LazyBinarySerDe.
+   */
+  private boolean[] sortableSortOrders;
+  private KeyValueHelper writeHelper;
+
+  private List<Object> EMPTY_LIST = new ArrayList<Object>(0);
+
+  public MapJoinBytesTableContainer(Configuration hconf, MapJoinObjectSerDeContext valCtx)
+      throws SerDeException {
+    this(HiveConf.getIntVar(hconf, HiveConf.ConfVars.HIVEHASHTABLETHRESHOLD),
+        HiveConf.getFloatVar(hconf, HiveConf.ConfVars.HIVEHASHTABLELOADFACTOR),
+        HiveConf.getIntVar(hconf, HiveConf.ConfVars.HIVEHASHTABLEWBSIZE), valCtx);
+  }
+
+  private MapJoinBytesTableContainer(int threshold, float loadFactor, int wbSize,
+      MapJoinObjectSerDeContext valCtx) throws SerDeException {
+    hashMap = new BytesBytesMultiHashMap(threshold, loadFactor, wbSize);
+  }
+
+  private LazyBinaryStructObjectInspector createInternalOi(
+      MapJoinObjectSerDeContext valCtx) throws SerDeException {
+    // We are going to use LBSerDe to serialize values; create OI for retrieval.
+    List<? extends StructField> fields = ((StructObjectInspector)
+        valCtx.getSerDe().getObjectInspector()).getAllStructFieldRefs();
+    List<String> colNames = new ArrayList<String>(fields.size());
+    List<ObjectInspector> colOis = new ArrayList<ObjectInspector>(fields.size());
+    for (int i = 0; i < fields.size(); ++i) {
+      StructField field = fields.get(i);
+      colNames.add(field.getFieldName());
+      // It would be nice if OI could return typeInfo...
+      TypeInfo typeInfo = TypeInfoUtils.getTypeInfoFromTypeString(
+          field.getFieldObjectInspector().getTypeName());
+      colOis.add(LazyBinaryUtils.getLazyBinaryObjectInspectorFromTypeInfo(typeInfo));
+    }
+
+    return LazyBinaryObjectInspectorFactory
+        .getLazyBinaryStructObjectInspector(colNames, colOis);
+  }
+
+  private static interface KeyValueHelper extends BytesBytesMultiHashMap.KvSource {
+    void setKeyValue(Writable key, Writable val) throws SerDeException;
+  }
+
+  private static class KeyValueWriter implements KeyValueHelper {
+    private final SerDe keySerDe, valSerDe;
+    private final StructObjectInspector keySoi, valSoi;
+    private final List<ObjectInspector> keyOis, valOis;
+    private final Object[] keyObjs, valObjs;
+    private final boolean hasFilterTag;
+
+    public KeyValueWriter(
+        SerDe keySerDe, SerDe valSerDe, boolean hasFilterTag) throws SerDeException {
+      this.keySerDe = keySerDe;
+      this.valSerDe = valSerDe;
+      keySoi = (StructObjectInspector)keySerDe.getObjectInspector();
+      valSoi = (StructObjectInspector)valSerDe.getObjectInspector();
+      List<? extends StructField> keyFields = keySoi.getAllStructFieldRefs(),
+          valFields = valSoi.getAllStructFieldRefs();
+      keyOis = new ArrayList<ObjectInspector>(keyFields.size());
+      valOis = new ArrayList<ObjectInspector>(valFields.size());
+      for (int i = 0; i < keyFields.size(); ++i) {
+        keyOis.add(keyFields.get(i).getFieldObjectInspector());
+      }
+      for (int i = 0; i < valFields.size(); ++i) {
+        valOis.add(valFields.get(i).getFieldObjectInspector());
+      }
+      keyObjs = new Object[keyOis.size()];
+      valObjs = new Object[valOis.size()];
+      this.hasFilterTag = hasFilterTag;
+    }
+
+    @Override
+    public void writeKey(RandomAccessOutput dest) throws SerDeException {
+      LazyBinarySerDe.serializeStruct(dest, keyObjs, keyOis);
+    }
+
+    @Override
+    public void writeValue(RandomAccessOutput dest) throws SerDeException {
+      LazyBinarySerDe.serializeStruct(dest, valObjs, valOis);
+    }
+
+    @Override
+    public void setKeyValue(Writable key, Writable val) throws SerDeException {
+      Object keyObj = keySerDe.deserialize(key), valObj = valSerDe.deserialize(val);
+      List<? extends StructField> keyFields = keySoi.getAllStructFieldRefs(),
+          valFields = valSoi.getAllStructFieldRefs();
+      for (int i = 0; i < keyFields.size(); ++i) {
+        keyObjs[i] = keySoi.getStructFieldData(keyObj, keyFields.get(i));
+      }
+      for (int i = 0; i < valFields.size(); ++i) {
+        valObjs[i] = valSoi.getStructFieldData(valObj, valFields.get(i));
+      }
+    }
+
+    @Override
+    public byte updateStateByte(Byte previousValue) {
+      if (!hasFilterTag) return (byte)0xff;
+      byte aliasFilter = (previousValue == null) ? (byte)0xff : previousValue.byteValue();
+      aliasFilter &= ((ShortWritable)valObjs[valObjs.length - 1]).get();
+      return aliasFilter;
+    }
+  }
+
+  private static class LazyBinaryKvWriter implements KeyValueHelper {
+    private final LazyBinaryStruct.SingleFieldGetter filterGetter;
+    private Writable key, value;
+    private final SerDe keySerDe;
+    private Boolean hasTag = null; // sanity check - we should not receive keys with tags
+
+    public LazyBinaryKvWriter(SerDe keySerDe, LazyBinaryStructObjectInspector valSoi,
+        boolean hasFilterTag) throws SerDeException {
+      this.keySerDe = keySerDe;
+      if (hasFilterTag) {
+        List<? extends StructField> fields = valSoi.getAllStructFieldRefs();
+        int ix = fields.size() - 1;
+        if (!(fields.get(ix).getFieldObjectInspector() instanceof ShortObjectInspector)) {
+          throw new SerDeException("Has filter tag, but corresponding OI is " +
+              fields.get(ix).getFieldObjectInspector());
+        }
+        filterGetter = new LazyBinaryStruct.SingleFieldGetter(valSoi, fields.size() - 1);
+      } else {
+        filterGetter = null;
+      }
+    }
+
+    @Override
+    public void writeKey(RandomAccessOutput dest) throws SerDeException {
+      if (!(key instanceof BinaryComparable)) {
+        throw new SerDeException("Unexpected type " + key.getClass().getCanonicalName());
+      }
+      sanityCheckKeyForTag();
+      BinaryComparable b = (BinaryComparable)key;
+      dest.write(b.getBytes(), 0, b.getLength() - (hasTag ? 1 : 0));
+    }
+
+    /**
+     * If we received data with tags from ReduceSinkOperators, no keys will match. This should
+     * not happen, but is important enough that we want to find out and work around it if some
+     * optimized change causes RSO to pass on tags.
+     */
+    private void sanityCheckKeyForTag() throws SerDeException {
+      if (hasTag != null) return;
+      BinaryComparable b = (BinaryComparable)key;
+      Object o = keySerDe.deserialize(key);
+      StructObjectInspector soi = (StructObjectInspector)keySerDe.getObjectInspector();
+      List<? extends StructField> fields = soi.getAllStructFieldRefs();
+      Object[] data = new Object[fields.size()];
+      List<ObjectInspector> fois = new ArrayList<ObjectInspector>(fields.size());
+      for (int i = 0; i < fields.size(); i++) {
+        data[i] = soi.getStructFieldData(o, fields.get(i));
+        fois.add(fields.get(i).getFieldObjectInspector());
+      }
+      Output output = new Output();
+      BinarySortableSerDe.serializeStruct(output, data, fois, new boolean[fields.size()]);
+      hasTag = (output.getLength() != b.getLength());
+      if (hasTag) {
+        LOG.error("Tag found in keys and will be removed. This should not happen.");
+        if (output.getLength() != (b.getLength() - 1)) {
+          throw new SerDeException(
+              "Unexpected tag: " + b.getLength() + " reserialized to " + output.getLength());
+        }
+      }
+    }
+
+    @Override
+    public void writeValue(RandomAccessOutput dest) throws SerDeException {
+      if (!(value instanceof BinaryComparable)) {
+        throw new SerDeException("Unexpected type " + value.getClass().getCanonicalName());
+      }
+      BinaryComparable b = (BinaryComparable)value;
+      dest.write(b.getBytes(), 0, b.getLength());
+    }
+
+    @Override
+    public void setKeyValue(Writable key, Writable val) {
+      this.key = key;
+      this.value = val;
+    }
+
+    @Override
+    public byte updateStateByte(Byte previousValue) {
+      if (filterGetter == null) return (byte)0xff;
+      byte aliasFilter = (previousValue == null) ? (byte)0xff : previousValue.byteValue();
+      filterGetter.init((BinaryComparable)value);
+      aliasFilter &= filterGetter.getShort();
+      return aliasFilter;
+    }
+  }
+
+  @SuppressWarnings("deprecation")
+  @Override
+  public MapJoinKey putRow(MapJoinObjectSerDeContext keyContext, Writable currentKey,
+      MapJoinObjectSerDeContext valueContext, Writable currentValue) throws SerDeException {
+    SerDe keySerde = keyContext.getSerDe(), valSerde = valueContext.getSerDe();
+    if (writeHelper == null) {
+      LOG.info("Initializing container with "
+          + keySerde.getClass().getName() + " and " + valSerde.getClass().getName());
+      if (keySerde instanceof BinarySortableSerDe && valSerde instanceof LazyBinarySerDe) {
+        LazyBinaryStructObjectInspector valSoi =
+            (LazyBinaryStructObjectInspector)valSerde.getObjectInspector();
+        writeHelper = new LazyBinaryKvWriter(keySerde, valSoi, valueContext.hasFilterTag());
+        internalValueOi = valSoi;
+        sortableSortOrders = ((BinarySortableSerDe)keySerde).getSortOrders();
+      } else {
+        writeHelper = new KeyValueWriter(keySerde, valSerde, valueContext.hasFilterTag());
+        internalValueOi = createInternalOi(valueContext);
+        sortableSortOrders = null;
+      }
+    }
+    writeHelper.setKeyValue(currentKey, currentValue);
+    hashMap.put(writeHelper);
+    return null; // there's no key to return
+  }
+
+  @Override
+  public void clear() {
+    hashMap.clear();
+  }
+
+  @Override
+  public MapJoinKey getAnyKey() {
+    return null; // This table has no keys.
+  }
+
+  @Override
+  public ReusableGetAdaptor createGetter(MapJoinKey keyTypeFromLoader) {
+    if (keyTypeFromLoader != null) {
+      throw new AssertionError("No key expected from loader but got " + keyTypeFromLoader);
+    }
+    return new GetAdaptor();
+  }
+
+  @Override
+  public void seal() {
+    hashMap.seal();
+  }
+
+  /** Implementation of ReusableGetAdaptor that has Output for key serialization; row
+   * container is also created once and reused for every row. */
+  private class GetAdaptor implements ReusableGetAdaptor {
+    private ReusableRowContainer currentValue = new ReusableRowContainer();
+    private Output output;
+    private boolean[] nulls;
+
+    @Override
+    public void setFromVector(VectorHashKeyWrapper kw,
+        VectorExpressionWriter[] keyOutputWriters,
+        VectorHashKeyWrapperBatch keyWrapperBatch) throws HiveException {
+      if (nulls == null) {
+        nulls = new boolean[keyOutputWriters.length];
+      } else {
+        assert nulls.length == keyOutputWriters.length;
+      }
+      try {
+        output = MapJoinKey.serializeVector(
+            output, kw, keyOutputWriters, keyWrapperBatch, nulls, sortableSortOrders);
+      } catch (SerDeException e) {
+        throw new HiveException(e);
+      }
+      this.currentValue.setFromOutput(output);
+    }
+
+    @Override
+    public void setFromRow(Object row, List<ExprNodeEvaluator> fields,
+        List<ObjectInspector> ois) throws HiveException {
+      if (nulls == null) {
+        nulls = new boolean[fields.size()];
+      }
+      Object[] fieldObjs = new Object[fields.size()];
+      for (int keyIndex = 0; keyIndex < fields.size(); ++keyIndex) {
+        fieldObjs[keyIndex] = fields.get(keyIndex).evaluate(row);
+        nulls[keyIndex] = (fieldObjs[keyIndex] == null);
+      }
+      try {
+        output = MapJoinKey.serializeRow(output, fieldObjs, ois, sortableSortOrders);
+      } catch (SerDeException e) {
+        throw new HiveException(e);
+      }
+      this.currentValue.setFromOutput(output);
+    }
+
+
+    @Override
+    public void setFromOther(ReusableGetAdaptor other) {
+      assert other instanceof GetAdaptor;
+      GetAdaptor other2 = (GetAdaptor)other;
+      output = other2.output;
+      nulls = other2.nulls;
+      this.currentValue.setFromOutput(output);
+    }
+
+    @Override
+    public boolean hasAnyNulls(int fieldCount, boolean[] nullsafes) {
+      if (nulls == null || nulls.length == 0) return false;
+      for (int i = 0; i < nulls.length; i++) {
+        if (nulls[i] == true && (nullsafes == null || !nullsafes[i])) {
+          return true;
+        }
+      }
+      return false;
+    }
+
+    @Override
+    public MapJoinRowContainer getCurrentRows() {
+      return currentValue.isEmpty() ? null : currentValue;
+    }
+  }
+
+  /** Row container that gets and deserializes the rows on demand from bytes provided. */
+  private class ReusableRowContainer
+    implements MapJoinRowContainer, AbstractRowContainer.RowIterator<List<Object>> {
+    private byte aliasFilter;
+    private List<WriteBuffers.ByteSegmentRef> refs;
+    private int currentRow;
+    /**
+     * Sometimes, when container is empty in multi-table mapjoin, we need to add a dummy row.
+     * This container does not normally support adding rows; this is for the dummy row.
+     */
+    private List<Object> dummyRow = null;
+
+    private final ByteArrayRef uselessIndirection; // LBStruct needs ByteArrayRef
+    private final LazyBinaryStruct valueStruct;
+
+    public ReusableRowContainer() {
+      if (internalValueOi != null) {
+        valueStruct = (LazyBinaryStruct)
+            LazyBinaryFactory.createLazyBinaryObject(internalValueOi);
+      } else {
+        valueStruct = null; // No rows?
+      }
+      uselessIndirection = new ByteArrayRef();
+      clearRows();
+    }
+
+    public void setFromOutput(Output output) {
+      if (refs == null) {
+        refs = new ArrayList<WriteBuffers.ByteSegmentRef>(0);
+      }
+      byte aliasFilter = hashMap.getValueRefs(output.getData(), output.getLength(), refs);
+      this.aliasFilter = refs.isEmpty() ? (byte) 0xff : aliasFilter;
+      this.dummyRow = null;
+    }
+
+    public boolean isEmpty() {
+      return refs.isEmpty() && (dummyRow == null);
+    }
+
+    // Implementation of row container
+    @Override
+    public AbstractRowContainer.RowIterator<List<Object>> rowIter() throws HiveException {
+      currentRow = -1;
+      return this;
+    }
+
+    @Override
+    public int rowCount() throws HiveException {
+      return dummyRow != null ? 1 : refs.size();
+    }
+
+    @Override
+    public void clearRows() {
+      // Doesn't clear underlying hashtable
+      if (refs != null) {
+        refs.clear();
+      }
+      dummyRow = null;
+      currentRow = -1;
+      aliasFilter = (byte) 0xff;
+    }
+
+    @Override
+    public byte getAliasFilter() throws HiveException {
+      return aliasFilter;
+    }
+
+    @Override
+    public MapJoinRowContainer copy() throws HiveException {
+      return this; // Independent of hashtable and can be modified, no need to copy.
+    }
+
+    // Implementation of row iterator
+    @Override
+    public List<Object> first() throws HiveException {
+      currentRow = 0;
+      return next();
+    }
+
+
+    @Override
+    public List<Object> next() throws HiveException {
+      if (dummyRow != null) {
+        List<Object> result = dummyRow;
+        dummyRow = null;
+        return result;
+      }
+      if (currentRow < 0 || refs.size() < currentRow) throw new HiveException("No rows");
+      if (refs.size() == currentRow) return null;
+      WriteBuffers.ByteSegmentRef ref = refs.get(currentRow++);
+      if (ref.getLength() == 0) {
+        return EMPTY_LIST; // shortcut, 0 length means no fields
+      }
+      if (ref.getBytes() == null) {
+        hashMap.populateValue(ref);
+      }
+      uselessIndirection.setData(ref.getBytes());
+      valueStruct.init(uselessIndirection, (int)ref.getOffset(), ref.getLength());
+      return valueStruct.getFieldsAsList(); // TODO: should we unset bytes after that?
+    }
+
+    public void addRow(List<Object> t) {
+      if (dummyRow != null || !refs.isEmpty()) {
+        throw new RuntimeException("Cannot add rows when not empty");
+      }
+      dummyRow = t;
+    }
+
+    // Various unsupported methods.
+    public void addRow(Object[] value) {
+      throw new RuntimeException(this.getClass().getCanonicalName() + " cannot add arrays");
+    }
+    public void write(MapJoinObjectSerDeContext valueContext, ObjectOutputStream out) {
+      throw new RuntimeException(this.getClass().getCanonicalName() + " cannot be serialized");
+    }
+  }
+
+  public static boolean isSupportedKey(ObjectInspector keyOi) {
+    List<? extends StructField> keyFields = ((StructObjectInspector)keyOi).getAllStructFieldRefs();
+    for (StructField field : keyFields) {
+      if (!MapJoinKey.isSupportedField(field.getFieldObjectInspector())) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  @Override
+  public void dumpMetrics() {
+    hashMap.debugDumpMetrics();
+  }
+}

Modified: hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/persistence/MapJoinKey.java
URL: http://svn.apache.org/viewvc/hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/persistence/MapJoinKey.java?rev=1594713&r1=1594712&r2=1594713&view=diff
==============================================================================
--- hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/persistence/MapJoinKey.java (original)
+++ hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/persistence/MapJoinKey.java Wed May 14 20:53:30 2014
@@ -25,7 +25,10 @@ import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.hadoop.hive.common.type.Decimal128;
+import org.apache.hadoop.hive.ql.debug.Utils;
 import org.apache.hadoop.hive.ql.exec.ExprNodeEvaluator;
 import org.apache.hadoop.hive.ql.exec.vector.VectorHashKeyWrapper;
 import org.apache.hadoop.hive.ql.exec.vector.VectorHashKeyWrapperBatch;
@@ -34,6 +37,7 @@ import org.apache.hadoop.hive.ql.metadat
 import org.apache.hadoop.hive.serde2.ByteStream.Output;
 import org.apache.hadoop.hive.serde2.SerDe;
 import org.apache.hadoop.hive.serde2.SerDeException;
+import org.apache.hadoop.hive.serde2.binarysortable.BinarySortableSerDe;
 import org.apache.hadoop.hive.serde2.io.HiveDecimalWritable;
 import org.apache.hadoop.hive.serde2.lazybinary.LazyBinarySerDe;
 import org.apache.hadoop.hive.serde2.lazybinary.LazyBinaryUtils;
@@ -48,20 +52,26 @@ import org.apache.hadoop.hive.serde2.obj
 import org.apache.hadoop.io.Writable;
 
 /**
- * The base class for MapJoinKey; also acts as a factory for creating and reading
- * the keys, choosing whether size-optimized byte-array based MJKBytes can be used.
+ * The base class for MapJoinKey.
+ * Ideally, this should now be removed, some table wrappers have no key object.
  */
 public abstract class MapJoinKey {
   private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
 
+  public abstract void write(MapJoinObjectSerDeContext context, ObjectOutputStream out)
+      throws IOException, SerDeException;
+
+  public abstract boolean hasAnyNulls(int fieldCount, boolean[] nullsafes);
+
   @SuppressWarnings("deprecation")
-  public static MapJoinKey read(Output output, MapJoinKey key, MapJoinObjectSerDeContext context,
-      Writable writable, boolean mayReuseKey) throws SerDeException {
+  public static MapJoinKey read(Output output, MapJoinKey key,
+      MapJoinObjectSerDeContext context, Writable writable, boolean mayReuseKey)
+          throws SerDeException {
     SerDe serde = context.getSerDe();
     Object obj = serde.deserialize(writable);
     boolean useOptimized = useOptimizedKeyBasedOnPrev(key);
     if (useOptimized || key == null) {
-      byte[] structBytes = serializeKey(output, obj, serde.getObjectInspector(), !useOptimized);
+      byte[] structBytes = serialize(output, obj, serde.getObjectInspector(), !useOptimized);
       if (structBytes != null) {
         return MapJoinKeyBytes.fromBytes(key, mayReuseKey, structBytes);
       } else if (useOptimized) {
@@ -95,7 +105,7 @@ public abstract class MapJoinKey {
     SUPPORTED_PRIMITIVES.add(PrimitiveCategory.CHAR);
   }
 
-  private static byte[] serializeKey(Output byteStream,
+  private static byte[] serialize(Output byteStream,
       Object obj, ObjectInspector oi, boolean checkTypes) throws SerDeException {
     if (null == obj || !(oi instanceof StructObjectInspector)) {
       return null; // not supported
@@ -113,16 +123,22 @@ public abstract class MapJoinKey {
     for (int i = 0; i < size; ++i) {
       StructField field = fields.get(i);
       ObjectInspector foi = field.getFieldObjectInspector();
-      if (checkTypes) {
-        if (foi.getCategory() != Category.PRIMITIVE) return null; // not supported
-        PrimitiveCategory pc = ((PrimitiveObjectInspector)foi).getPrimitiveCategory();
-        if (!SUPPORTED_PRIMITIVES.contains(pc)) return null; // not supported
+      if (checkTypes && !isSupportedField(foi)) {
+        return null;
       }
       fieldData[i] = soi.getStructFieldData(obj, field);
       fieldOis.add(foi);
     }
 
-    return serializeRowCommon(byteStream, fieldData, fieldOis);
+    byteStream = serializeRow(byteStream, fieldData, fieldOis, null);
+    return Arrays.copyOf(byteStream.getData(), byteStream.getLength());
+  }
+
+  public static boolean isSupportedField(ObjectInspector foi) {
+    if (foi.getCategory() != Category.PRIMITIVE) return false; // not supported
+    PrimitiveCategory pc = ((PrimitiveObjectInspector)foi).getPrimitiveCategory();
+    if (!SUPPORTED_PRIMITIVES.contains(pc)) return false; // not supported
+    return true;
   }
 
   public static MapJoinKey readFromVector(Output output, MapJoinKey key, VectorHashKeyWrapper kw,
@@ -132,7 +148,10 @@ public abstract class MapJoinKey {
     if (useOptimized || key == null) {
       byte[] structBytes = null;
       try {
-        structBytes = serializeVector(output, kw, keyOutputWriters, keyWrapperBatch);
+        if (keyOutputWriters.length <= 8) {
+          output = serializeVector(output, kw, keyOutputWriters, keyWrapperBatch, null, null);
+          structBytes = Arrays.copyOf(output.getData(), output.getLength());
+        }
       } catch (SerDeException e) {
         throw new HiveException(e);
       }
@@ -148,19 +167,26 @@ public abstract class MapJoinKey {
     return result;
   }
 
-  private static byte[] serializeVector(Output byteStream, VectorHashKeyWrapper kw,
-      VectorExpressionWriter[] keyOutputWriters, VectorHashKeyWrapperBatch keyWrapperBatch)
-          throws HiveException, SerDeException {
+  /**
+   * Serializes row to output for vectorized path.
+   * @param byteStream Output to reuse. Can be null, in that case a new one would be created.
+   */
+  public static Output serializeVector(Output byteStream, VectorHashKeyWrapper kw,
+      VectorExpressionWriter[] keyOutputWriters, VectorHashKeyWrapperBatch keyWrapperBatch,
+      boolean[] nulls, boolean[] sortableSortOrders) throws HiveException, SerDeException {
     Object[] fieldData = new Object[keyOutputWriters.length];
     List<ObjectInspector> fieldOis = new ArrayList<ObjectInspector>();
     for (int i = 0; i < keyOutputWriters.length; ++i) {
       VectorExpressionWriter writer = keyOutputWriters[i];
       fieldOis.add(writer.getObjectInspector());
-      // This is rather convoluted... to simplify for per, we could call getRawKeyValue
+      // This is rather convoluted... to simplify for perf, we could call getRawKeyValue
       // instead of writable, and serialize based on Java type as opposed to OI.
       fieldData[i] = keyWrapperBatch.getWritableKeyValue(kw, i, writer);
+      if (nulls != null) {
+        nulls[i] = (fieldData[i] == null);
+      }
     }
-    return serializeRowCommon(byteStream, fieldData, fieldOis);
+    return serializeRow(byteStream, fieldData, fieldOis, sortableSortOrders);
   }
 
   public static MapJoinKey readFromRow(Output output, MapJoinKey key, Object row,
@@ -173,7 +199,15 @@ public abstract class MapJoinKey {
     boolean useOptimized = useOptimizedKeyBasedOnPrev(key);
     if (useOptimized || key == null) {
       try {
-        byte[] structBytes = serializeRow(output, fieldObjs, keyFieldsOI);
+        byte[] structBytes = null;
+        if (fieldObjs.length <= 8) {
+          if (fieldObjs.length == 0) {
+            structBytes = EMPTY_BYTE_ARRAY; // shortcut for null keys
+          } else {
+            output = serializeRow(output, fieldObjs, keyFieldsOI, null);
+            structBytes = Arrays.copyOf(output.getData(), output.getLength());
+          }
+        }
         if (structBytes != null) {
           return MapJoinKeyBytes.fromBytes(key, mayReuseKey, structBytes);
         } else if (useOptimized) {
@@ -189,34 +223,31 @@ public abstract class MapJoinKey {
     return result;
   }
 
-  private static byte[] serializeRow(Output byteStream,
-      Object[] fieldData, List<ObjectInspector> fieldOis) throws SerDeException {
-    if (fieldData.length > 8) {
-      return null; // not supported
-    } else if (fieldData.length == 0) {
-      return EMPTY_BYTE_ARRAY; // shortcut for null keys
-    }
-    assert fieldData.length == fieldOis.size();
-    return serializeRowCommon(byteStream, fieldData, fieldOis);
-  }
+  private static final Log LOG = LogFactory.getLog(MapJoinKey.class);
 
-  private static byte[] serializeRowCommon(Output byteStream,
-      Object[] fieldData, List<ObjectInspector> fieldOis) throws SerDeException {
+
+  /**
+   * Serializes row to output.
+   * @param byteStream Output to reuse. Can be null, in that case a new one would be created.
+   */
+  public static Output serializeRow(Output byteStream, Object[] fieldData,
+      List<ObjectInspector> fieldOis, boolean[] sortableSortOrders) throws SerDeException {
     if (byteStream == null) {
       byteStream = new Output();
     } else {
       byteStream.reset();
     }
-    LazyBinarySerDe.serializeStruct(byteStream, fieldData, fieldOis);
-    return Arrays.copyOf(byteStream.getData(), byteStream.getCount());
+    if (fieldData.length == 0) {
+      byteStream.reset();
+    } else if (sortableSortOrders == null) {
+      LazyBinarySerDe.serializeStruct(byteStream, fieldData, fieldOis);
+    } else {
+      BinarySortableSerDe.serializeStruct(byteStream, fieldData, fieldOis, sortableSortOrders);
+    }
+    return byteStream;
   }
 
   private static boolean useOptimizedKeyBasedOnPrev(MapJoinKey key) {
     return (key != null) && (key instanceof MapJoinKeyBytes);
   }
-
-  public abstract void write(MapJoinObjectSerDeContext context, ObjectOutputStream out)
-      throws IOException, SerDeException;
-
-  public abstract boolean hasAnyNulls(int fieldCount, boolean[] nullsafes);
-}
\ No newline at end of file
+}

Added: hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/persistence/MapJoinPersistableTableContainer.java
URL: http://svn.apache.org/viewvc/hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/persistence/MapJoinPersistableTableContainer.java?rev=1594713&view=auto
==============================================================================
--- hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/persistence/MapJoinPersistableTableContainer.java (added)
+++ hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/persistence/MapJoinPersistableTableContainer.java Wed May 14 20:53:30 2014
@@ -0,0 +1,30 @@
+/**
+ * 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.hive.ql.exec.persistence;
+
+import java.util.Map;
+import java.util.Set;
+
+public interface MapJoinPersistableTableContainer extends MapJoinTableContainer {
+  void put(MapJoinKey key, MapJoinRowContainer value);
+  Set<Map.Entry<MapJoinKey, MapJoinRowContainer>> entrySet();
+  int size();
+  MapJoinRowContainer get(MapJoinKey key);
+  Map<String, String> getMetaData();
+}

Modified: hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/persistence/MapJoinRowContainer.java
URL: http://svn.apache.org/viewvc/hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/persistence/MapJoinRowContainer.java?rev=1594713&r1=1594712&r2=1594713&view=diff
==============================================================================
--- hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/persistence/MapJoinRowContainer.java (original)
+++ hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/persistence/MapJoinRowContainer.java Wed May 14 20:53:30 2014
@@ -27,12 +27,12 @@ import org.apache.hadoop.hive.serde2.Ser
 
 public interface MapJoinRowContainer extends AbstractRowContainer<List<Object>> {
 
-  public byte getAliasFilter() throws HiveException;
+  byte getAliasFilter() throws HiveException;
 
-  public MapJoinRowContainer copy() throws HiveException;
+  MapJoinRowContainer copy() throws HiveException;
 
-  public void addRow(Object[] value) throws HiveException;
+  void addRow(Object[] value) throws HiveException;
 
-  public void write(MapJoinObjectSerDeContext valueContext, ObjectOutputStream out)
+  void write(MapJoinObjectSerDeContext valueContext, ObjectOutputStream out)
       throws IOException, SerDeException;
 }
\ No newline at end of file

Modified: hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/persistence/MapJoinTableContainer.java
URL: http://svn.apache.org/viewvc/hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/persistence/MapJoinTableContainer.java?rev=1594713&r1=1594712&r2=1594713&view=diff
==============================================================================
--- hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/persistence/MapJoinTableContainer.java (original)
+++ hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/persistence/MapJoinTableContainer.java Wed May 14 20:53:30 2014
@@ -18,23 +18,79 @@
 
 package org.apache.hadoop.hive.ql.exec.persistence;
 
-import java.util.Map;
-import java.util.Set;
+import java.util.List;
 
-public interface MapJoinTableContainer {
-
-  public int size();
-
-  public MapJoinRowContainer get(MapJoinKey key);
+import org.apache.hadoop.hive.ql.exec.ExprNodeEvaluator;
+import org.apache.hadoop.hive.ql.exec.vector.VectorHashKeyWrapper;
+import org.apache.hadoop.hive.ql.exec.vector.VectorHashKeyWrapperBatch;
+import org.apache.hadoop.hive.ql.exec.vector.expressions.VectorExpressionWriter;
+import org.apache.hadoop.hive.ql.metadata.HiveException;
+import org.apache.hadoop.hive.serde2.SerDeException;
+import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
+import org.apache.hadoop.io.Writable;
 
-  public void put(MapJoinKey key, MapJoinRowContainer value);
-
-  public Set<Map.Entry<MapJoinKey, MapJoinRowContainer>> entrySet();
-
-  public Map<String, String> getMetaData();
+public interface MapJoinTableContainer {
+  /**
+   * Retrieve rows from hashtable key by key, one key at a time, w/o copying the structures
+   * for each key. "Old" HashMapWrapper will still create/retrieve new objects for java HashMap;
+   * but the optimized one doesn't have to.
+   */
+  public interface ReusableGetAdaptor {
+    /**
+     * Changes current rows to which adaptor is referring to the rows corresponding to
+     * the key represented by a VHKW object, and writers and batch used to interpret it.
+     */
+    void setFromVector(VectorHashKeyWrapper kw, VectorExpressionWriter[] keyOutputWriters,
+        VectorHashKeyWrapperBatch keyWrapperBatch) throws HiveException;
+
+    /**
+     * Changes current rows to which adaptor is referring to the rows corresponding to
+     * the key represented by a row object, and fields and ois used to interpret it.
+     */
+    void setFromRow(Object row, List<ExprNodeEvaluator> fields, List<ObjectInspector> ois)
+        throws HiveException;
+
+    /**
+     * Changes current rows to which adaptor is referring to the rows corresponding to
+     * the key that another adaptor has already deserialized via setFromVector/setFromRow.
+     */
+    void setFromOther(ReusableGetAdaptor other);
+
+    /**
+     * Checks whether the current key has any nulls.
+     */
+    boolean hasAnyNulls(int fieldCount, boolean[] nullsafes);
+
+    /**
+     * @return The container w/the rows corresponding to a key set via a previous set... call.
+     */
+    MapJoinRowContainer getCurrentRows();
+  }
+
+  /**
+   * Adds row from input to the table.
+   */
+  MapJoinKey putRow(MapJoinObjectSerDeContext keyContext, Writable currentKey,
+      MapJoinObjectSerDeContext valueContext, Writable currentValue)
+          throws SerDeException, HiveException;
+
+  /**
+   * Indicates to the container that the puts have ended; table is now r/o.
+   */
+  void seal();
+
+  /**
+   * Creates reusable get adaptor that can be used to retrieve rows from the table
+   * based on either vectorized or non-vectorized input rows to MapJoinOperator.
+   * @param keyTypeFromLoader Last key from hash table loader, to determine key type used
+   *                          when loading hashtable (if it can vary).
+   */
+  ReusableGetAdaptor createGetter(MapJoinKey keyTypeFromLoader);
 
-  public void clear();
+  /** Clears the contents of the table. */
+  void clear();
 
-  public MapJoinKey getAnyKey();
+  MapJoinKey getAnyKey();
 
+  void dumpMetrics();
 }

Modified: hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/persistence/MapJoinTableContainerSerDe.java
URL: http://svn.apache.org/viewvc/hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/persistence/MapJoinTableContainerSerDe.java?rev=1594713&r1=1594712&r2=1594713&view=diff
==============================================================================
--- hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/persistence/MapJoinTableContainerSerDe.java (original)
+++ hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/persistence/MapJoinTableContainerSerDe.java Wed May 14 20:53:30 2014
@@ -54,11 +54,11 @@ public class MapJoinTableContainerSerDe 
    * @param in Input stream.
    * @return Loaded table.
    */
-  public MapJoinTableContainer load(ObjectInputStream in) 
+  public MapJoinPersistableTableContainer load(ObjectInputStream in)
       throws HiveException {
     SerDe keySerDe = keyContext.getSerDe();
     SerDe valueSerDe = valueContext.getSerDe();
-    MapJoinTableContainer tableContainer;
+    MapJoinPersistableTableContainer tableContainer;
     try {
       String name = in.readUTF();
       Map<String, String> metaData = (Map<String, String>) in.readObject();
@@ -86,7 +86,7 @@ public class MapJoinTableContainerSerDe 
       throw new HiveException("Error while trying to create table container", e);
     }
   }
-  public void persist(ObjectOutputStream out, MapJoinTableContainer tableContainer)
+  public void persist(ObjectOutputStream out, MapJoinPersistableTableContainer tableContainer)
       throws HiveException {
     int numKeys = tableContainer.size();
     try { 
@@ -110,17 +110,20 @@ public class MapJoinTableContainerSerDe 
   }
   
   public static void persistDummyTable(ObjectOutputStream out) throws IOException {
-    MapJoinTableContainer tableContainer = new HashMapWrapper();
+    MapJoinPersistableTableContainer tableContainer = new HashMapWrapper();
     out.writeUTF(tableContainer.getClass().getName());
     out.writeObject(tableContainer.getMetaData());
     out.writeInt(tableContainer.size());
   }
-  
-  private MapJoinTableContainer create(String name, Map<String, String> metaData) throws HiveException {
+
+  private MapJoinPersistableTableContainer create(
+      String name, Map<String, String> metaData) throws HiveException {
     try {
       @SuppressWarnings("unchecked")
-      Class<? extends MapJoinTableContainer> clazz = (Class<? extends MapJoinTableContainer>) Class.forName(name);
-      Constructor<? extends MapJoinTableContainer> constructor = clazz.getDeclaredConstructor(Map.class);
+      Class<? extends MapJoinPersistableTableContainer> clazz =
+          (Class<? extends MapJoinPersistableTableContainer>)Class.forName(name);
+      Constructor<? extends MapJoinPersistableTableContainer> constructor =
+          clazz.getDeclaredConstructor(Map.class);
       return constructor.newInstance(metaData);
     } catch (Exception e) {
       String msg = "Error while attemping to create table container" +

Modified: hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/tez/HashTableLoader.java
URL: http://svn.apache.org/viewvc/hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/tez/HashTableLoader.java?rev=1594713&r1=1594712&r2=1594713&view=diff
==============================================================================
--- hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/tez/HashTableLoader.java (original)
+++ hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/tez/HashTableLoader.java Wed May 14 20:53:30 2014
@@ -18,26 +18,44 @@
 package org.apache.hadoop.hive.ql.exec.tez;
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hive.conf.HiveConf;
+import org.apache.hadoop.hive.ql.debug.Utils;
 import org.apache.hadoop.hive.ql.exec.MapJoinOperator;
 import org.apache.hadoop.hive.ql.exec.MapredContext;
 import org.apache.hadoop.hive.ql.exec.mr.ExecMapperContext;
 import org.apache.hadoop.hive.ql.exec.persistence.HashMapWrapper;
+import org.apache.hadoop.hive.ql.exec.persistence.MapJoinBytesTableContainer;
+import org.apache.hadoop.hive.ql.exec.persistence.MapJoinKey;
 import org.apache.hadoop.hive.ql.exec.persistence.MapJoinKeyObject;
 import org.apache.hadoop.hive.ql.exec.persistence.LazyFlatRowContainer;
 import org.apache.hadoop.hive.ql.exec.persistence.MapJoinKey;
+import org.apache.hadoop.hive.ql.exec.persistence.MapJoinObjectSerDeContext;
 import org.apache.hadoop.hive.ql.exec.persistence.MapJoinTableContainer;
 import org.apache.hadoop.hive.ql.exec.persistence.MapJoinTableContainerSerDe;
 import org.apache.hadoop.hive.ql.metadata.HiveException;
 import org.apache.hadoop.hive.ql.plan.MapJoinDesc;
 import org.apache.hadoop.hive.serde2.ByteStream.Output;
-import org.apache.hadoop.hive.serde2.lazybinary.LazyBinarySerDe;
 import org.apache.hadoop.hive.serde2.SerDeException;
+import org.apache.hadoop.hive.serde2.lazybinary.LazyBinaryFactory;
+import org.apache.hadoop.hive.serde2.lazybinary.LazyBinarySerDe;
+import org.apache.hadoop.hive.serde2.lazybinary.LazyBinaryStruct;
+import org.apache.hadoop.hive.serde2.lazybinary.LazyBinaryUtils;
+import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.StructField;
+import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
+import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector.Category;
+import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector.PrimitiveCategory;
+import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo;
+import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoFactory;
+import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoUtils;
 import org.apache.hadoop.io.BytesWritable;
 import org.apache.hadoop.io.Writable;
 import org.apache.tez.runtime.api.LogicalInput;
@@ -70,18 +88,11 @@ public class HashTableLoader implements 
 
     TezContext tezContext = (TezContext) MapredContext.get();
     Map<Integer, String> parentToInput = desc.getParentToInput();
-    int hashTableThreshold = HiveConf.getIntVar(hconf, HiveConf.ConfVars.HIVEHASHTABLETHRESHOLD);
-    float hashTableLoadFactor = HiveConf.getFloatVar(hconf,
-        HiveConf.ConfVars.HIVEHASHTABLELOADFACTOR);
-    boolean useLazyRows = HiveConf.getBoolVar(hconf, HiveConf.ConfVars.HIVEMAPJOINLAZYHASHTABLE);
 
+    boolean useOptimizedTables = HiveConf.getBoolVar(
+        hconf, HiveConf.ConfVars.HIVEMAPJOINUSEOPTIMIZEDTABLE);
+    boolean isFirstKey = true;
     TezCacheAccess tezCacheAccess = TezCacheAccess.createInstance(hconf);
-    // We only check if we can use optimized keys here; that is ok because we don't
-    // create optimized keys in MapJoin if hash map doesn't have optimized keys.
-    if (!HiveConf.getBoolVar(hconf, HiveConf.ConfVars.HIVEMAPJOINUSEOPTIMIZEDKEYS)) {
-      lastKey = new MapJoinKeyObject();
-    }
-    Output output = new Output(); // Reusable output for serialization.
     for (int pos = 0; pos < mapJoinTables.length; pos++) {
       if (pos == desc.getPosBigTable()) {
         continue;
@@ -92,27 +103,29 @@ public class HashTableLoader implements 
 
       try {
         KeyValueReader kvReader = (KeyValueReader) input.getReader();
-
-        MapJoinTableContainer tableContainer = new HashMapWrapper(hashTableThreshold,
-            hashTableLoadFactor);
-
-        // simply read all the kv pairs into the hashtable.
+        MapJoinObjectSerDeContext keyCtx = mapJoinTableSerdes[pos].getKeyContext(),
+          valCtx = mapJoinTableSerdes[pos].getValueContext();
+        if (useOptimizedTables) {
+          ObjectInspector keyOi = keyCtx.getSerDe().getObjectInspector();
+          if (!MapJoinBytesTableContainer.isSupportedKey(keyOi)) {
+            if (isFirstKey) {
+              useOptimizedTables = false;
+            } else {
+              throw new HiveException(describeOi(
+                  "Only a subset of mapjoin keys is supported. Unsupported key: ", keyOi));
+            }
+          }
+        }
+        isFirstKey = false;
+        MapJoinTableContainer tableContainer = useOptimizedTables
+            ? new MapJoinBytesTableContainer(hconf, valCtx) : new HashMapWrapper(hconf);
 
         while (kvReader.next()) {
-          // We pass key in as reference, to find out quickly if optimized keys can be used.
-          // However, we do not reuse the object since we are putting them into the hashmap.
-          lastKey = MapJoinKey.read(output, lastKey, mapJoinTableSerdes[pos].getKeyContext(),
-              (Writable)kvReader.getCurrentKey(), false);
-
-          LazyFlatRowContainer values = (LazyFlatRowContainer)tableContainer.get(lastKey);
-          if (values == null) {
-            values = new LazyFlatRowContainer();
-            tableContainer.put(lastKey, values);
-          }
-          values.add(mapJoinTableSerdes[pos].getValueContext(),
-              (BytesWritable)kvReader.getCurrentValue(), useLazyRows);
+          lastKey = tableContainer.putRow(keyCtx, (Writable)kvReader.getCurrentKey(),
+              valCtx, (Writable)kvReader.getCurrentValue());
         }
 
+        tableContainer.seal();
         mapJoinTables[pos] = tableContainer;
       } catch (IOException e) {
         throw new HiveException(e);
@@ -130,8 +143,17 @@ public class HashTableLoader implements 
         LOG.info("Setting Input: " + inputName + " as cached");
       }
     }
-    if (lastKey == null) {
-      lastKey = new MapJoinKeyObject(); // No rows in tables, the key type doesn't matter.
+  }
+
+  private String describeOi(String desc, ObjectInspector keyOi) {
+    for (StructField field : ((StructObjectInspector)keyOi).getAllStructFieldRefs()) {
+      ObjectInspector oi = field.getFieldObjectInspector();
+      String cat = oi.getCategory().toString();
+      if (oi.getCategory() == Category.PRIMITIVE) {
+        cat = ((PrimitiveObjectInspector)oi).getPrimitiveCategory().toString();
+      }
+      desc += field.getFieldName() + ":" + cat + ", ";
     }
+    return desc;
   }
 }

Modified: hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorMapJoinOperator.java
URL: http://svn.apache.org/viewvc/hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorMapJoinOperator.java?rev=1594713&r1=1594712&r2=1594713&view=diff
==============================================================================
--- hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorMapJoinOperator.java (original)
+++ hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorMapJoinOperator.java Wed May 14 20:53:30 2014
@@ -32,6 +32,8 @@ import org.apache.hadoop.hive.ql.exec.Ta
 import org.apache.hadoop.hive.ql.exec.Utilities;
 import org.apache.hadoop.hive.ql.exec.persistence.MapJoinKeyObject;
 import org.apache.hadoop.hive.ql.exec.persistence.MapJoinKey;
+import org.apache.hadoop.hive.ql.exec.persistence.MapJoinKey;
+import org.apache.hadoop.hive.ql.exec.persistence.MapJoinTableContainer.ReusableGetAdaptor;
 import org.apache.hadoop.hive.ql.exec.vector.expressions.VectorExpression;
 import org.apache.hadoop.hive.ql.exec.vector.expressions.VectorExpressionWriter;
 import org.apache.hadoop.hive.ql.exec.vector.expressions.VectorExpressionWriterFactory;
@@ -72,7 +74,6 @@ public class VectorMapJoinOperator exten
   private VectorExpression[] bigTableValueExpressions;
 
   private transient VectorizedRowBatch outputBatch;
-  private transient MapJoinKeyEvaluator keyEvaluator;
   private transient VectorExpressionWriter[] valueWriters;
   private transient Map<ObjectInspector, VectorColumnAssign[]> outputVectorAssigners;
 
@@ -89,10 +90,6 @@ public class VectorMapJoinOperator exten
     super();
   }
 
-  private interface MapJoinKeyEvaluator {
-      MapJoinKey evaluate(VectorHashKeyWrapper kw)
-          throws HiveException;
-  }
 
   public VectorMapJoinOperator (VectorizationContext vContext, OperatorDesc conf)
     throws HiveException {
@@ -142,30 +139,11 @@ public class VectorMapJoinOperator exten
 
     vrbCtx = new VectorizedRowBatchCtx();
     vrbCtx.init(hconf, this.fileKey, (StructObjectInspector) this.outputObjInspector);
-    
+
     outputBatch = vrbCtx.createVectorizedRowBatch();
 
     keyWrapperBatch =VectorHashKeyWrapperBatch.compileKeyWrapperBatch(keyExpressions);
 
-    // This key evaluator translates from the vectorized VectorHashKeyWrapper format
-    // into the row-mode MapJoinKey
-    keyEvaluator = new MapJoinKeyEvaluator() {
-      private MapJoinKey key = null;
-      private final Output output = new Output();
-
-      public MapJoinKeyEvaluator init() {
-        return this;
-      }
-
-      @Override
-      public MapJoinKey evaluate(VectorHashKeyWrapper kw) throws HiveException {
-        MapJoinKey refKey = getRefKey(key, alias);
-        key = MapJoinKey.readFromVector(
-            output, refKey, kw, keyOutputWriters, keyWrapperBatch, refKey == key);
-        return key;
-      }
-    }.init();
-
     Map<Byte, List<ExprNodeDesc>> valueExpressions = conf.getExprs();
     List<ExprNodeDesc> bigTableExpressions = valueExpressions.get(posBigTable);
 
@@ -258,8 +236,9 @@ public class VectorMapJoinOperator exten
   }
 
   @Override
-  protected MapJoinKey computeMapJoinKey(Object row, byte alias) throws HiveException {
-    return keyEvaluator.evaluate(keyValues[batchIndex]);
+  protected void setMapJoinKey(ReusableGetAdaptor dest, Object row, byte alias)
+      throws HiveException {
+    dest.setFromVector(keyValues[batchIndex], keyOutputWriters, keyWrapperBatch);
   }
 
   @Override

Modified: hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorizedColumnarSerDe.java
URL: http://svn.apache.org/viewvc/hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorizedColumnarSerDe.java?rev=1594713&r1=1594712&r2=1594713&view=diff
==============================================================================
--- hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorizedColumnarSerDe.java (original)
+++ hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/vector/VectorizedColumnarSerDe.java Wed May 14 20:53:30 2014
@@ -194,8 +194,8 @@ public class VectorizedColumnarSerDe ext
           }
 
           byteRow.get(k).set(serializeVectorStream.getData(), count, serializeVectorStream
-              .getCount() - count);
-          count = serializeVectorStream.getCount();
+              .getLength() - count);
+          count = serializeVectorStream.getLength();
         }
 
       }

Modified: hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/optimizer/ConvertJoinMapJoin.java
URL: http://svn.apache.org/viewvc/hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/optimizer/ConvertJoinMapJoin.java?rev=1594713&r1=1594712&r2=1594713&view=diff
==============================================================================
--- hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/optimizer/ConvertJoinMapJoin.java (original)
+++ hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/optimizer/ConvertJoinMapJoin.java Wed May 14 20:53:30 2014
@@ -362,7 +362,6 @@ public class ConvertJoinMapJoin implemen
 
     Operator<? extends OperatorDesc> parentBigTableOp
       = mapJoinOp.getParentOperators().get(bigTablePosition);
-
     if (parentBigTableOp instanceof ReduceSinkOperator) {
       mapJoinOp.getParentOperators().remove(bigTablePosition);
       if (!(mapJoinOp.getParentOperators().contains(

Modified: hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/optimizer/ReduceSinkMapJoinProc.java
URL: http://svn.apache.org/viewvc/hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/optimizer/ReduceSinkMapJoinProc.java?rev=1594713&r1=1594712&r2=1594713&view=diff
==============================================================================
--- hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/optimizer/ReduceSinkMapJoinProc.java (original)
+++ hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/optimizer/ReduceSinkMapJoinProc.java Wed May 14 20:53:30 2014
@@ -77,7 +77,9 @@ public class ReduceSinkMapJoinProc imple
     context.currentRootOperator = null;
 
     ReduceSinkOperator parentRS = (ReduceSinkOperator)stack.get(stack.size() - 2);
-
+    // remove the tag for in-memory side of mapjoin
+    parentRS.getConf().setSkipTag(true);
+    parentRS.setSkipTag(true);
     // remember the original parent list before we start modifying it.
     if (!context.mapJoinParentMap.containsKey(mapJoinOp)) {
       List<Operator<?>> parents = new ArrayList(mapJoinOp.getParentOperators());
@@ -166,8 +168,7 @@ public class ReduceSinkMapJoinProc imple
     context.linkWorkWithReduceSinkMap.put(parentWork, reduceSinks);
 
     // create the dummy operators
-    List<Operator<? extends OperatorDesc>> dummyOperators =
-        new ArrayList<Operator<? extends OperatorDesc>>();
+    List<Operator<?>> dummyOperators = new ArrayList<Operator<?>>();
 
     // create an new operator: HashTableDummyOperator, which share the table desc
     HashTableDummyDesc desc = new HashTableDummyDesc();

Modified: hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/plan/ReduceSinkDesc.java
URL: http://svn.apache.org/viewvc/hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/plan/ReduceSinkDesc.java?rev=1594713&r1=1594712&r2=1594713&view=diff
==============================================================================
--- hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/plan/ReduceSinkDesc.java (original)
+++ hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/plan/ReduceSinkDesc.java Wed May 14 20:53:30 2014
@@ -21,6 +21,9 @@ package org.apache.hadoop.hive.ql.plan;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
 
 /**
  * ReduceSinkDesc.
@@ -83,7 +86,9 @@ public class ReduceSinkDesc extends Abst
   private int topN = -1;
   private float topNMemoryUsage = -1;
   private boolean mapGroupBy;  // for group-by, values with same key on top-K should be forwarded
+  private boolean skipTag; // Skip writing tags when feeding into mapjoin hashtable
 
+  private static transient Log LOG = LogFactory.getLog(ReduceSinkDesc.class);
   public ReduceSinkDesc() {
   }
 
@@ -133,6 +138,7 @@ public class ReduceSinkDesc extends Abst
     desc.setNumBuckets(numBuckets);
     desc.setBucketCols(bucketCols);
     desc.setStatistics(this.getStatistics());
+    desc.setSkipTag(skipTag);
     return desc;
   }
 
@@ -326,4 +332,12 @@ public class ReduceSinkDesc extends Abst
   public void setBucketCols(List<ExprNodeDesc> bucketCols) {
     this.bucketCols = bucketCols;
   }
+
+  public void setSkipTag(boolean value) {
+    this.skipTag = value;
+  }
+
+  public boolean getSkipTag() {
+    return skipTag;
+  }
 }

Modified: hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/udf/UDFToString.java
URL: http://svn.apache.org/viewvc/hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/udf/UDFToString.java?rev=1594713&r1=1594712&r2=1594713&view=diff
==============================================================================
--- hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/udf/UDFToString.java (original)
+++ hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/udf/UDFToString.java Wed May 14 20:53:30 2014
@@ -70,7 +70,7 @@ public class UDFToString extends UDF {
     } else {
       out.reset();
       LazyInteger.writeUTF8NoException(out, i.get());
-      t.set(out.getData(), 0, out.getCount());
+      t.set(out.getData(), 0, out.getLength());
       return t;
     }
   }
@@ -81,7 +81,7 @@ public class UDFToString extends UDF {
     } else {
       out.reset();
       LazyInteger.writeUTF8NoException(out, i.get());
-      t.set(out.getData(), 0, out.getCount());
+      t.set(out.getData(), 0, out.getLength());
       return t;
     }
   }
@@ -92,7 +92,7 @@ public class UDFToString extends UDF {
     } else {
       out.reset();
       LazyInteger.writeUTF8NoException(out, i.get());
-      t.set(out.getData(), 0, out.getCount());
+      t.set(out.getData(), 0, out.getLength());
       return t;
     }
   }
@@ -103,7 +103,7 @@ public class UDFToString extends UDF {
     } else {
       out.reset();
       LazyLong.writeUTF8NoException(out, i.get());
-      t.set(out.getData(), 0, out.getCount());
+      t.set(out.getData(), 0, out.getLength());
       return t;
     }
   }

Added: hive/trunk/ql/src/test/org/apache/hadoop/hive/ql/exec/persistence/TestBytesBytesMultiHashMap.java
URL: http://svn.apache.org/viewvc/hive/trunk/ql/src/test/org/apache/hadoop/hive/ql/exec/persistence/TestBytesBytesMultiHashMap.java?rev=1594713&view=auto
==============================================================================
--- hive/trunk/ql/src/test/org/apache/hadoop/hive/ql/exec/persistence/TestBytesBytesMultiHashMap.java (added)
+++ hive/trunk/ql/src/test/org/apache/hadoop/hive/ql/exec/persistence/TestBytesBytesMultiHashMap.java Wed May 14 20:53:30 2014
@@ -0,0 +1,237 @@
+/**
+ * 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.hive.ql.exec.persistence;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+
+import org.apache.hadoop.hive.serde2.ByteStream.RandomAccessOutput;
+import org.apache.hadoop.hive.serde2.lazybinary.LazyBinaryUtils;
+import org.apache.hadoop.hive.serde2.SerDeException;
+import org.apache.hadoop.hive.serde2.WriteBuffers;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class TestBytesBytesMultiHashMap {
+  private static final float LOAD_FACTOR = 0.75f;
+  private static final int CAPACITY = 8;
+  private static final int WB_SIZE = 128; // Make sure we cross some buffer boundaries...
+
+  @Test
+  public void testCapacityValidation() {
+    BytesBytesMultiHashMap map = new BytesBytesMultiHashMap(CAPACITY, LOAD_FACTOR, WB_SIZE);
+    assertEquals(CAPACITY, map.getCapacity());
+    map = new BytesBytesMultiHashMap(9, LOAD_FACTOR, WB_SIZE);
+    assertEquals(16, map.getCapacity());
+  }
+
+  @Test
+  public void testPutGetOne() throws Exception {
+    BytesBytesMultiHashMap map = new BytesBytesMultiHashMap(CAPACITY, LOAD_FACTOR, WB_SIZE);
+    RandomKvSource kv = new RandomKvSource(0, 0);
+    map.put(kv);
+    verifyResults(map, kv.getLastKey(), kv.getLastValue());
+    kv = new RandomKvSource(10, 100);
+    map.put(kv);
+    verifyResults(map, kv.getLastKey(), kv.getLastValue());
+  }
+
+  @Test
+  public void testPutGetMultiple() throws Exception {
+    BytesBytesMultiHashMap map = new BytesBytesMultiHashMap(CAPACITY, LOAD_FACTOR, WB_SIZE);
+    RandomKvSource kv = new RandomKvSource(0, 100);
+    map.put(kv);
+    verifyResults(map, kv.getLastKey(), kv.getLastValue());
+    FixedKeyKvSource kv2 = new FixedKeyKvSource(kv.getLastKey(), 0, 100);
+    kv2.values.add(kv.getLastValue());
+    for (int i = 0; i < 3; ++i) {
+      map.put(kv2);
+      verifyResults(map, kv2.key, kv2.values.toArray(new byte[kv2.values.size()][]));
+    }
+  }
+
+  @Test
+  public void testGetNonExistent() throws Exception {
+    BytesBytesMultiHashMap map = new BytesBytesMultiHashMap(CAPACITY, LOAD_FACTOR, WB_SIZE);
+    RandomKvSource kv = new RandomKvSource(1, 100);
+    map.put(kv);
+    byte[] key = kv.getLastKey();
+    key[0] = (byte)(key[0] + 1);
+    FixedKeyKvSource kv2 = new FixedKeyKvSource(kv.getLastKey(), 0, 100);
+    map.put(kv2);
+    key[0] = (byte)(key[0] + 1);
+    List<WriteBuffers.ByteSegmentRef> results = new ArrayList<WriteBuffers.ByteSegmentRef>(0);
+    map.getValueRefs(key, key.length, results);
+    assertTrue(results.isEmpty());
+    map.getValueRefs(key, 0, results);
+    assertTrue(results.isEmpty());
+  }
+
+  @Test
+  public void testPutWithFullMap() throws Exception {
+    // Make sure the map does not expand; should be able to find space.
+    BytesBytesMultiHashMap map = new BytesBytesMultiHashMap(CAPACITY, 1f, WB_SIZE);
+    UniqueKeysKvSource kv = new UniqueKeysKvSource();
+    for (int i = 0; i < CAPACITY; ++i) {
+      map.put(kv);
+    }
+    for (int i = 0; i < kv.keys.size(); ++i) {
+      verifyResults(map, kv.keys.get(i), kv.values.get(i));
+    }
+    assertEquals(CAPACITY, map.getCapacity());
+    // Get of non-existent key should terminate..
+    List<WriteBuffers.ByteSegmentRef> results = new ArrayList<WriteBuffers.ByteSegmentRef>(0);
+    map.getValueRefs(new byte[0], 0, results);
+    assertTrue(results.isEmpty());
+  }
+
+  @Test
+  public void testExpand() throws Exception {
+    // Start with capacity 1; make sure we expand on every put.
+    BytesBytesMultiHashMap map = new BytesBytesMultiHashMap(1, 0.0000001f, WB_SIZE);
+    UniqueKeysKvSource kv = new UniqueKeysKvSource();
+    for (int i = 0; i < 18; ++i) {
+      map.put(kv);
+      for (int j = 0; j <= i; ++j) {
+        verifyResults(map, kv.keys.get(j), kv.values.get(j));
+      }
+    }
+    assertEquals(1 << 18, map.getCapacity());
+  }
+
+  private void verifyResults(BytesBytesMultiHashMap map, byte[] key, byte[]... values) {
+    List<WriteBuffers.ByteSegmentRef> results = new ArrayList<WriteBuffers.ByteSegmentRef>(0);
+    byte state = map.getValueRefs(key, key.length, results);
+    assertEquals(state, results.size());
+    assertEquals(values.length, results.size());
+    HashSet<ByteBuffer> hs = new HashSet<ByteBuffer>();
+    for (int i = 0; i < results.size(); ++i) {
+      WriteBuffers.ByteSegmentRef result = results.get(i);
+      map.populateValue(result);
+      hs.add(result.copy());
+    }
+    for (int i = 0; i < values.length; ++i) {
+      assertTrue(hs.contains(ByteBuffer.wrap(values[i])));
+    }
+  }
+
+  private static class FixedKeyKvSource extends RandomKvSource {
+    private byte[] key;
+
+    public FixedKeyKvSource(byte[] key, int minLength, int maxLength) {
+      super(minLength, maxLength);
+      this.key = key;
+    }
+
+    @Override
+    public void writeKey(RandomAccessOutput dest) throws SerDeException {
+      try {
+        dest.write(key);
+      } catch (IOException e) {
+        e.printStackTrace();
+        fail("Thrown " + e.getMessage());
+      }
+    }
+  }
+
+  private static class UniqueKeysKvSource extends RandomKvSource {
+    private long lastKey = -1;
+    private byte[] buffer = new byte[9];
+    private byte[] lastBuffer;
+
+    public UniqueKeysKvSource() {
+      super(0, 0);
+    }
+
+    @Override
+    public void writeKey(RandomAccessOutput dest) throws SerDeException {
+      lastKey += 465623573; // This number is certified to be random.
+      int len = LazyBinaryUtils.writeVLongToByteArray(buffer, lastKey);
+      lastBuffer = Arrays.copyOf(buffer, len);
+      keys.add(lastBuffer);
+      writeLastBuffer(dest);
+    }
+
+    private void writeLastBuffer(RandomAccessOutput dest) {
+      try {
+        dest.write(lastBuffer);
+      } catch (IOException e) {
+        e.printStackTrace();
+        fail("Thrown " + e.getMessage());
+      }
+    }
+
+    @Override
+    public void writeValue(RandomAccessOutput dest) throws SerDeException {
+      // Assumes value is written after key.
+      values.add(lastBuffer);
+      writeLastBuffer(dest);
+    }
+  }
+
+  private static class RandomKvSource implements BytesBytesMultiHashMap.KvSource {
+    private int minLength, maxLength;
+    private final Random rdm = new Random(43);
+    public List<byte[]> keys = new ArrayList<byte[]>(), values = new ArrayList<byte[]>();
+
+    public RandomKvSource(int minLength, int maxLength) {
+      this.minLength = minLength;
+      this.maxLength = maxLength;
+    }
+
+    public byte[] getLastValue() {
+      return values.get(values.size() - 1);
+    }
+
+    public byte[] getLastKey() {
+      return keys.get(keys.size() - 1);
+    }
+
+    @Override
+    public void writeKey(RandomAccessOutput dest) throws SerDeException {
+      keys.add(write(dest));
+    }
+
+    @Override
+    public void writeValue(RandomAccessOutput dest) throws SerDeException {
+      values.add(write(dest));
+    }
+
+    protected byte[] write(RandomAccessOutput dest) {
+      byte[] bytes = new byte[minLength + rdm.nextInt(maxLength - minLength + 1)];
+      rdm.nextBytes(bytes);
+      try {
+        dest.write(bytes);
+      } catch (IOException e) {
+        e.printStackTrace();
+        fail("Thrown " + e.getMessage());
+      }
+      return bytes;
+    }
+
+    @Override
+    public byte updateStateByte(Byte previousValue) {
+      return (byte)(previousValue == null ? 1 : previousValue + 1);
+    }
+  }
+}

Modified: hive/trunk/ql/src/test/org/apache/hadoop/hive/ql/exec/persistence/TestMapJoinEqualityTableContainer.java
URL: http://svn.apache.org/viewvc/hive/trunk/ql/src/test/org/apache/hadoop/hive/ql/exec/persistence/TestMapJoinEqualityTableContainer.java?rev=1594713&r1=1594712&r2=1594713&view=diff
==============================================================================
--- hive/trunk/ql/src/test/org/apache/hadoop/hive/ql/exec/persistence/TestMapJoinEqualityTableContainer.java (original)
+++ hive/trunk/ql/src/test/org/apache/hadoop/hive/ql/exec/persistence/TestMapJoinEqualityTableContainer.java Wed May 14 20:53:30 2014
@@ -34,7 +34,7 @@ public class TestMapJoinEqualityTableCon
   private static final MapJoinKeyObject KEY3 = new MapJoinKeyObject(new Object[] {new Text("key3")});
   private static final MapJoinKeyObject KEY4 = new MapJoinKeyObject(new Object[] {new Text("key4")});
   private static final Object[] VALUE = new Object[] {new Text("value")};
-  private MapJoinTableContainer container;
+  private HashMapWrapper container;
   private MapJoinRowContainer rowContainer;
   @Before
   public void setup() throws Exception {

Modified: hive/trunk/ql/src/test/org/apache/hadoop/hive/ql/exec/persistence/TestMapJoinTableContainer.java
URL: http://svn.apache.org/viewvc/hive/trunk/ql/src/test/org/apache/hadoop/hive/ql/exec/persistence/TestMapJoinTableContainer.java?rev=1594713&r1=1594712&r2=1594713&view=diff
==============================================================================
--- hive/trunk/ql/src/test/org/apache/hadoop/hive/ql/exec/persistence/TestMapJoinTableContainer.java (original)
+++ hive/trunk/ql/src/test/org/apache/hadoop/hive/ql/exec/persistence/TestMapJoinTableContainer.java Wed May 14 20:53:30 2014
@@ -39,7 +39,7 @@ public class TestMapJoinTableContainer {
   private ByteArrayOutputStream baos;
   private ObjectOutputStream out;
   private ObjectInputStream in;
-  private MapJoinTableContainer container;
+  private MapJoinPersistableTableContainer container;
   private MapJoinTableContainerSerDe containerSerde;
   private MapJoinKeyObject key;
   private MapJoinRowContainer rowContainer;

Modified: hive/trunk/ql/src/test/queries/clientpositive/mapjoin_decimal.q
URL: http://svn.apache.org/viewvc/hive/trunk/ql/src/test/queries/clientpositive/mapjoin_decimal.q?rev=1594713&r1=1594712&r2=1594713&view=diff
==============================================================================
--- hive/trunk/ql/src/test/queries/clientpositive/mapjoin_decimal.q (original)
+++ hive/trunk/ql/src/test/queries/clientpositive/mapjoin_decimal.q Wed May 14 20:53:30 2014
@@ -26,10 +26,15 @@ INSERT INTO TABLE t2 select dec from ove
 explain
 select t1.dec, t2.dec from t1 join t2 on (t1.dec=t2.dec);
 
+set hive.mapjoin.optimized.hashtable=false;
 set hive.mapjoin.optimized.keys=false;
 
 select t1.dec, t2.dec from t1 join t2 on (t1.dec=t2.dec);
 
 set hive.mapjoin.optimized.keys=true;
 
+select t1.dec, t2.dec from t1 join t2 on (t1.dec=t2.dec);
+
+set hive.mapjoin.optimized.hashtable=true;
+
 select t1.dec, t2.dec from t1 join t2 on (t1.dec=t2.dec);
\ No newline at end of file

Modified: hive/trunk/ql/src/test/queries/clientpositive/mapjoin_mapjoin.q
URL: http://svn.apache.org/viewvc/hive/trunk/ql/src/test/queries/clientpositive/mapjoin_mapjoin.q?rev=1594713&r1=1594712&r2=1594713&view=diff
==============================================================================
--- hive/trunk/ql/src/test/queries/clientpositive/mapjoin_mapjoin.q (original)
+++ hive/trunk/ql/src/test/queries/clientpositive/mapjoin_mapjoin.q Wed May 14 20:53:30 2014
@@ -8,12 +8,18 @@ explain select srcpart.key from srcpart 
 
 explain
 select srcpart.key from srcpart join src on (srcpart.value=src.value) join src1 on (srcpart.key=src1.key) where srcpart.value > 'val_450';
-select srcpart.key from srcpart join src on (srcpart.value=src.value) join src1 on (srcpart.key=src1.key) where srcpart.value > 'val_450';
 
 explain
 select count(*) from srcpart join src on (srcpart.value=src.value) join src src1 on (srcpart.key=src1.key) group by ds;
+
+set hive.mapjoin.optimized.hashtable=false;
+
+select srcpart.key from srcpart join src on (srcpart.value=src.value) join src1 on (srcpart.key=src1.key) where srcpart.value > 'val_450';
+
 select count(*) from srcpart join src on (srcpart.value=src.value) join src src1 on (srcpart.key=src1.key) group by ds;
 
-set hive.mapjoin.lazy.hashtable=false;
+set hive.mapjoin.optimized.hashtable=true;
+
+select srcpart.key from srcpart join src on (srcpart.value=src.value) join src1 on (srcpart.key=src1.key) where srcpart.value > 'val_450';
 
-select count(*) from srcpart join src src on (srcpart.value=src.value) join src src1 on (srcpart.key=src1.key) group by ds;
+select count(*) from srcpart join src on (srcpart.value=src.value) join src src1 on (srcpart.key=src1.key) group by ds;

Modified: hive/trunk/ql/src/test/queries/clientpositive/tez_union.q
URL: http://svn.apache.org/viewvc/hive/trunk/ql/src/test/queries/clientpositive/tez_union.q?rev=1594713&r1=1594712&r2=1594713&view=diff
==============================================================================
--- hive/trunk/ql/src/test/queries/clientpositive/tez_union.q (original)
+++ hive/trunk/ql/src/test/queries/clientpositive/tez_union.q Wed May 14 20:53:30 2014
@@ -91,4 +91,4 @@ select u.key as ukey, s.key as skey from
 right outer join src s on u.key = s.key;
 
 select * from ut order by ukey, skey limit 20;
-drop table ut;
\ No newline at end of file
+drop table ut;

Modified: hive/trunk/ql/src/test/results/clientpositive/mapjoin_mapjoin.q.out
URL: http://svn.apache.org/viewvc/hive/trunk/ql/src/test/results/clientpositive/mapjoin_mapjoin.q.out?rev=1594713&r1=1594712&r2=1594713&view=diff
==============================================================================
--- hive/trunk/ql/src/test/results/clientpositive/mapjoin_mapjoin.q.out (original)
+++ hive/trunk/ql/src/test/results/clientpositive/mapjoin_mapjoin.q.out Wed May 14 20:53:30 2014
@@ -192,46 +192,6 @@ STAGE PLANS:
       Processor Tree:
         ListSink
 
-PREHOOK: query: select srcpart.key from srcpart join src on (srcpart.value=src.value) join src1 on (srcpart.key=src1.key) where srcpart.value > 'val_450'
-PREHOOK: type: QUERY
-PREHOOK: Input: default@src
-PREHOOK: Input: default@src1
-PREHOOK: Input: default@srcpart
-PREHOOK: Input: default@srcpart@ds=2008-04-08/hr=11
-PREHOOK: Input: default@srcpart@ds=2008-04-08/hr=12
-PREHOOK: Input: default@srcpart@ds=2008-04-09/hr=11
-PREHOOK: Input: default@srcpart@ds=2008-04-09/hr=12
-#### A masked pattern was here ####
-POSTHOOK: query: select srcpart.key from srcpart join src on (srcpart.value=src.value) join src1 on (srcpart.key=src1.key) where srcpart.value > 'val_450'
-POSTHOOK: type: QUERY
-POSTHOOK: Input: default@src
-POSTHOOK: Input: default@src1
-POSTHOOK: Input: default@srcpart
-POSTHOOK: Input: default@srcpart@ds=2008-04-08/hr=11
-POSTHOOK: Input: default@srcpart@ds=2008-04-08/hr=12
-POSTHOOK: Input: default@srcpart@ds=2008-04-09/hr=11
-POSTHOOK: Input: default@srcpart@ds=2008-04-09/hr=12
-#### A masked pattern was here ####
-98
-98
-66
-98
-98
-98
-98
-66
-98
-98
-98
-98
-66
-98
-98
-98
-98
-66
-98
-98
 PREHOOK: query: explain
 select count(*) from srcpart join src on (srcpart.value=src.value) join src src1 on (srcpart.key=src1.key) group by ds
 PREHOOK: type: QUERY
@@ -368,6 +328,46 @@ STAGE PLANS:
       Processor Tree:
         ListSink
 
+PREHOOK: query: select srcpart.key from srcpart join src on (srcpart.value=src.value) join src1 on (srcpart.key=src1.key) where srcpart.value > 'val_450'
+PREHOOK: type: QUERY
+PREHOOK: Input: default@src
+PREHOOK: Input: default@src1
+PREHOOK: Input: default@srcpart
+PREHOOK: Input: default@srcpart@ds=2008-04-08/hr=11
+PREHOOK: Input: default@srcpart@ds=2008-04-08/hr=12
+PREHOOK: Input: default@srcpart@ds=2008-04-09/hr=11
+PREHOOK: Input: default@srcpart@ds=2008-04-09/hr=12
+#### A masked pattern was here ####
+POSTHOOK: query: select srcpart.key from srcpart join src on (srcpart.value=src.value) join src1 on (srcpart.key=src1.key) where srcpart.value > 'val_450'
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@src
+POSTHOOK: Input: default@src1
+POSTHOOK: Input: default@srcpart
+POSTHOOK: Input: default@srcpart@ds=2008-04-08/hr=11
+POSTHOOK: Input: default@srcpart@ds=2008-04-08/hr=12
+POSTHOOK: Input: default@srcpart@ds=2008-04-09/hr=11
+POSTHOOK: Input: default@srcpart@ds=2008-04-09/hr=12
+#### A masked pattern was here ####
+98
+98
+66
+98
+98
+98
+98
+66
+98
+98
+98
+98
+66
+98
+98
+98
+98
+66
+98
+98
 PREHOOK: query: select count(*) from srcpart join src on (srcpart.value=src.value) join src src1 on (srcpart.key=src1.key) group by ds
 PREHOOK: type: QUERY
 PREHOOK: Input: default@src
@@ -388,16 +388,56 @@ POSTHOOK: Input: default@srcpart@ds=2008
 #### A masked pattern was here ####
 5308
 5308
-PREHOOK: query: select count(*) from srcpart join src src on (srcpart.value=src.value) join src src1 on (srcpart.key=src1.key) group by ds
+PREHOOK: query: select srcpart.key from srcpart join src on (srcpart.value=src.value) join src1 on (srcpart.key=src1.key) where srcpart.value > 'val_450'
 PREHOOK: type: QUERY
 PREHOOK: Input: default@src
+PREHOOK: Input: default@src1
 PREHOOK: Input: default@srcpart
 PREHOOK: Input: default@srcpart@ds=2008-04-08/hr=11
 PREHOOK: Input: default@srcpart@ds=2008-04-08/hr=12
 PREHOOK: Input: default@srcpart@ds=2008-04-09/hr=11
 PREHOOK: Input: default@srcpart@ds=2008-04-09/hr=12
 #### A masked pattern was here ####
-POSTHOOK: query: select count(*) from srcpart join src src on (srcpart.value=src.value) join src src1 on (srcpart.key=src1.key) group by ds
+POSTHOOK: query: select srcpart.key from srcpart join src on (srcpart.value=src.value) join src1 on (srcpart.key=src1.key) where srcpart.value > 'val_450'
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@src
+POSTHOOK: Input: default@src1
+POSTHOOK: Input: default@srcpart
+POSTHOOK: Input: default@srcpart@ds=2008-04-08/hr=11
+POSTHOOK: Input: default@srcpart@ds=2008-04-08/hr=12
+POSTHOOK: Input: default@srcpart@ds=2008-04-09/hr=11
+POSTHOOK: Input: default@srcpart@ds=2008-04-09/hr=12
+#### A masked pattern was here ####
+98
+98
+66
+98
+98
+98
+98
+66
+98
+98
+98
+98
+66
+98
+98
+98
+98
+66
+98
+98
+PREHOOK: query: select count(*) from srcpart join src on (srcpart.value=src.value) join src src1 on (srcpart.key=src1.key) group by ds
+PREHOOK: type: QUERY
+PREHOOK: Input: default@src
+PREHOOK: Input: default@srcpart
+PREHOOK: Input: default@srcpart@ds=2008-04-08/hr=11
+PREHOOK: Input: default@srcpart@ds=2008-04-08/hr=12
+PREHOOK: Input: default@srcpart@ds=2008-04-09/hr=11
+PREHOOK: Input: default@srcpart@ds=2008-04-09/hr=12
+#### A masked pattern was here ####
+POSTHOOK: query: select count(*) from srcpart join src on (srcpart.value=src.value) join src src1 on (srcpart.key=src1.key) group by ds
 POSTHOOK: type: QUERY
 POSTHOOK: Input: default@src
 POSTHOOK: Input: default@srcpart

Modified: hive/trunk/ql/src/test/results/clientpositive/tez/mapjoin_decimal.q.out
URL: http://svn.apache.org/viewvc/hive/trunk/ql/src/test/results/clientpositive/tez/mapjoin_decimal.q.out?rev=1594713&r1=1594712&r2=1594713&view=diff
==============================================================================
--- hive/trunk/ql/src/test/results/clientpositive/tez/mapjoin_decimal.q.out (original)
+++ hive/trunk/ql/src/test/results/clientpositive/tez/mapjoin_decimal.q.out Wed May 14 20:53:30 2014
@@ -371,3 +371,121 @@ POSTHOOK: Lineage: t2.dec EXPRESSION [(o
 14	14
 14	14
 14	14
+PREHOOK: query: select t1.dec, t2.dec from t1 join t2 on (t1.dec=t2.dec)
+PREHOOK: type: QUERY
+PREHOOK: Input: default@t1
+PREHOOK: Input: default@t2
+#### A masked pattern was here ####
+POSTHOOK: query: select t1.dec, t2.dec from t1 join t2 on (t1.dec=t2.dec)
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@t1
+POSTHOOK: Input: default@t2
+#### A masked pattern was here ####
+POSTHOOK: Lineage: t1.dec SIMPLE [(over1k)over1k.FieldSchema(name:dec, type:decimal(4,2), comment:null), ]
+POSTHOOK: Lineage: t2.dec EXPRESSION [(over1k)over1k.FieldSchema(name:dec, type:decimal(4,2), comment:null), ]
+9	9
+9	9
+9	9
+9	9
+9	9
+9	9
+9	9
+9	9
+9	9
+9	9
+9	9
+9	9
+9	9
+9	9
+9	9
+9	9
+9	9
+9	9
+9	9
+9	9
+9	9
+45	45
+45	45
+45	45
+45	45
+45	45
+79	79
+79	79
+79	79
+79	79
+79	79
+79	79
+17	17
+17	17
+17	17
+17	17
+17	17
+17	17
+17	17
+17	17
+17	17
+17	17
+6	6
+6	6
+6	6
+6	6
+6	6
+6	6
+62	62
+62	62
+62	62
+62	62
+62	62
+62	62
+62	62
+62	62
+62	62
+62	62
+62	62
+62	62
+64	64
+64	64
+64	64
+64	64
+64	64
+64	64
+64	64
+64	64
+64	64
+64	64
+64	64
+64	64
+64	64
+64	64
+64	64
+64	64
+64	64
+64	64
+89	89
+89	89
+89	89
+89	89
+89	89
+89	89
+89	89
+89	89
+89	89
+89	89
+89	89
+89	89
+70	70
+70	70
+70	70
+70	70
+70	70
+70	70
+70	70
+14	14
+14	14
+14	14
+14	14
+14	14
+14	14
+14	14
+14	14
+14	14

Modified: hive/trunk/ql/src/test/results/clientpositive/tez/mapjoin_mapjoin.q.out
URL: http://svn.apache.org/viewvc/hive/trunk/ql/src/test/results/clientpositive/tez/mapjoin_mapjoin.q.out?rev=1594713&r1=1594712&r2=1594713&view=diff
==============================================================================
--- hive/trunk/ql/src/test/results/clientpositive/tez/mapjoin_mapjoin.q.out (original)
+++ hive/trunk/ql/src/test/results/clientpositive/tez/mapjoin_mapjoin.q.out Wed May 14 20:53:30 2014
@@ -170,46 +170,6 @@ STAGE PLANS:
       Processor Tree:
         ListSink
 
-PREHOOK: query: select srcpart.key from srcpart join src on (srcpart.value=src.value) join src1 on (srcpart.key=src1.key) where srcpart.value > 'val_450'
-PREHOOK: type: QUERY
-PREHOOK: Input: default@src
-PREHOOK: Input: default@src1
-PREHOOK: Input: default@srcpart
-PREHOOK: Input: default@srcpart@ds=2008-04-08/hr=11
-PREHOOK: Input: default@srcpart@ds=2008-04-08/hr=12
-PREHOOK: Input: default@srcpart@ds=2008-04-09/hr=11
-PREHOOK: Input: default@srcpart@ds=2008-04-09/hr=12
-#### A masked pattern was here ####
-POSTHOOK: query: select srcpart.key from srcpart join src on (srcpart.value=src.value) join src1 on (srcpart.key=src1.key) where srcpart.value > 'val_450'
-POSTHOOK: type: QUERY
-POSTHOOK: Input: default@src
-POSTHOOK: Input: default@src1
-POSTHOOK: Input: default@srcpart
-POSTHOOK: Input: default@srcpart@ds=2008-04-08/hr=11
-POSTHOOK: Input: default@srcpart@ds=2008-04-08/hr=12
-POSTHOOK: Input: default@srcpart@ds=2008-04-09/hr=11
-POSTHOOK: Input: default@srcpart@ds=2008-04-09/hr=12
-#### A masked pattern was here ####
-98
-98
-66
-98
-98
-98
-98
-66
-98
-98
-98
-98
-66
-98
-98
-98
-98
-66
-98
-98
 PREHOOK: query: explain
 select count(*) from srcpart join src on (srcpart.value=src.value) join src src1 on (srcpart.key=src1.key) group by ds
 PREHOOK: type: QUERY
@@ -317,6 +277,46 @@ STAGE PLANS:
       Processor Tree:
         ListSink
 
+PREHOOK: query: select srcpart.key from srcpart join src on (srcpart.value=src.value) join src1 on (srcpart.key=src1.key) where srcpart.value > 'val_450'
+PREHOOK: type: QUERY
+PREHOOK: Input: default@src
+PREHOOK: Input: default@src1
+PREHOOK: Input: default@srcpart
+PREHOOK: Input: default@srcpart@ds=2008-04-08/hr=11
+PREHOOK: Input: default@srcpart@ds=2008-04-08/hr=12
+PREHOOK: Input: default@srcpart@ds=2008-04-09/hr=11
+PREHOOK: Input: default@srcpart@ds=2008-04-09/hr=12
+#### A masked pattern was here ####
+POSTHOOK: query: select srcpart.key from srcpart join src on (srcpart.value=src.value) join src1 on (srcpart.key=src1.key) where srcpart.value > 'val_450'
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@src
+POSTHOOK: Input: default@src1
+POSTHOOK: Input: default@srcpart
+POSTHOOK: Input: default@srcpart@ds=2008-04-08/hr=11
+POSTHOOK: Input: default@srcpart@ds=2008-04-08/hr=12
+POSTHOOK: Input: default@srcpart@ds=2008-04-09/hr=11
+POSTHOOK: Input: default@srcpart@ds=2008-04-09/hr=12
+#### A masked pattern was here ####
+98
+98
+66
+98
+98
+98
+98
+66
+98
+98
+98
+98
+66
+98
+98
+98
+98
+66
+98
+98
 PREHOOK: query: select count(*) from srcpart join src on (srcpart.value=src.value) join src src1 on (srcpart.key=src1.key) group by ds
 PREHOOK: type: QUERY
 PREHOOK: Input: default@src
@@ -337,16 +337,56 @@ POSTHOOK: Input: default@srcpart@ds=2008
 #### A masked pattern was here ####
 5308
 5308
-PREHOOK: query: select count(*) from srcpart join src src on (srcpart.value=src.value) join src src1 on (srcpart.key=src1.key) group by ds
+PREHOOK: query: select srcpart.key from srcpart join src on (srcpart.value=src.value) join src1 on (srcpart.key=src1.key) where srcpart.value > 'val_450'
 PREHOOK: type: QUERY
 PREHOOK: Input: default@src
+PREHOOK: Input: default@src1
 PREHOOK: Input: default@srcpart
 PREHOOK: Input: default@srcpart@ds=2008-04-08/hr=11
 PREHOOK: Input: default@srcpart@ds=2008-04-08/hr=12
 PREHOOK: Input: default@srcpart@ds=2008-04-09/hr=11
 PREHOOK: Input: default@srcpart@ds=2008-04-09/hr=12
 #### A masked pattern was here ####
-POSTHOOK: query: select count(*) from srcpart join src src on (srcpart.value=src.value) join src src1 on (srcpart.key=src1.key) group by ds
+POSTHOOK: query: select srcpart.key from srcpart join src on (srcpart.value=src.value) join src1 on (srcpart.key=src1.key) where srcpart.value > 'val_450'
+POSTHOOK: type: QUERY
+POSTHOOK: Input: default@src
+POSTHOOK: Input: default@src1
+POSTHOOK: Input: default@srcpart
+POSTHOOK: Input: default@srcpart@ds=2008-04-08/hr=11
+POSTHOOK: Input: default@srcpart@ds=2008-04-08/hr=12
+POSTHOOK: Input: default@srcpart@ds=2008-04-09/hr=11
+POSTHOOK: Input: default@srcpart@ds=2008-04-09/hr=12
+#### A masked pattern was here ####
+98
+98
+66
+98
+98
+98
+98
+66
+98
+98
+98
+98
+66
+98
+98
+98
+98
+66
+98
+98
+PREHOOK: query: select count(*) from srcpart join src on (srcpart.value=src.value) join src src1 on (srcpart.key=src1.key) group by ds
+PREHOOK: type: QUERY
+PREHOOK: Input: default@src
+PREHOOK: Input: default@srcpart
+PREHOOK: Input: default@srcpart@ds=2008-04-08/hr=11
+PREHOOK: Input: default@srcpart@ds=2008-04-08/hr=12
+PREHOOK: Input: default@srcpart@ds=2008-04-09/hr=11
+PREHOOK: Input: default@srcpart@ds=2008-04-09/hr=12
+#### A masked pattern was here ####
+POSTHOOK: query: select count(*) from srcpart join src on (srcpart.value=src.value) join src src1 on (srcpart.key=src1.key) group by ds
 POSTHOOK: type: QUERY
 POSTHOOK: Input: default@src
 POSTHOOK: Input: default@srcpart

Modified: hive/trunk/serde/src/java/org/apache/hadoop/hive/serde2/ByteStream.java
URL: http://svn.apache.org/viewvc/hive/trunk/serde/src/java/org/apache/hadoop/hive/serde2/ByteStream.java?rev=1594713&r1=1594712&r2=1594713&view=diff
==============================================================================
--- hive/trunk/serde/src/java/org/apache/hadoop/hive/serde2/ByteStream.java (original)
+++ hive/trunk/serde/src/java/org/apache/hadoop/hive/serde2/ByteStream.java Wed May 14 20:53:30 2014
@@ -18,12 +18,13 @@
 
 package org.apache.hadoop.hive.serde2;
 
+import java.io.IOException;
+
 import org.apache.hadoop.hive.common.io.NonSyncByteArrayInputStream;
 import org.apache.hadoop.hive.common.io.NonSyncByteArrayOutputStream;
 
 /**
  * Extensions to bytearrayinput/output streams.
- * 
  */
 public class ByteStream {
   /**
@@ -62,16 +63,13 @@ public class ByteStream {
    * Output.
    *
    */
-  public static class Output extends NonSyncByteArrayOutputStream {
+  public static final class Output
+    extends NonSyncByteArrayOutputStream implements RandomAccessOutput {
     @Override
     public byte[] getData() {
       return buf;
     }
 
-    public int getCount() {
-      return count;
-    }
-
     public Output() {
       super();
     }
@@ -79,5 +77,30 @@ public class ByteStream {
     public Output(int size) {
       super(size);
     }
+
+    @Override
+    public void writeInt(long offset, int value) {
+      int offset2 = (int)offset;
+      getData()[offset2++] = (byte) (value >> 24);
+      getData()[offset2++] = (byte) (value >> 16);
+      getData()[offset2++] = (byte) (value >> 8);
+      getData()[offset2] = (byte) (value);
+    }
+
+    @Override
+    public void reserve(int byteCount) {
+      for (int i = 0; i < byteCount; ++i) {
+        write(0);
+      }
+    }
+  }
+
+  public static interface RandomAccessOutput {
+    public void writeInt(long offset, int value);
+    public void reserve(int byteCount);
+    public void write(int b);
+    public void write(byte b[]) throws IOException;
+    public void write(byte b[], int off, int len);
+    public int getLength();
   }
 }