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>—</td> + * <td>IDLE</td> + * <td>—</td></tr> + * <tr><td>startBatch()</td> + * <td>startWrite()</td> + * <td>IDLE → IN_WRITE</td> + * <td>startWrite()</td></tr> + * <tr><td>start() (new row)</td> + * <td>startRow()</td> + * <td>IN_WRITE → IN_ROW</td> + * <td>startRow()</td></tr> + * <tr><td>start() (without save)</td> + * <td>restartRow()</td> + * <td>IN_ROW → IN_ROW</td> + * <td>restartRow()</td></tr> + * <tr><td>save() (array)</td> + * <td>saveValue()</td> + * <td>IN_ROW → IN_ROW</td> + * <td>saveValue()</td></tr> + * <tr><td rowspan=2>save() (row)</td> + * <td>saveValue()</td> + * <td>IN_ROW → IN_ROW</td> + * <td>saveValue()</td></tr> + * <tr><td>saveRow()</td> + * <td>IN_ROW → IN_WRITE</td> + * <td>saveRow()</td></tr> + * <tr><td rowspan=2>end batch</td> + * <td>—</td> + * <td>IN_ROW → IDLE</td> + * <td>endWrite()</td></tr> + * <tr><td>—</td> + * <td>IN_WRITE → 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; } +}
