http://git-wip-us.apache.org/repos/asf/drill/blob/40de8ca4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/AbstractTupleWriter.java
----------------------------------------------------------------------
diff --git 
a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/AbstractTupleWriter.java
 
b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/AbstractTupleWriter.java
new file mode 100644
index 0000000..1fd12f2
--- /dev/null
+++ 
b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/AbstractTupleWriter.java
@@ -0,0 +1,450 @@
+/*
+ * 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.drill.exec.vector.accessor.writer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.drill.exec.record.ColumnMetadata;
+import org.apache.drill.exec.record.MaterializedField;
+import org.apache.drill.exec.record.TupleMetadata;
+import org.apache.drill.exec.vector.accessor.ArrayWriter;
+import org.apache.drill.exec.vector.accessor.ColumnWriterIndex;
+import org.apache.drill.exec.vector.accessor.ObjectType;
+import org.apache.drill.exec.vector.accessor.ObjectWriter;
+import org.apache.drill.exec.vector.accessor.ScalarWriter;
+import org.apache.drill.exec.vector.accessor.TupleWriter;
+import org.apache.drill.exec.vector.accessor.impl.HierarchicalFormatter;
+
+/**
+ * Implementation for a writer for a tuple (a row or a map.) Provides access 
to each
+ * column using either a name or a numeric index.
+ * <p>
+ * A tuple maintains an internal state needed to handle dynamic column 
additions.
+ * The state identifies the amount of "catch up" needed to get the new column 
into
+ * the same state as the existing columns. The state is also handy for 
understanding
+ * the tuple lifecycle. This lifecycle works for all three cases of:
+ * <ul>
+ * <li>Top-level tuple (row).</li>
+ * <li>Nested tuple (map).</li>
+ * <li>Array of tuples (repeated map).</li>
+ * </ul>
+ *
+ * Specifically, the transitions, for batch, row and array events, are:
+ *
+ * <table border=1>
+ * <tr><th>Public API</th><th>Tuple Event</th><th>State Transition</th>
+ *     <th>Child Event</th></tr>
+ * <tr><td>(Start state)</td>
+ *     <td>&mdash;</td>
+ *     <td>IDLE</td>
+ *     <td>&mdash;</td></tr>
+ * <tr><td>startBatch()</td>
+ *     <td>startWrite()</td>
+ *     <td>IDLE &rarr; IN_WRITE</td>
+ *     <td>startWrite()</td></tr>
+ * <tr><td>start() (new row)</td>
+ *     <td>startRow()</td>
+ *     <td>IN_WRITE &rarr; IN_ROW</td>
+ *     <td>startRow()</td></tr>
+ * <tr><td>start() (without save)</td>
+ *     <td>restartRow()</td>
+ *     <td>IN_ROW &rarr; IN_ROW</td>
+ *     <td>restartRow()</td></tr>
+ * <tr><td>save() (array)</td>
+ *     <td>saveValue()</td>
+ *     <td>IN_ROW &rarr; IN_ROW</td>
+ *     <td>saveValue()</td></tr>
+ * <tr><td rowspan=2>save() (row)</td>
+ *     <td>saveValue()</td>
+ *     <td>IN_ROW &rarr; IN_ROW</td>
+ *     <td>saveValue()</td></tr>
+ * <tr><td>saveRow()</td>
+ *     <td>IN_ROW &rarr; IN_WRITE</td>
+ *     <td>saveRow()</td></tr>
+ * <tr><td rowspan=2>end batch</td>
+ *     <td>&mdash;</td>
+ *     <td>IN_ROW &rarr; IDLE</td>
+ *     <td>endWrite()</td></tr>
+ * <tr><td>&mdash;</td>
+ *     <td>IN_WRITE &rarr; IDLE</td>
+ *     <td>endWrite()</td></tr>
+ * </table>
+ *
+ * Notes:
+ * <ul>
+ * <li>For the top-level tuple, a special case occurs with ending a batch. (The
+ *     method for doing so differs depending on implementation.) If a row is 
active,
+ *     then that row's values are discarded. Then, the batch is ended.</li>
+ * </ul>
+ */
+
+public abstract class AbstractTupleWriter implements TupleWriter, WriterEvents 
{
+
+  /**
+   * Generic object wrapper for the tuple writer.
+   */
+
+  public static class TupleObjectWriter extends AbstractObjectWriter {
+
+    private AbstractTupleWriter tupleWriter;
+
+    public TupleObjectWriter(ColumnMetadata schema, AbstractTupleWriter 
tupleWriter) {
+      super(schema);
+      this.tupleWriter = tupleWriter;
+    }
+
+    @Override
+    public ObjectType type() { return ObjectType.TUPLE; }
+
+    @Override
+    public void set(Object value) { tupleWriter.setObject(value); }
+
+    @Override
+    public TupleWriter tuple() { return tupleWriter; }
+
+    @Override
+    public WriterEvents events() { return tupleWriter; }
+
+    @Override
+    public void bindListener(TupleWriterListener listener) {
+      tupleWriter.bindListener(listener);
+    }
+
+    @Override
+    public void dump(HierarchicalFormatter format) {
+      format
+        .startObject(this)
+        .attribute("tupleWriter");
+      tupleWriter.dump(format);
+      format.endObject();
+    }
+  }
+
+  /**
+   * Tracks the write state of the tuple to allow applying the correct
+   * operations to newly-added columns to synchronize them with the rest
+   * of the tuple.
+   */
+
+  public enum State {
+    /**
+     * No write is in progress. Nothing need be done to newly-added
+     * writers.
+     */
+    IDLE,
+
+    /**
+     * <tt>startWrite()</tt> has been called to start a write operation
+     * (start a batch), but <tt>startValue()</tt> has not yet been called
+     * to start a row (or value within an array). <tt>startWrite()</tt> must
+     * be called on newly added columns.
+     */
+
+    IN_WRITE,
+
+    /**
+     * Both <tt>startWrite()</tt> and <tt>startValue()</tt> has been called on
+     * the tuple to prepare for writing values, and both must be called on
+     * newly-added vectors.
+     */
+
+    IN_ROW
+  }
+
+  protected final TupleMetadata schema;
+  protected final List<AbstractObjectWriter> writers;
+  protected ColumnWriterIndex vectorIndex;
+  protected ColumnWriterIndex childIndex;
+  protected TupleWriterListener listener;
+  protected State state = State.IDLE;
+
+  protected AbstractTupleWriter(TupleMetadata schema, 
List<AbstractObjectWriter> writers) {
+    this.schema = schema;
+    this.writers = writers;
+  }
+
+  protected AbstractTupleWriter(TupleMetadata schema) {
+    this(schema, new ArrayList<AbstractObjectWriter>());
+  }
+
+  protected void bindIndex(ColumnWriterIndex index, ColumnWriterIndex 
childIndex) {
+    vectorIndex = index;
+    this.childIndex = childIndex;
+
+    for (int i = 0; i < writers.size(); i++) {
+      writers.get(i).events().bindIndex(childIndex);
+    }
+  }
+
+  @Override
+  public void bindIndex(ColumnWriterIndex index) {
+    bindIndex(index, index);
+  }
+
+  @Override
+  public ColumnWriterIndex writerIndex() { return vectorIndex; }
+
+  /**
+   * Add a column writer to an existing tuple writer. Used for implementations
+   * that support "live" schema evolution: column discovery while writing.
+   * The corresponding metadata must already have been added to the schema.
+   *
+   * @param colWriter the column writer to add
+   */
+
+  public int addColumnWriter(AbstractObjectWriter colWriter) {
+    assert writers.size() == schema.size();
+    int colIndex = schema.addColumn(colWriter.schema());
+    writers.add(colWriter);
+    colWriter.events().bindIndex(childIndex);
+    if (state != State.IDLE) {
+      colWriter.events().startWrite();
+      if (state == State.IN_ROW) {
+        colWriter.events().startRow();
+      }
+    }
+    return colIndex;
+  }
+
+  @Override
+  public int addColumn(ColumnMetadata column) {
+    if (listener == null) {
+      throw new UnsupportedOperationException("addColumn");
+    }
+    AbstractObjectWriter colWriter = (AbstractObjectWriter) 
listener.addColumn(this, column);
+    return addColumnWriter(colWriter);
+  }
+
+  @Override
+  public int addColumn(MaterializedField field) {
+    if (listener == null) {
+      throw new UnsupportedOperationException("addColumn");
+    }
+    AbstractObjectWriter colWriter = (AbstractObjectWriter) 
listener.addColumn(this, field);
+    return addColumnWriter(colWriter);
+  }
+
+  @Override
+  public TupleMetadata schema() { return schema; }
+
+  @Override
+  public int size() { return schema().size(); }
+
+  @Override
+  public void startWrite() {
+    assert state == State.IDLE;
+    state = State.IN_WRITE;
+    for (int i = 0; i < writers.size();  i++) {
+      writers.get(i).events().startWrite();
+    }
+  }
+
+  @Override
+  public void startRow() {
+    // Must be in a write. Can start a row only once.
+    // To restart, call restartRow() instead.
+
+    assert state == State.IN_WRITE;
+    state = State.IN_ROW;
+    for (int i = 0; i < writers.size();  i++) {
+      writers.get(i).events().startRow();
+    }
+  }
+
+  @Override
+  public void endArrayValue() {
+    assert state == State.IN_ROW;
+    for (int i = 0; i < writers.size();  i++) {
+      writers.get(i).events().endArrayValue();
+    }
+  }
+
+  @Override
+  public void restartRow() {
+
+    // Rewind is normally called only when a value is active: it resets
+    // pointers to allow rewriting the value. However, if this tuple
+    // is nested in an array, then the array entry could have been
+    // saved (state here is IN_WRITE), but the row as a whole has
+    // not been saved. Thus, we must also allow a rewind() while in
+    // the IN_WRITE state to set the pointers back to the start of
+    // the current row.
+
+    assert state == State.IN_ROW;
+    for (int i = 0; i < writers.size();  i++) {
+      writers.get(i).events().restartRow();
+    }
+  }
+
+  @Override
+  public void saveRow() {
+    assert state == State.IN_ROW;
+    for (int i = 0; i < writers.size();  i++) {
+      writers.get(i).events().saveRow();
+    }
+    state = State.IN_WRITE;
+  }
+
+  @Override
+  public void preRollover() {
+
+    // Rollover can only happen while a row is in progress.
+
+    assert state == State.IN_ROW;
+    for (int i = 0; i < writers.size();  i++) {
+      writers.get(i).events().preRollover();
+    }
+  }
+
+  @Override
+  public void postRollover() {
+
+    // Rollover can only happen while a row is in progress.
+
+    assert state == State.IN_ROW;
+    for (int i = 0; i < writers.size();  i++) {
+      writers.get(i).events().postRollover();
+    }
+  }
+
+  @Override
+  public void endWrite() {
+    assert state != State.IDLE;
+    for (int i = 0; i < writers.size();  i++) {
+      writers.get(i).events().endWrite();
+    }
+    state = State.IDLE;
+  }
+
+  @Override
+  public ObjectWriter column(int colIndex) {
+    return writers.get(colIndex);
+  }
+
+  @Override
+  public ObjectWriter column(String colName) {
+    int index = schema.index(colName);
+    if (index == -1) {
+      throw new UndefinedColumnException(colName);
+    }
+    return writers.get(index);
+  }
+
+  @Override
+  public void set(int colIndex, Object value) {
+    ObjectWriter colWriter = column(colIndex);
+    switch (colWriter.type()) {
+    case ARRAY:
+      colWriter.array().setObject(value);
+      break;
+    case SCALAR:
+      colWriter.scalar().setObject(value);
+      break;
+    case TUPLE:
+      colWriter.tuple().setObject(value);
+      break;
+    default:
+      throw new IllegalStateException("Unexpected object type: " + 
colWriter.type());
+    }
+  }
+
+  @Override
+  public void setTuple(Object ...values) {
+    setObject(values);
+  }
+
+  @Override
+  public void setObject(Object value) {
+    Object values[] = (Object[]) value;
+    if (values.length != schema.size()) {
+      throw new IllegalArgumentException(
+          "Map has " + schema.size() +
+          " columns, but value array has " +
+          values.length + " values.");
+    }
+    for (int i = 0; i < values.length; i++) {
+      set(i, values[i]);
+    }
+  }
+
+  @Override
+  public ScalarWriter scalar(int colIndex) {
+    return column(colIndex).scalar();
+  }
+
+  @Override
+  public ScalarWriter scalar(String colName) {
+    return column(colName).scalar();
+  }
+
+  @Override
+  public TupleWriter tuple(int colIndex) {
+    return column(colIndex).tuple();
+  }
+
+  @Override
+  public TupleWriter tuple(String colName) {
+    return column(colName).tuple();
+  }
+
+  @Override
+  public ArrayWriter array(int colIndex) {
+    return column(colIndex).array();
+  }
+
+  @Override
+  public ArrayWriter array(String colName) {
+    return column(colName).array();
+  }
+
+  @Override
+  public ObjectType type(int colIndex) {
+    return column(colIndex).type();
+  }
+
+  @Override
+  public ObjectType type(String colName) {
+    return column(colName).type();
+  }
+
+  @Override
+  public int lastWriteIndex() {
+    return vectorIndex.vectorIndex();
+  }
+
+  @Override
+  public void bindListener(TupleWriterListener listener) {
+    this.listener = listener;
+  }
+
+  public void dump(HierarchicalFormatter format) {
+    format
+      .startObject(this)
+      .attribute("vectorIndex", vectorIndex)
+      .attribute("state", state)
+      .attributeArray("writers");
+    for (int i = 0; i < writers.size(); i++) {
+      format.element(i);
+      writers.get(i).dump(format);
+    }
+    format
+      .endArray()
+      .endObject();
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/40de8ca4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/BaseScalarWriter.java
----------------------------------------------------------------------
diff --git 
a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/BaseScalarWriter.java
 
b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/BaseScalarWriter.java
new file mode 100644
index 0000000..4793277
--- /dev/null
+++ 
b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/BaseScalarWriter.java
@@ -0,0 +1,272 @@
+/*
+ * 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.drill.exec.vector.accessor.writer;
+
+import java.math.BigDecimal;
+
+import org.apache.drill.exec.vector.accessor.ColumnWriterIndex;
+import org.apache.drill.exec.vector.accessor.impl.HierarchicalFormatter;
+import org.joda.time.Period;
+
+import io.netty.buffer.DrillBuf;
+
+/**
+ * Column writer implementation that acts as the basis for the
+ * generated, vector-specific implementations. All set methods
+ * throw an exception; subclasses simply override the supported
+ * method(s).
+ * <p>
+ * The only tricky part to this class is understanding the
+ * state of the write indexes as the write proceeds. There are
+ * two pointers to consider:
+ * <ul>
+ * <li>lastWriteIndex: The position in the vector at which the
+ * client last asked us to write data. This index is maintained
+ * in this class because it depends only on the actions of this
+ * class.</li>
+ * <li>vectorIndex: The position in the vector at which we will
+ * write if the client chooses to write a value at this time.
+ * The vector index is shared by all columns at the same repeat
+ * level. It is incremented as the client steps through the write
+ * and is observed in this class each time a write occurs.</i>
+ * </ul>
+ * A repeat level is defined as any of the following:
+ * <ul>
+ * <li>The set of top-level scalar columns, or those within a
+ * top-level, non-repeated map, or nested to any depth within
+ * non-repeated maps rooted at the top level.</li>
+ * <li>The values for a single scalar array.</li>
+ * <li>The set of scalar columns within a repeated map, or
+ * nested within non-repeated maps within a repeated map.</li>
+ * </ul>
+ * Items at a repeat level index together and share a vector
+ * index. However, the columns within a repeat level
+ * <i>do not</i> share a last write index: some can lag further
+ * behind than others.
+ * <p>
+ * Let's illustrate the states. Let's focus on one column and
+ * illustrate the three states that can occur during write:
+ * <ul>
+ * <li><b>Behind</b>: the last write index is more than one position behind
+ * the vector index. Zero-filling will be needed to catch up to
+ * the vector index.</li>
+ * <li><b>Written</b>: the last write index is the same as the vector
+ * index because the client wrote data at this position (and previous
+ * values were back-filled with nulls, empties or zeros.)</li>
+ * <li><b>Unwritten</b>: the last write index is one behind the vector
+ * index. This occurs when the column was written, then the client
+ * moved to the next row or array position.</li>
+ * <li><b>Restarted</b>: The current row is abandoned (perhaps filtered
+ * out) and is to be rewritten. The last write position moves
+ * back one position. Note that, the Restarted state is
+ * indistinguishable from the unwritten state: the only real
+ * difference is that the current slot (pointed to by the
+ * vector index) contains the previous written value that must
+ * be overwritten or back-filled. But, this is fine, because we
+ * assume that unwritten values are garbage anyway.</li>
+ * </ul>
+ * To illustrate:<pre><code>
+ *      Behind      Written    Unwritten    Restarted
+ *       |X|          |X|         |X|          |X|
+ *   lw >|X|          |X|         |X|          |X|
+ *       | |          |0|         |0|     lw > |0|
+ *    v >| |  lw, v > |X|    lw > |X|      v > |X|
+ *                            v > | |
+ * </code></pre>
+ * The illustrated state transitions are:
+ * <ul>
+ * <li>Suppose the state starts in Behind.<ul>
+ *   <li>If the client writes a value, then the empty slot is
+ *       back-filled and the state moves to Written.</li>
+ *   <li>If the client does not write a value, the state stays
+ *       at Behind, and the gap of unfilled values grows.</li></ul></li>
+ * <li>When in the Written state:<ul>
+ *   <li>If the client saves the current row or array position,
+ *       the vector index increments and we move to the Unwritten
+ *       state.</li>
+ *   <li>If the client abandons the row, the last write position
+ *       moves back one to recreate the unwritten state. We've
+ *       shown this state separately above just to illustrate
+ *       the two transitions from Written.</li></ul></li>
+ * <li>When in the Unwritten (or Restarted) states:<ul>
+ *   <li>If the client writes a value, then the writer moves back to the
+ *       Written state.</li>
+ *   <li>If the client skips the value, then the vector index increments
+ *       again, leaving a gap, and the writer moves to the
+ *       Behind state.</li></ul>
+ * </ul>
+ * <p>
+ * We've already noted that the Restarted state is identical to
+ * the Unwritten state (and was discussed just to make the flow a bit
+ * clearer.) The astute reader will have noticed that the Behind state is
+ * the same as the Unwritten state if we define the combined state as
+ * when the last write position is behind the vector index.
+ * <p>
+ * Further, if
+ * one simply treats the gap between last write and the vector indexes
+ * as the amount (which may be zero) to back-fill, then there is just
+ * one state. This is, in fact, how the code works: it always writes
+ * to the vector index (and can do so multiple times for a single row),
+ * back-filling as necessary.
+ * <p>
+ * The states, then, are more for our use in understanding the algorithm.
+ * They are also very useful when working through the logic of performing
+ * a roll-over when a vector overflows.
+ */
+
+public abstract class BaseScalarWriter extends AbstractScalarWriter {
+
+  public static final int MIN_BUFFER_SIZE = 256;
+
+  /**
+   * Indicates the position in the vector to write. Set via an object so that
+   * all writers (within the same subtree) can agree on the write position.
+   * For example, all top-level, simple columns see the same row index.
+   * All columns within a repeated map see the same (inner) index, etc.
+   */
+
+  protected ColumnWriterIndex vectorIndex;
+
+  /**
+   * Listener invoked if the vector overflows. If not provided, then the writer
+   * does not support vector overflow.
+   */
+
+  protected ColumnWriterListener listener;
+
+  protected DrillBuf drillBuf;
+
+  /**
+   * Capacity, in values, of the currently allocated buffer that backs
+   * the vector. Updated each time the buffer changes. The capacity is in
+   * values (rather than bytes) to streamline the per-write logic.
+   */
+
+  protected int capacity;
+
+  @Override
+  public void bindIndex(ColumnWriterIndex vectorIndex) {
+    this.vectorIndex = vectorIndex;
+  }
+
+  @Override
+  public ColumnWriterIndex writerIndex() { return vectorIndex; }
+
+  @Override
+  public void bindListener(ColumnWriterListener listener) {
+    this.listener = listener;
+  }
+
+  /**
+   * All change of buffer comes through this function to allow capturing
+   * the buffer address and capacity. Only two ways to set the buffer:
+   * by binding to a vector in bindVector(), or by resizing the vector
+   * in writeIndex().
+   */
+
+  protected abstract void setBuffer();
+
+  protected void realloc(int size) {
+    vector().reallocRaw(size);
+    setBuffer();
+  }
+
+  /**
+   * The vector is about to grow. Give the listener a chance to
+   * veto the growth and opt for overflow instead.
+   *
+   * @param delta the new amount of memory to allocate
+   * @return true if the vector can be grown, false if an
+   * overflow should be triggered
+   */
+
+  protected boolean canExpand(int delta) {
+    if (listener == null) {
+      return true;
+    } else {
+      return listener.canExpand(this, delta);
+    }
+  }
+
+  /**
+   * Handle vector overflow. If this is an array, then there is a slim chance
+   * we may need to grow the vector immediately after overflow. Since a double
+   * overflow is not allowed, this recursive call won't continue forever.
+   */
+
+  protected void overflowed() {
+    if (listener == null) {
+      throw new IndexOutOfBoundsException("Overflow not supported");
+    } else {
+      listener.overflowed(this);
+    }
+  }
+
+  public abstract void skipNulls();
+
+  @Override
+  public void setNull() {
+    throw new UnsupportedOperationException("Vector is not nullable");
+  }
+
+  @Override
+  public void setInt(int value) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public void setLong(long value) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public void setDouble(double value) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public void setString(String value) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public void setBytes(byte[] value, int len) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public void setDecimal(BigDecimal value) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public void setPeriod(Period value) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public void dump(HierarchicalFormatter format) {
+    format.extend();
+    super.dump(format);
+    format
+      .attribute("vectorIndex", vectorIndex)
+      .attributeIdentity("listener", listener)
+      .attribute("capacity", capacity)
+      .endObject();
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/40de8ca4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/BaseVarWidthWriter.java
----------------------------------------------------------------------
diff --git 
a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/BaseVarWidthWriter.java
 
b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/BaseVarWidthWriter.java
new file mode 100644
index 0000000..e54625e
--- /dev/null
+++ 
b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/BaseVarWidthWriter.java
@@ -0,0 +1,157 @@
+/*
+ * 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.drill.exec.vector.accessor.writer;
+
+import org.apache.drill.exec.memory.BaseAllocator;
+import org.apache.drill.exec.vector.UInt4Vector;
+import org.apache.drill.exec.vector.ValueVector;
+import org.apache.drill.exec.vector.accessor.ColumnWriterIndex;
+import org.apache.drill.exec.vector.accessor.impl.HierarchicalFormatter;
+
+/**
+ * Base class for variable-width (VarChar, VarBinary, etc.) writers.
+ * Handles the additional complexity that such writers work with
+ * both an offset vector and a data vector. The offset vector is
+ * written using a specialized offset vector writer. The last write
+ * index is defined as the the last write position in the offset
+ * vector; not the last write position in the variable-width
+ * vector.
+ * <p>
+ * Most and value events are forwarded to the offset vector.
+ */
+
+public abstract class BaseVarWidthWriter extends BaseScalarWriter {
+  protected final OffsetVectorWriter offsetsWriter;
+
+  public BaseVarWidthWriter(UInt4Vector offsetVector) {
+    offsetsWriter = new OffsetVectorWriter(offsetVector);
+  }
+
+  @Override
+  public void bindIndex(final ColumnWriterIndex index) {
+    offsetsWriter.bindIndex(index);
+    super.bindIndex(index);
+  }
+
+  @Override
+  public void startWrite() {
+    setBuffer();
+    offsetsWriter.startWrite();
+  }
+
+  @Override
+  public void startRow() { offsetsWriter.startRow(); }
+
+  protected final int writeIndex(final int width) {
+
+    // This is performance critical code; every operation counts.
+    // Please be thoughtful when changing the code.
+
+    int writeOffset = offsetsWriter.nextOffset();
+    if (writeOffset + width < capacity) {
+      return writeOffset;
+    }
+    resize(writeOffset + width);
+    return offsetsWriter.nextOffset();
+  }
+
+  @Override
+  protected final void setBuffer() {
+    drillBuf = vector().getBuffer();
+    capacity = drillBuf.capacity();
+  }
+
+  private void resize(int size) {
+    if (size <= capacity) {
+      return;
+    }
+
+    // Since some vectors start off as 0 length, set a
+    // minimum size to avoid silly thrashing on early rows.
+
+    if (size < MIN_BUFFER_SIZE) {
+      size = MIN_BUFFER_SIZE;
+    }
+
+    // Grow the vector -- or overflow if the growth would make the batch
+    // consume too much memory. The idea is that we grow vectors as they
+    // fit the available memory budget, then we fill those vectors until
+    // one of them needs more space. At that point we trigger overflow to
+    // a new set of vectors. Internal fragmentation will result, but this
+    // approach (along with proper initial vector sizing), minimizes that
+    // fragmentation.
+
+    size = BaseAllocator.nextPowerOfTwo(size);
+
+    // Two cases: grow this vector or allocate a new one.
+
+    if (size <= ValueVector.MAX_BUFFER_SIZE && canExpand(size - capacity)) {
+
+      // Optimized form of reAlloc() which does not zero memory, does not do
+      // bounds checks (since they were already done above). The write index
+      // and offset remain unchanged.
+
+      realloc(size);
+    } else {
+
+      // Allocate a new vector, or throw an exception if overflow is not
+      // supported. If overflow is supported, the callback will call
+      // endWrite(), which will set the final writer index for the current
+      // vector. Then, bindVector() will be called to provide the new vector.
+      // The write index changes with the new vector.
+
+      overflowed();
+    }
+  }
+
+  @Override
+  public void skipNulls() { }
+
+  @Override
+  public void restartRow() { offsetsWriter.restartRow(); }
+
+  @Override
+  public int lastWriteIndex() { return offsetsWriter.lastWriteIndex(); }
+
+  @Override
+  public final void preRollover() {
+    vector().getBuffer().writerIndex(offsetsWriter.rowStartOffset());
+    offsetsWriter.preRollover();
+  }
+
+  @Override
+  public void postRollover() {
+    setBuffer();
+    offsetsWriter.postRollover();
+  }
+
+  @Override
+  public final void endWrite() {
+    vector().getBuffer().writerIndex(offsetsWriter.nextOffset());
+    offsetsWriter.endWrite();
+  }
+
+  @Override
+  public void dump(HierarchicalFormatter format) {
+    format.extend();
+    super.dump(format);
+    format.attribute("offsetsWriter");
+    offsetsWriter.dump(format);
+    format.endObject();
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/40de8ca4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/ColumnWriterFactory.java
----------------------------------------------------------------------
diff --git 
a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/ColumnWriterFactory.java
 
b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/ColumnWriterFactory.java
new file mode 100644
index 0000000..5a1187a
--- /dev/null
+++ 
b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/ColumnWriterFactory.java
@@ -0,0 +1,196 @@
+/*
+ * 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.drill.exec.vector.accessor.writer;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.drill.common.types.TypeProtos.MajorType;
+import org.apache.drill.common.types.TypeProtos.MinorType;
+import org.apache.drill.exec.record.ColumnMetadata;
+import org.apache.drill.exec.vector.NullableVector;
+import org.apache.drill.exec.vector.UInt4Vector;
+import org.apache.drill.exec.vector.ValueVector;
+import org.apache.drill.exec.vector.accessor.ColumnAccessors;
+import 
org.apache.drill.exec.vector.accessor.writer.AbstractArrayWriter.ArrayObjectWriter;
+import 
org.apache.drill.exec.vector.accessor.writer.AbstractScalarWriter.ScalarObjectWriter;
+import 
org.apache.drill.exec.vector.accessor.writer.AbstractTupleWriter.TupleObjectWriter;
+import org.apache.drill.exec.vector.accessor.writer.MapWriter.ArrayMapWriter;
+import 
org.apache.drill.exec.vector.accessor.writer.MapWriter.DummyArrayMapWriter;
+import org.apache.drill.exec.vector.accessor.writer.MapWriter.DummyMapWriter;
+import org.apache.drill.exec.vector.accessor.writer.MapWriter.SingleMapWriter;
+import org.apache.drill.exec.vector.accessor.writer.dummy.DummyArrayWriter;
+import org.apache.drill.exec.vector.accessor.writer.dummy.DummyScalarWriter;
+import org.apache.drill.exec.vector.complex.AbstractMapVector;
+import org.apache.drill.exec.vector.complex.MapVector;
+import org.apache.drill.exec.vector.complex.RepeatedMapVector;
+import org.apache.drill.exec.vector.complex.RepeatedValueVector;
+
+/**
+ * Gather generated writer classes into a set of class tables to allow rapid
+ * run-time creation of writers. Builds the writer and its object writer
+ * wrapper which binds the vector to the writer.
+ */
+
+@SuppressWarnings("unchecked")
+public class ColumnWriterFactory {
+
+  private static final int typeCount = MinorType.values().length;
+  private static final Class<? extends BaseScalarWriter> requiredWriters[] = 
new Class[typeCount];
+
+  static {
+    ColumnAccessors.defineRequiredWriters(requiredWriters);
+  }
+
+  public static AbstractObjectWriter buildColumnWriter(ColumnMetadata schema, 
ValueVector vector) {
+    if (vector == null) {
+      return buildDummyColumnWriter(schema);
+    }
+
+    // Build a writer for a materialized column.
+
+    assert schema.type() == vector.getField().getType().getMinorType();
+    assert schema.mode() == vector.getField().getType().getMode();
+
+    switch (schema.type()) {
+    case GENERIC_OBJECT:
+    case LATE:
+    case NULL:
+    case LIST:
+    case MAP:
+      throw new UnsupportedOperationException(schema.type().toString());
+    default:
+      switch (schema.mode()) {
+      case OPTIONAL:
+        NullableVector nullableVector = (NullableVector) vector;
+        return NullableScalarWriter.build(schema, nullableVector,
+                newWriter(nullableVector.getValuesVector()));
+      case REQUIRED:
+        return new ScalarObjectWriter(schema, newWriter(vector));
+      case REPEATED:
+        RepeatedValueVector repeatedVector = (RepeatedValueVector) vector;
+        return ScalarArrayWriter.build(schema, repeatedVector,
+                newWriter(repeatedVector.getDataVector()));
+      default:
+        throw new UnsupportedOperationException(schema.mode().toString());
+      }
+    }
+  }
+
+  /**
+   * Build a writer for a non-projected column.
+   * @param schema schema of the column
+   * @return a "dummy" writer for the column
+   */
+
+  public static AbstractObjectWriter buildDummyColumnWriter(ColumnMetadata 
schema) {
+    switch (schema.type()) {
+    case GENERIC_OBJECT:
+    case LATE:
+    case NULL:
+    case LIST:
+    case MAP:
+      throw new UnsupportedOperationException(schema.type().toString());
+    default:
+      ScalarObjectWriter scalarWriter = new ScalarObjectWriter(schema,
+          new DummyScalarWriter());
+      switch (schema.mode()) {
+      case OPTIONAL:
+      case REQUIRED:
+        return scalarWriter;
+      case REPEATED:
+        return new ArrayObjectWriter(schema,
+            new DummyArrayWriter(
+              scalarWriter));
+      default:
+        throw new UnsupportedOperationException(schema.mode().toString());
+      }
+    }
+  }
+
+  public static TupleObjectWriter buildMap(ColumnMetadata schema, MapVector 
vector,
+                                        List<AbstractObjectWriter> writers) {
+    MapWriter mapWriter;
+    if (schema.isProjected()) {
+      mapWriter = new SingleMapWriter(schema, vector, writers);
+    } else {
+      mapWriter = new DummyMapWriter(schema, writers);
+    }
+    return new TupleObjectWriter(schema, mapWriter);
+  }
+
+  public static ArrayObjectWriter buildMapArray(ColumnMetadata schema,
+                                        UInt4Vector offsetVector,
+                                        List<AbstractObjectWriter> writers) {
+    MapWriter mapWriter;
+    if (schema.isProjected()) {
+      mapWriter = new ArrayMapWriter(schema, writers);
+    } else {
+      mapWriter = new DummyArrayMapWriter(schema, writers);
+    }
+    TupleObjectWriter mapArray = new TupleObjectWriter(schema, mapWriter);
+    AbstractArrayWriter arrayWriter;
+    if (schema.isProjected()) {
+      arrayWriter = new ObjectArrayWriter(
+          offsetVector,
+          mapArray);
+    } else  {
+      arrayWriter = new DummyArrayWriter(mapArray);
+    }
+    return new ArrayObjectWriter(schema, arrayWriter);
+  }
+
+  public static AbstractObjectWriter buildMapWriter(ColumnMetadata schema,
+      AbstractMapVector vector,
+      List<AbstractObjectWriter> writers) {
+    assert (vector != null) == schema.isProjected();
+    if (! schema.isArray()) {
+      return buildMap(schema, (MapVector) vector, writers);
+    } else if (vector == null) {
+      return buildMapArray(schema,
+          null, writers);
+    } else {
+      return buildMapArray(schema,
+          ((RepeatedMapVector) vector).getOffsetVector(),
+          writers);
+    }
+  }
+
+  public static AbstractObjectWriter buildMapWriter(ColumnMetadata schema, 
AbstractMapVector vector) {
+    assert schema.mapSchema().size() == 0;
+    return buildMapWriter(schema, vector, new 
ArrayList<AbstractObjectWriter>());
+  }
+
+  public static BaseScalarWriter newWriter(ValueVector vector) {
+    MajorType major = vector.getField().getType();
+    MinorType type = major.getMinorType();
+    try {
+      Class<? extends BaseScalarWriter> accessorClass = 
requiredWriters[type.ordinal()];
+      if (accessorClass == null) {
+        throw new UnsupportedOperationException(type.toString());
+      }
+      Constructor<? extends BaseScalarWriter> ctor = 
accessorClass.getConstructor(ValueVector.class);
+      return ctor.newInstance(vector);
+    } catch (InstantiationException | IllegalAccessException | 
NoSuchMethodException |
+             SecurityException | IllegalArgumentException | 
InvocationTargetException e) {
+      throw new IllegalStateException(e);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/40de8ca4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/MapWriter.java
----------------------------------------------------------------------
diff --git 
a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/MapWriter.java
 
b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/MapWriter.java
new file mode 100644
index 0000000..8aec301
--- /dev/null
+++ 
b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/MapWriter.java
@@ -0,0 +1,155 @@
+/*
+ * 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.drill.exec.vector.accessor.writer;
+
+import java.util.List;
+
+import org.apache.drill.exec.record.ColumnMetadata;
+import org.apache.drill.exec.vector.accessor.ColumnWriterIndex;
+import org.apache.drill.exec.vector.complex.MapVector;
+
+/**
+ * Writer for a Drill Map type. Maps are actually tuples, just like rows.
+ */
+
+public abstract class MapWriter extends AbstractTupleWriter {
+
+  /**
+   * Wrap the outer index to avoid incrementing the array index
+   * on the call to <tt>nextElement().</tt> For maps, the increment
+   * is done at the map level, not the column level.
+   */
+
+  private static class MemberWriterIndex implements ColumnWriterIndex {
+    private ColumnWriterIndex baseIndex;
+
+    private MemberWriterIndex(ColumnWriterIndex baseIndex) {
+      this.baseIndex = baseIndex;
+    }
+
+    @Override public int rowStartIndex() { return baseIndex.rowStartIndex(); }
+    @Override public int vectorIndex() { return baseIndex.vectorIndex(); }
+    @Override public void nextElement() { }
+    @Override public void rollover() { }
+    @Override public ColumnWriterIndex outerIndex() {
+      return baseIndex.outerIndex();
+    }
+
+    @Override
+    public String toString() {
+      return new StringBuilder()
+        .append("[")
+        .append(getClass().getSimpleName())
+        .append(" baseIndex = ")
+        .append(baseIndex.toString())
+        .append("]")
+        .toString();
+    }
+  }
+
+  /**
+   * Writer for a single (non-array) map. Clients don't really "write" maps;
+   * rather, this writer is a holder for the columns within the map, and those
+   * columns are what is written.
+   */
+
+  protected static class SingleMapWriter extends MapWriter {
+    private final MapVector mapVector;
+
+    protected SingleMapWriter(ColumnMetadata schema, MapVector vector, 
List<AbstractObjectWriter> writers) {
+      super(schema, writers);
+      mapVector = vector;
+    }
+
+    @Override
+    public void endWrite() {
+      super.endWrite();
+
+      // Special form of set value count: used only for
+      // this class to avoid setting the value count of children.
+      // Setting these counts was already done. Doing it again
+      // will corrupt nullable vectors because the writers don't
+      // set the "lastSet" field of nullable vector accessors,
+      // and the initial value of -1 will cause all values to
+      // be overwritten.
+      //
+      // Note that the map vector can be null if there is no actual
+      // map vector represented by this writer.
+
+      if (mapVector != null) {
+        mapVector.setMapValueCount(vectorIndex.vectorIndex());
+      }
+    }
+  }
+
+  /**
+   * Writer for a an array of maps. A single array index coordinates writes
+   * to the constituent member vectors so that, say, the values for (row 10,
+   * element 5) all occur to the same position in the columns within the map.
+   * Since the map is an array, it has an associated offset vector, which the
+   * parent array writer is responsible for maintaining.
+   */
+
+  protected static class ArrayMapWriter extends MapWriter {
+
+    protected ArrayMapWriter(ColumnMetadata schema, List<AbstractObjectWriter> 
writers) {
+      super(schema, writers);
+    }
+
+    @Override
+    public void bindIndex(ColumnWriterIndex index) {
+
+      // This is a repeated map, so the provided index is an array element
+      // index. Convert this to an index that will not increment the element
+      // index on each write so that a map with three members, say, won't
+      // increment the index for each member. Rather, the index must be
+      // incremented at the array level.
+
+      bindIndex(index, new MemberWriterIndex(index));
+    }
+
+    // In endWrite(), do not call setValueCount on the map vector.
+    // Doing so will zero-fill the composite vectors because
+    // the internal map state does not track the writer state.
+    // Instead, the code in this structure has set the value
+    // count for each composite vector individually.
+  }
+
+  protected static class DummyMapWriter extends MapWriter {
+
+    protected DummyMapWriter(ColumnMetadata schema,
+        List<AbstractObjectWriter> writers) {
+      super(schema, writers);
+    }
+  }
+
+  protected static class DummyArrayMapWriter extends MapWriter {
+
+    protected DummyArrayMapWriter(ColumnMetadata schema,
+        List<AbstractObjectWriter> writers) {
+      super(schema, writers);
+    }
+  }
+
+  protected final ColumnMetadata mapColumnSchema;
+
+  protected MapWriter(ColumnMetadata schema, List<AbstractObjectWriter> 
writers) {
+    super(schema.mapSchema(), writers);
+    mapColumnSchema = schema;
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/40de8ca4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/NullableScalarWriter.java
----------------------------------------------------------------------
diff --git 
a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/NullableScalarWriter.java
 
b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/NullableScalarWriter.java
new file mode 100644
index 0000000..6da2b50
--- /dev/null
+++ 
b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/NullableScalarWriter.java
@@ -0,0 +1,190 @@
+/*
+ * 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.drill.exec.vector.accessor.writer;
+
+import java.math.BigDecimal;
+
+import org.apache.drill.exec.record.ColumnMetadata;
+import org.apache.drill.exec.vector.BaseDataValueVector;
+import org.apache.drill.exec.vector.NullableVector;
+import org.apache.drill.exec.vector.accessor.ColumnAccessors.UInt1ColumnWriter;
+import org.apache.drill.exec.vector.accessor.impl.HierarchicalFormatter;
+import org.apache.drill.exec.vector.accessor.ColumnWriterIndex;
+import org.apache.drill.exec.vector.accessor.ValueType;
+import org.joda.time.Period;
+
+public class NullableScalarWriter extends AbstractScalarWriter {
+
+  private final UInt1ColumnWriter isSetWriter;
+  private final BaseScalarWriter baseWriter;
+
+  public NullableScalarWriter(NullableVector nullableVector, BaseScalarWriter 
baseWriter) {
+    isSetWriter = new UInt1ColumnWriter(nullableVector.getBitsVector());
+    this.baseWriter = baseWriter;
+  }
+
+  public static ScalarObjectWriter build(ColumnMetadata schema,
+      NullableVector nullableVector, BaseScalarWriter baseWriter) {
+    return new ScalarObjectWriter(schema,
+        new NullableScalarWriter(nullableVector, baseWriter));
+  }
+
+  public BaseScalarWriter bitsWriter() { return isSetWriter; }
+  public BaseScalarWriter baseWriter() { return baseWriter; }
+
+  @Override
+  public BaseDataValueVector vector() {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public void bindIndex(ColumnWriterIndex index) {
+    isSetWriter.bindIndex(index);
+    baseWriter.bindIndex(index);
+  }
+
+  @Override
+  public ColumnWriterIndex writerIndex() { return baseWriter.writerIndex(); }
+
+  @Override
+  public ValueType valueType() {
+    return baseWriter.valueType();
+  }
+
+  @Override
+  public void restartRow() {
+    isSetWriter.restartRow();
+    baseWriter.restartRow();
+  }
+
+  @Override
+  public void setNull() {
+    isSetWriter.setInt(0);
+    baseWriter.skipNulls();
+  }
+
+  @Override
+  public void setInt(int value) {
+    baseWriter.setInt(value);
+    isSetWriter.setInt(1);
+  }
+
+  @Override
+  public void setLong(long value) {
+    baseWriter.setLong(value);
+    isSetWriter.setInt(1);
+  }
+
+  @Override
+  public void setDouble(double value) {
+    baseWriter.setDouble(value);
+    isSetWriter.setInt(1);
+  }
+
+  @Override
+  public void setString(String value) {
+    // String may overflow. Set bits after
+    // overflow since bits vector does not have
+    // overflow handling separate from the nullable
+    // vector as a whole.
+
+    baseWriter.setString(value);
+    isSetWriter.setInt(1);
+  }
+
+  @Override
+  public void setBytes(byte[] value, int len) {
+    baseWriter.setBytes(value, len);
+    isSetWriter.setInt(1);
+  }
+
+  @Override
+  public void setDecimal(BigDecimal value) {
+    baseWriter.setDecimal(value);
+    isSetWriter.setInt(1);
+  }
+
+  @Override
+  public void setPeriod(Period value) {
+    baseWriter.setPeriod(value);
+    isSetWriter.setInt(1);
+  }
+
+  @Override
+  public void preRollover() {
+    isSetWriter.preRollover();
+    baseWriter.preRollover();
+  }
+
+  @Override
+  public void postRollover() {
+    isSetWriter.postRollover();
+    baseWriter.postRollover();
+  }
+
+  @Override
+  public int lastWriteIndex() {
+    return baseWriter.lastWriteIndex();
+  }
+
+  @Override
+  public void bindListener(ColumnWriterListener listener) {
+    baseWriter.bindListener(listener);
+  }
+
+  @Override
+  public void startWrite() {
+    isSetWriter.startWrite();
+    baseWriter.startWrite();
+  }
+
+  @Override
+  public void startRow() {
+    // Skip calls for performance: they do nothing for
+    // scalar writers -- the only kind supported here.
+//    isSetWriter.startRow();
+    baseWriter.startRow();
+  }
+
+  @Override
+  public void endArrayValue() {
+    // Skip calls for performance: they do nothing for
+    // scalar writers -- the only kind supported here.
+//    isSetWriter.saveValue();
+    baseWriter.endArrayValue();
+  }
+
+  @Override
+  public void endWrite() {
+    isSetWriter.endWrite();
+    // Avoid back-filling null values.
+    baseWriter.skipNulls();
+    baseWriter.endWrite();
+  }
+
+  @Override
+  public void dump(HierarchicalFormatter format) {
+    format.extend();
+    super.dump(format);
+    format.attribute("isSetWriter");
+    isSetWriter.dump(format);
+    format.attribute("baseWriter");
+    baseWriter.dump(format);
+    format.endObject();
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/40de8ca4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/ObjectArrayWriter.java
----------------------------------------------------------------------
diff --git 
a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/ObjectArrayWriter.java
 
b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/ObjectArrayWriter.java
new file mode 100644
index 0000000..3554a3b
--- /dev/null
+++ 
b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/ObjectArrayWriter.java
@@ -0,0 +1,143 @@
+/*
+ * 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.drill.exec.vector.accessor.writer;
+
+import org.apache.drill.exec.vector.UInt4Vector;
+import org.apache.drill.exec.vector.accessor.ColumnWriterIndex;
+import 
org.apache.drill.exec.vector.accessor.writer.AbstractArrayWriter.BaseArrayWriter;
+
+/**
+ * Writer for an array of either a map or another array. Here, the contents
+ * are a structure and need explicit saves. State transitions in addition to 
the
+ * base class are:
+ *
+ * <table border=1>
+ * <tr><th>Public API</th><th>Array Event</th>
+ *     <th>Offset Event</th><th>Element Event</th></tr>
+ * <tr><td>save() (array)</td>
+ *     <td>saveValue()</td>
+ *     <td>saveValue()</td>
+ *     <td>saveValue()</td></tr>
+ * </table>
+ *
+ * This class is use for arrays of maps (and for arrays of arrays). When used
+ * with a map, then we have a single offset vector pointing into a group of
+ * arrays. Consider the simple case of a map of three scalars. Here, we have
+ * a hybrid of the states discussed for the {@link BaseScalarWriter} and those
+ * discussed for {@link OffsetVectorWriter}. That is, the offset vector
+ * points into one map element. The individual elements can we Behind,
+ * Written or Unwritten, depending on the specific actions taken by the
+ * client.
+ * <p>
+ * For example:<pre><code>
+ *   Offset Vector      Vector A     Vector B    Vector C       Index
+ *       |    |   + - >   |X| < lwa    |Y|         |Z|            8
+ *  lw > |  8 | - +       | |          |Y|         |Z|            9
+ *   v > | 10 | - - - >   | |          |Y|         |Z|           10
+ *       |    |           | |          |Y| < lwb   |Z|           11
+ *       |    |      v' > | |          | |         |Z| < lwc     12
+ * </code></pre>
+ * In the above:
+ * <ul>
+ * <li>The last write index, lw, for the current row points to the
+ *     previous start position. (Recall that finishing the row writes the
+ *     end position into the entry for the <i>next</i> row.</li>
+ * <li>The top-level vector index, v, points to start position of
+ *     the current row, which is offset 10 in all three data vectors.</li>
+ * <li>The current array write position, v', is for the third element
+ *     of the array that starts at position 10.</li>
+ * <li>Since the row is active, the end position of the row has not yet
+ *     been written, and so is blank in the offset vector.</li>
+ * <li>The previous row had a two-element map array written, starting
+ *     at offset 8 and ending at offset 9 (inclusive), identified as
+ *     writing the next start offset (exclusive) into the following
+ *     offset array slot.</li>
+ * <li>Column A has not had data written since the first element of the
+ *     previous row. It is currently in the Behind state with the last
+ *     write position for A, lwa, pointing to the last write.</li>
+ * <li>Column B is in the Unwritten state. A value was written for
+ *     previous element in the map array, but not for the current element.
+ *     We see this by the fact that the last write position for B, lwb,
+ *     is one behind v'.</li>
+ * <li>Column C has been written for the current array element and is
+ *     in the Written state, with the last write position, lwc, pointing
+ *     to the same location as v'.</li>
+ * </ul>
+ * Suppose we now write to Vector A and end the row:<pre><code>
+ *   Offset Vector      Vector A     Vector B    Vector C       Index
+ *       |    |   + - >   |X|          |Y|         |Z|            8
+ *       |  8 | - +       |0|          |Y|         |Z|            9
+ *  lw > | 10 | - - - >   |0|          |Y|         |Z|           10
+ *   v > | 13 | - +       |0|          |Y| < lwb   |Z|           11
+ *       |    |   |       |X| < lwa    | |         |Z| < lwc     12
+ *       |    |   + - >   | |          | |         | | < v'      13
+ * </code></pre>
+ * Here:
+ * <ul>
+ * <li>Vector A has been back-filled and the last write index advanced.</li>
+ * <li>Vector B is now in the Behind state. Vectors A and B are in the
+ *     Unwritten state.</li>
+ * <li>The end position has been written to the offset vector, the
+ *     offset vector last write position has been advance, and the
+ *     top-level vector offset has advanced.</li>
+ * </ul>
+ * All this happens automatically as part of the indexing mechanisms.
+ * The key reason to understand this flow is to understand what happens
+ * in vector overflow: unlike an array of scalars, in which the data
+ * vector can never be in the Behind state, when we have an array of
+ * maps then each vector can be in an of the scalar writer state.
+ */
+
+public class ObjectArrayWriter extends BaseArrayWriter {
+
+  protected ObjectArrayWriter(UInt4Vector offsetVector, AbstractObjectWriter 
elementWriter) {
+    super(offsetVector, elementWriter);
+  }
+
+  @Override
+  public void bindIndex(ColumnWriterIndex index) {
+    elementIndex = new ArrayElementWriterIndex();
+    super.bindIndex(index);
+  }
+
+  @Override
+  public void save() {
+    elementObjWriter.events().endArrayValue();
+    elementIndex.next();
+  }
+
+  @Override
+  public void set(Object... values) {
+    setObject(values);
+  }
+
+  @Override
+  public void setObject(Object array) {
+    Object values[] = (Object[]) array;
+    for (int i = 0; i < values.length; i++) {
+      elementObjWriter.set(values[i]);
+      save();
+    }
+  }
+
+  @Override
+  public int lastWriteIndex() {
+    // Undefined for arrays
+    return 0;
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/40de8ca4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/OffsetVectorWriter.java
----------------------------------------------------------------------
diff --git 
a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/OffsetVectorWriter.java
 
b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/OffsetVectorWriter.java
new file mode 100644
index 0000000..d5f9b30
--- /dev/null
+++ 
b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/OffsetVectorWriter.java
@@ -0,0 +1,283 @@
+/*
+ * 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.drill.exec.vector.accessor.writer;
+
+import org.apache.drill.exec.vector.BaseDataValueVector;
+import org.apache.drill.exec.vector.UInt4Vector;
+import org.apache.drill.exec.vector.accessor.ValueType;
+import org.apache.drill.exec.vector.accessor.impl.HierarchicalFormatter;
+
+/**
+ * Specialized column writer for the (hidden) offset vector used
+ * with variable-length or repeated vectors. See comments in the
+ * <tt>ColumnAccessors.java</tt> template file for more details.
+ * <p>
+ * Note that the <tt>lastWriteIndex</tt> tracked here corresponds
+ * to the data values; it is one less than the actual offset vector
+ * last write index due to the nature of offset vector layouts. The selection
+ * of last write index basis makes roll-over processing easier as only this
+ * writer need know about the +1 translation required for writing.
+ * <p>
+ * The states illustrated in the base class apply here as well,
+ * remembering that the end offset for a row (or array position)
+ * is written one ahead of the vector index.
+ * <p>
+ * The vector index does create an interesting dynamic for the child
+ * writers. From the child writer's perspective, the states described in
+ * the super class are the only states of interest. Here we want to
+ * take the perspective of the parent.
+ * <p>
+ * The offset vector is an implementation of a repeat level. A repeat
+ * level can occur for a single array, or for a collection of columns
+ * within a repeated map. (A repeat level also occurs for variable-width
+ * fields, but this is a bit harder to see, so let's ignore that for
+ * now.)
+ * <p>
+ * The key point to realize is that each repeat level introduces an
+ * isolation level in terms of indexing. That is, empty values in the
+ * outer level have no affect on indexing in the inner level. In fact,
+ * the nature of a repeated outer level means that there are no empties
+ * in the inner level.
+ * <p>
+ * To illustrate:<pre><code>
+ *       Offset Vector          Data Vector   Indexes
+ *  lw, v > | 10 |   - - - - - >   | X |        10
+ *          | 12 |   - - +         | X | < lw'  11
+ *          |    |       + - - >   |   | < v'   12
+ * </code></pre>
+ * In the above, the client has just written an array of two elements
+ * at the current write position. The data starts at offset 10 in
+ * the data vector, and the next write will be at 12. The end offset
+ * is written one ahead of the vector index.
+ * <p>
+ * From the data vector's perspective, its last-write (lw') reflects
+ * the last element written. If this is an array of scalars, then the
+ * write index is automatically incremented, as illustrated by v'.
+ * (For map arrays, the index must be incremented by calling
+ * <tt>save()</tt> on the map array writer.)
+ * <p>
+ * Suppose the client now skips some arrays:<pre><code>
+ *       Offset Vector          Data Vector
+ *     lw > | 10 |   - - - - - >   | X |        10
+ *          | 12 |   - - +         | X | < lw'  11
+ *          |    |       + - - >   |   | < v'   12
+ *          |    |                 |   |        13
+ *      v > |    |                 |   |        14
+ * </code></pre>
+ * The last write position does not move and there are gaps in the
+ * offset vector. The vector index points to the current row. Note
+ * that the data vector last write and vector indexes do not change,
+ * this reflects the fact that the the data vector's vector index
+ * (v') matches the tail offset
+ * <p>
+ * The
+ * client now writes a three-element vector:<pre><code>
+ *       Offset Vector          Data Vector
+ *          | 10 |   - - - - - >   | X |        10
+ *          | 12 |   - - +         | X |        11
+ *          | 12 |   - - + - - >   | Y |        12
+ *          | 12 |   - - +         | Y |        13
+ *  lw, v > | 12 |   - - +         | Y | < lw'  14
+ *          | 15 |   - - - - - >   |   | < v'   15
+ * </code></pre>
+ * Quite a bit just happened. The empty offset slots were back-filled
+ * with the last write offset in the data vector. The client wrote
+ * three values, which advanced the last write and vector indexes
+ * in the data vector. And, the last write index in the offset
+ * vector also moved to reflect the update of the offset vector.
+ * Note that as a result, multiple positions in the offset vector
+ * point to the same location in the data vector. This is fine; we
+ * compute the number of entries as the difference between two successive
+ * offset vector positions, so the empty positions have become 0-length
+ * arrays.
+ * <p>
+ * Note that, for an array of scalars, when overflow occurs,
+ * we need only worry about two
+ * states in the data vector. Either data has been written for the
+ * row (as in the third example above), and so must be moved to the
+ * roll-over vector, or no data has been written and no move is
+ * needed. We never have to worry about missing values because the
+ * cannot occur in the data vector.
+ * <p>
+ * See {@link ObjectArrayWriter} for information about arrays of
+ * maps (arrays of multiple columns.)
+ */
+
+public class OffsetVectorWriter extends AbstractFixedWidthWriter {
+
+  private static final int VALUE_WIDTH = UInt4Vector.VALUE_WIDTH;
+
+  private UInt4Vector vector;
+
+  /**
+   * Offset of the first value for the current row. Used during
+   * overflow or if the row is restarted.
+   */
+
+  private int rowStartOffset;
+
+  /**
+   * Cached value of the end offset for the current value. Used
+   * primarily for variable-width columns to allow the column to be
+   * rewritten multiple times within the same row. The start offset
+   * value is updated with the end offset only when the value is
+   * committed in {@link @endValue()}.
+   */
+
+  private int nextOffset;
+
+  public OffsetVectorWriter(UInt4Vector vector) {
+    this.vector = vector;
+  }
+
+  @Override public BaseDataValueVector vector() { return vector; }
+  @Override public int width() { return VALUE_WIDTH; }
+
+  @Override
+  protected void realloc(int size) {
+    vector.reallocRaw(size);
+    setBuffer();
+  }
+
+  @Override
+  public ValueType valueType() { return ValueType.INTEGER; }
+
+  @Override
+  public void startWrite() {
+    super.startWrite();
+    nextOffset = 0;
+    rowStartOffset = 0;
+
+    // Special handling for first value. Alloc vector if needed.
+    // Offset vectors require a 0 at position 0. The (end) offset
+    // for row 0 starts at position 1, which is handled in
+    // writeOffset() below.
+
+    if (capacity * VALUE_WIDTH < MIN_BUFFER_SIZE) {
+      realloc(MIN_BUFFER_SIZE);
+    }
+    vector.getBuffer().unsafePutInt(0, 0);
+  }
+
+  public int nextOffset() { return nextOffset; }
+  public int rowStartOffset() { return rowStartOffset; }
+
+  @Override
+  public void startRow() { rowStartOffset = nextOffset; }
+
+  /**
+   * Return the write offset, which is one greater than the index reported
+   * by the vector index.
+   *
+   * @return the offset in which to write the current offset of the end
+   * of the current data value
+   */
+
+  protected final int writeIndex() {
+
+    // "Fast path" for the normal case of no fills, no overflow.
+    // This is the only bounds check we want to do for the entire
+    // set operation.
+
+    // This is performance critical code; every operation counts.
+    // Please be thoughtful when changing the code.
+
+    final int valueIndex = vectorIndex.vectorIndex();
+    int writeIndex = valueIndex + 1;
+    if (lastWriteIndex < valueIndex - 1 || writeIndex >= capacity) {
+      writeIndex = prepareWrite(writeIndex);
+    }
+
+    // Track the last write location for zero-fill use next time around.
+    // Recall, it is the value index, which is one less than the (end)
+    // offset index.
+
+    lastWriteIndex = writeIndex - 1;
+    return writeIndex;
+  }
+
+  protected int prepareWrite(int writeIndex) {
+
+    // Either empties must be filled or the vector is full.
+
+    resize(writeIndex);
+
+    // Call to resize may cause rollover, so reset write index
+    // afterwards.
+
+    writeIndex = vectorIndex.vectorIndex() + 1;
+
+    // Fill empties to the write position.
+
+    fillEmpties(writeIndex);
+    return writeIndex;
+  }
+
+  @Override
+  protected final void fillEmpties(final int writeIndex) {
+    while (lastWriteIndex < writeIndex - 1) {
+      drillBuf.unsafePutInt((++lastWriteIndex + 1) * VALUE_WIDTH, nextOffset);
+    }
+  }
+
+  public final void setNextOffset(final int newOffset) {
+    final int writeIndex = writeIndex();
+    drillBuf.unsafePutInt(writeIndex * VALUE_WIDTH, newOffset);
+    nextOffset = newOffset;
+  }
+
+  @Override
+  public void skipNulls() {
+
+    // Nothing to do. Fill empties logic will fill in missing
+    // offsets.
+  }
+
+  @Override
+  public void restartRow() {
+    nextOffset = rowStartOffset;
+    super.restartRow();
+  }
+
+  @Override
+  public void preRollover() {
+    setValueCount(vectorIndex.rowStartIndex() + 1);
+  }
+
+  @Override
+  public void postRollover() {
+    final int newNext = nextOffset - rowStartOffset;
+    super.postRollover();
+    nextOffset = newNext;
+  }
+
+  @Override
+  public final void endWrite() {
+    setValueCount(vectorIndex.vectorIndex() + 1);
+  }
+
+  @Override
+  public void dump(HierarchicalFormatter format) {
+    format.extend();
+    super.dump(format);
+    format
+      .attribute("lastWriteIndex", lastWriteIndex)
+      .attribute("nextOffset", nextOffset)
+      .endObject();
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/40de8ca4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/ScalarArrayWriter.java
----------------------------------------------------------------------
diff --git 
a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/ScalarArrayWriter.java
 
b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/ScalarArrayWriter.java
new file mode 100644
index 0000000..95f8f29
--- /dev/null
+++ 
b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/ScalarArrayWriter.java
@@ -0,0 +1,229 @@
+/*
+ * 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.drill.exec.vector.accessor.writer;
+
+import java.math.BigDecimal;
+
+import org.apache.drill.exec.record.ColumnMetadata;
+import org.apache.drill.exec.vector.accessor.ColumnWriterIndex;
+import org.apache.drill.exec.vector.accessor.ScalarWriter.ColumnWriterListener;
+import 
org.apache.drill.exec.vector.accessor.writer.AbstractArrayWriter.BaseArrayWriter;
+import 
org.apache.drill.exec.vector.accessor.writer.AbstractScalarWriter.ScalarObjectWriter;
+import org.apache.drill.exec.vector.complex.RepeatedValueVector;
+import org.joda.time.Period;
+
+/**
+ * Writer for a column that holds an array of scalars. This writer manages
+ * the array itself. A type-specific child writer manages the elements within
+ * the array. The overall row index (usually) provides the index into
+ * the offset vector. An array-specific element index provides the index
+ * into elements.
+ * <p>
+ * This class manages the offset vector directly. Doing so saves one read and
+ * one write to direct memory per element value.
+ * <p>
+ * Provides generic write methods for testing and other times when
+ * convenience is more important than speed.
+ * <p>
+ * The scalar writer for array-valued columns appends values: once a value
+ * is written, it cannot be changed. As a result, writer methods have no item 
index;
+ * each set advances the array to the next position. This is an abstract base 
class;
+ * subclasses are generated for each repeated value vector type.
+ */
+
+public class ScalarArrayWriter extends BaseArrayWriter {
+
+  /**
+   * For scalar arrays, incrementing the element index and
+   * committing the current value is done automatically since
+   * there is exactly one value per array element.
+   */
+
+  public class ScalarElementWriterIndex extends ArrayElementWriterIndex {
+
+    @Override
+    public final void nextElement() { next(); }
+  }
+
+  private final BaseScalarWriter elementWriter;
+
+  public ScalarArrayWriter(ColumnMetadata schema,
+      RepeatedValueVector vector, BaseScalarWriter elementWriter) {
+    super(vector.getOffsetVector(),
+        new ScalarObjectWriter(schema, elementWriter));
+    this.elementWriter = elementWriter;
+  }
+
+  public static ArrayObjectWriter build(ColumnMetadata schema,
+      RepeatedValueVector repeatedVector, BaseScalarWriter elementWriter) {
+    return new ArrayObjectWriter(schema,
+        new ScalarArrayWriter(schema, repeatedVector, elementWriter));
+  }
+
+  @Override
+  public void bindIndex(ColumnWriterIndex index) {
+    elementIndex = new ScalarElementWriterIndex();
+    super.bindIndex(index);
+    elementWriter.bindIndex(elementIndex);
+  }
+
+  @Override
+  public void bindListener(ColumnWriterListener listener) {
+    elementWriter.bindListener(listener);
+  }
+
+  @Override
+  public void save() {
+    // No-op: done when writing each scalar value
+  }
+
+  @Override
+  public void set(Object... values) {
+    for (Object value : values) {
+      entry().set(value);
+    }
+  }
+
+  @Override
+  public void setObject(Object array) {
+    if (array == null) {
+      // Assume null means a 0-element array since Drill does
+      // not support null for the whole array.
+
+      return;
+    }
+    String objClass = array.getClass().getName();
+    if (! objClass.startsWith("[")) {
+      throw new IllegalArgumentException("Argument must be an array");
+    }
+
+    // Figure out type
+
+    char second = objClass.charAt(1);
+    switch ( second ) {
+    case  '[':
+      // bytes is represented as an array of byte arrays.
+
+      char third = objClass.charAt(2);
+      switch (third) {
+      case 'B':
+        setBytesArray((byte[][]) array);
+        break;
+      default:
+        throw new IllegalArgumentException( "Unknown Java array type: " + 
objClass );
+      }
+      break;
+    case  'S':
+      setShortArray((short[]) array );
+      break;
+    case  'I':
+      setIntArray((int[]) array );
+      break;
+    case  'J':
+      setLongArray((long[]) array );
+      break;
+    case  'F':
+      setFloatArray((float[]) array );
+      break;
+    case  'D':
+      setDoubleArray((double[]) array );
+      break;
+    case  'Z':
+      setBooleanArray((boolean[]) array );
+      break;
+    case 'L':
+      int posn = objClass.indexOf(';');
+
+      // If the array is of type Object, then we have no type info.
+
+      String memberClassName = objClass.substring( 2, posn );
+      if (memberClassName.equals(String.class.getName())) {
+        setStringArray((String[]) array );
+      } else if (memberClassName.equals(Period.class.getName())) {
+        setPeriodArray((Period[]) array );
+      } else if (memberClassName.equals(BigDecimal.class.getName())) {
+        setBigDecimalArray((BigDecimal[]) array );
+      } else {
+        throw new IllegalArgumentException( "Unknown Java array type: " + 
memberClassName );
+      }
+      break;
+    default:
+      throw new IllegalArgumentException( "Unknown Java array type: " + 
objClass );
+    }
+  }
+
+  public void setBooleanArray(boolean[] value) {
+    for (int i = 0; i < value.length; i++) {
+      elementWriter.setInt(value[i] ? 1 : 0);
+    }
+  }
+
+  public void setBytesArray(byte[][] value) {
+    for (int i = 0; i < value.length; i++) {
+      elementWriter.setBytes(value[i], value[i].length);
+    }
+  }
+
+  public void setShortArray(short[] value) {
+    for (int i = 0; i < value.length; i++) {
+      elementWriter.setInt(value[i]);
+    }
+  }
+
+  public void setIntArray(int[] value) {
+    for (int i = 0; i < value.length; i++) {
+      elementWriter.setInt(value[i]);
+    }
+  }
+
+  public void setLongArray(long[] value) {
+    for (int i = 0; i < value.length; i++) {
+      elementWriter.setLong(value[i]);
+    }
+  }
+
+  public void setFloatArray(float[] value) {
+    for (int i = 0; i < value.length; i++) {
+      elementWriter.setDouble(value[i]);
+    }
+  }
+
+  public void setDoubleArray(double[] value) {
+    for (int i = 0; i < value.length; i++) {
+      elementWriter.setDouble(value[i]);
+    }
+  }
+
+  public void setStringArray(String[] value) {
+    for (int i = 0; i < value.length; i++) {
+      elementWriter.setString(value[i]);
+    }
+  }
+
+  public void setPeriodArray(Period[] value) {
+    for (int i = 0; i < value.length; i++) {
+      elementWriter.setPeriod(value[i]);
+    }
+  }
+
+  public void setBigDecimalArray(BigDecimal[] value) {
+    for (int i = 0; i < value.length; i++) {
+      elementWriter.setDecimal(value[i]);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/40de8ca4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/WriterEvents.java
----------------------------------------------------------------------
diff --git 
a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/WriterEvents.java
 
b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/WriterEvents.java
new file mode 100644
index 0000000..7566f28
--- /dev/null
+++ 
b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/WriterEvents.java
@@ -0,0 +1,127 @@
+/*
+ * 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.drill.exec.vector.accessor.writer;
+
+import org.apache.drill.exec.vector.accessor.ColumnWriterIndex;
+
+/**
+ * Internal interface used to control the behavior
+ * of writers. Consumers of writers never use this method; it is
+ * instead used by the code that implements writers.
+ * <p>
+ * Most methods here represents events in a state machine. The top-level
+ * writer provides a set of public methods which trigger one or more of
+ * these internal events. The events draw some fine distinctions between
+ * top-level values and those nested within arrays. See each kind of
+ * writer for the details.
+ * <p>
+ * The events also ensure symmetry between top-level and nested tuples,
+ * especially those nested within an array. That is, an event cannot change
+ * meaning depending on whether the tuple is top-level or nested within an
+ * array. Instead, the order of calls, or selectively making or not making
+ * calls, can change.
+ */
+
+public interface WriterEvents {
+
+  /**
+   * Bind the writer to a writer index.
+   *
+   * @param index the writer index (top level or nested for
+   * arrays)
+   */
+
+  void bindIndex(ColumnWriterIndex index);
+
+  ColumnWriterIndex writerIndex();
+
+  /**
+   * Start a write (batch) operation. Performs any vector initialization
+   * required at the start of a batch (especially for offset vectors.)
+   */
+
+  void startWrite();
+
+  /**
+   * Start a new row. To be called only when a row is not active. To
+   * restart a row, call {@link #restartRow()} instead.
+   */
+
+  void startRow();
+
+  /**
+   * End a value. Similar to {@link saveRow()}, but the save of a value
+   * is conditional on saving the row. This version is primarily of use
+   * in tuples nested inside arrays: it saves each tuple within the array,
+   * advancing to a new position in the array. The update of the array's
+   * offset vector based on the cumulative value saves is done when
+   * saving the row.
+   */
+
+  void endArrayValue();
+
+  /**
+   * During a writer to a row, rewind the the current index position to
+   * restart the row.
+   * Done when abandoning the current row, such as when filtering out
+   * a row at read time.
+   */
+
+  void restartRow();
+
+  /**
+   * Saves a row. Commits offset vector locations and advances each to
+   * the next position. Can be called only when a row is active.
+   */
+
+  void saveRow();
+
+  /**
+   * End a batch: finalize any vector values.
+   */
+
+  void endWrite();
+
+  /**
+   * The vectors backing this vector are about to roll over. Finish
+   * the current batch up to, but not including, the current row.
+   */
+
+  void preRollover();
+
+  /**
+   * The vectors backing this writer rolled over. This means that data
+   * for the current row has been rolled over into a new vector. Offsets
+   * and indexes should be shifted based on the understanding that data
+   * for the current row now resides at the start of a new vector instead
+   * of its previous location elsewhere in an old vector.
+   */
+
+  void postRollover();
+
+  /**
+   * Return the last write position in the vector. This may be the
+   * same as the writer index position (if the vector was written at
+   * that point), or an earlier point. In either case, this value
+   * points to the last valid value in the vector.
+   *
+   * @return index of the last valid value in the vector
+   */
+
+  int lastWriteIndex();
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/40de8ca4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/dummy/DummyArrayWriter.java
----------------------------------------------------------------------
diff --git 
a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/dummy/DummyArrayWriter.java
 
b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/dummy/DummyArrayWriter.java
new file mode 100644
index 0000000..7c9f8ba
--- /dev/null
+++ 
b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/dummy/DummyArrayWriter.java
@@ -0,0 +1,96 @@
+/*
+ * 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.drill.exec.vector.accessor.writer.dummy;
+
+import org.apache.drill.exec.vector.accessor.ColumnWriterIndex;
+import org.apache.drill.exec.vector.accessor.ScalarWriter.ColumnWriterListener;
+import org.apache.drill.exec.vector.accessor.TupleWriter.TupleWriterListener;
+import org.apache.drill.exec.vector.accessor.writer.AbstractArrayWriter;
+import org.apache.drill.exec.vector.accessor.writer.AbstractObjectWriter;
+import org.apache.drill.exec.vector.accessor.writer.OffsetVectorWriter;
+
+/**
+ * Dummy scalar array writer that allows a client to write values into
+ * the array, but discards all of them. Provides no implementations of
+ * any methods, all are simply ignored.
+ * <p>
+ * Experience may suggest that some methods must return non-dummy
+ * values, such as the number of items in the array. That can be added
+ * as needed.
+ */
+public class DummyArrayWriter extends AbstractArrayWriter {
+
+  public DummyArrayWriter(
+      AbstractObjectWriter elementWriter) {
+    super(elementWriter);
+  }
+
+  @Override
+  public int size() { return 0; }
+
+  @Override
+  public void save() { }
+
+  @Override
+  public void set(Object... values) { }
+
+  @Override
+  public void setObject(Object array) { }
+
+  @Override
+  public void bindIndex(ColumnWriterIndex index) { }
+
+  @Override
+  public ColumnWriterIndex writerIndex() { return null; }
+
+  @Override
+  public void startWrite() { }
+
+  @Override
+  public void startRow() { }
+
+  @Override
+  public void endArrayValue() { }
+
+  @Override
+  public void restartRow() { }
+
+  @Override
+  public void saveRow() { }
+
+  @Override
+  public void endWrite() { }
+
+  @Override
+  public void preRollover() { }
+
+  @Override
+  public void postRollover() { }
+
+  @Override
+  public int lastWriteIndex() { return 0; }
+
+  @Override
+  public void bindListener(ColumnWriterListener listener) { }
+
+  @Override
+  public void bindListener(TupleWriterListener listener) { }
+
+  @Override
+  public OffsetVectorWriter offsetWriter() { return null; }
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/40de8ca4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/dummy/DummyScalarWriter.java
----------------------------------------------------------------------
diff --git 
a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/dummy/DummyScalarWriter.java
 
b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/dummy/DummyScalarWriter.java
new file mode 100644
index 0000000..e8272d6
--- /dev/null
+++ 
b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/writer/dummy/DummyScalarWriter.java
@@ -0,0 +1,89 @@
+/*
+ * 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.drill.exec.vector.accessor.writer.dummy;
+
+import java.math.BigDecimal;
+
+import org.apache.drill.exec.vector.BaseDataValueVector;
+import org.apache.drill.exec.vector.accessor.ColumnWriterIndex;
+import org.apache.drill.exec.vector.accessor.ValueType;
+import org.apache.drill.exec.vector.accessor.writer.AbstractScalarWriter;
+import org.joda.time.Period;
+
+/**
+ * Represents a non-projected column. The writer accepts data, but
+ * discards it. The writer does not participate in writer events,
+ * nor is it backed by a real vector, index or type.
+ */
+
+public class DummyScalarWriter extends AbstractScalarWriter {
+
+  @Override
+  public void bindListener(ColumnWriterListener listener) { }
+
+  @Override
+  public ValueType valueType() { return null; }
+
+  @Override
+  public void setNull() { }
+
+  @Override
+  public void setInt(int value) { }
+
+  @Override
+  public void setLong(long value) { }
+
+  @Override
+  public void setDouble(double value) { }
+
+  @Override
+  public void setString(String value) { }
+
+  @Override
+  public void setBytes(byte[] value, int len) { }
+
+  @Override
+  public void setDecimal(BigDecimal value) { }
+
+  @Override
+  public void setPeriod(Period value) { }
+
+  @Override
+  public void bindIndex(ColumnWriterIndex index) { }
+
+  @Override
+  public ColumnWriterIndex writerIndex() { return null; }
+
+  @Override
+  public void restartRow() { }
+
+  @Override
+  public void endWrite() { }
+
+  @Override
+  public void preRollover() { }
+
+  @Override
+  public void postRollover() { }
+
+  @Override
+  public int lastWriteIndex() { return 0; }
+
+  @Override
+  public BaseDataValueVector vector() { return null; }
+}

Reply via email to