http://git-wip-us.apache.org/repos/asf/drill/blob/4f2182e4/exec/java-exec/src/test/java/org/apache/drill/test/rowSet/test/VectorPrinter.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/test/java/org/apache/drill/test/rowSet/test/VectorPrinter.java b/exec/java-exec/src/test/java/org/apache/drill/test/rowSet/test/VectorPrinter.java deleted file mode 100644 index 2056220..0000000 --- a/exec/java-exec/src/test/java/org/apache/drill/test/rowSet/test/VectorPrinter.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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.test.rowSet.test; - -import org.apache.drill.exec.vector.UInt4Vector; -import org.apache.drill.exec.vector.ValueVector; -import org.apache.drill.exec.vector.VarCharVector; - -import com.google.common.base.Charsets; - -/** - * Handy tool to visualize string and offset vectors for - * debugging. - */ - -public class VectorPrinter { - - public static void printOffsets(UInt4Vector vector, int start, int length) { - header(vector, start, length); - for (int i = start, j = 0; j < length; i++, j++) { - if (j > 0) { - System.out.print(" "); - } - System.out.print(vector.getAccessor().get(i)); - } - System.out.print("], addr = "); - System.out.println(vector.getBuffer().addr()); - } - - public static void printStrings(VarCharVector vector, int start, int length) { - printOffsets(vector.getOffsetVector(), start, length + 1); - header(vector, start, length); - System.out.println(); - for (int i = start, j = 0; j < length; i++, j++) { - System.out.print(" "); - System.out.print(i); - System.out.print(": \""); - System.out.print(stringAt(vector, i)); - System.out.println("\""); - } - System.out.println("]"); - } - - public static void header(ValueVector vector, int start, int length) { - System.out.print(vector.getClass()); - System.out.print(": ("); - System.out.print(start); - System.out.print(" - "); - System.out.print(start + length - 1); - System.out.print("): ["); - } - - public static String stringAt(VarCharVector vector, int i) { - return new String(vector.getAccessor().get(i), Charsets.UTF_8); - } - -}
http://git-wip-us.apache.org/repos/asf/drill/blob/4f2182e4/exec/memory/base/src/main/java/io/netty/buffer/DrillBuf.java ---------------------------------------------------------------------- diff --git a/exec/memory/base/src/main/java/io/netty/buffer/DrillBuf.java b/exec/memory/base/src/main/java/io/netty/buffer/DrillBuf.java index 109500a..513f50b 100644 --- a/exec/memory/base/src/main/java/io/netty/buffer/DrillBuf.java +++ b/exec/memory/base/src/main/java/io/netty/buffer/DrillBuf.java @@ -823,4 +823,19 @@ public final class DrillBuf extends AbstractByteBuf implements AutoCloseable { historicalLog.buildHistory(sb, indent + 1, verbosity.includeStackTraces); } } + + /** + * Convenience method to read buffer bytes into a newly allocated byte + * array. + * + * @param srcOffset the offset into this buffer of the data to read + * @param length number of bytes to read + * @return byte array with the requested bytes + */ + + public byte[] unsafeGetMemory(int srcOffset, int length) { + byte buf[] = new byte[length]; + PlatformDependent.copyMemory(addr + srcOffset, buf, 0, length); + return buf; + } } http://git-wip-us.apache.org/repos/asf/drill/blob/4f2182e4/exec/vector/src/main/codegen/templates/ColumnAccessors.java ---------------------------------------------------------------------- diff --git a/exec/vector/src/main/codegen/templates/ColumnAccessors.java b/exec/vector/src/main/codegen/templates/ColumnAccessors.java index 14ec1e8..4836099 100644 --- a/exec/vector/src/main/codegen/templates/ColumnAccessors.java +++ b/exec/vector/src/main/codegen/templates/ColumnAccessors.java @@ -1,3 +1,4 @@ +<#macro copyright> /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -16,9 +17,10 @@ * limitations under the License. */ +// This class is generated using Freemarker and the ${.template_name} template. +</#macro> <@pp.dropOutputFile /> <@pp.changeOutputFile name="/org/apache/drill/exec/vector/accessor/ColumnAccessors.java" /> -<#include "/@includes/license.ftl" /> <#macro getType drillType label> @Override public ValueType valueType() { @@ -31,74 +33,6 @@ </#if> } </#macro> -<#macro bindReader vectorPrefix drillType isArray > - <#if drillType = "Decimal9" || drillType == "Decimal18"> - private MajorType type; - </#if> - private ${vectorPrefix}${drillType}Vector.Accessor accessor; - - @Override - public void bindVector(ValueVector vector) { - <#if drillType = "Decimal9" || drillType == "Decimal18"> - type = vector.getField().getType(); - </#if> - accessor = ((${vectorPrefix}${drillType}Vector) vector).getAccessor(); - } - - <#if drillType = "Decimal9" || drillType == "Decimal18"> - @Override - public void bindVector(MajorType type, VectorAccessor va) { - super.bindVector(type, va); - this.type = type; - } - - </#if> - private ${vectorPrefix}${drillType}Vector.Accessor accessor() { - if (vectorAccessor == null) { - return accessor; - } else { - return ((${vectorPrefix}${drillType}Vector) vectorAccessor.vector()).getAccessor(); - } - } -</#macro> -<#macro get drillType accessorType label isArray> - @Override - public ${accessorType} get${label}(<#if isArray>int index</#if>) { - <#assign getObject ="getObject"/> - <#if isArray> - <#assign indexVar = "index"/> - <#else> - <#assign indexVar = ""/> - </#if> - <#if drillType == "VarChar" || drillType == "Var16Char" || drillType == "VarBinary"> - return accessor().get(vectorIndex.vectorIndex(${indexVar})); - <#elseif drillType == "Decimal9" || drillType == "Decimal18"> - return DecimalUtility.getBigDecimalFromPrimitiveTypes( - accessor().get(vectorIndex.vectorIndex(${indexVar})), - type.getScale(), - type.getPrecision()); - <#elseif accessorType == "BigDecimal" || accessorType == "Period"> - return accessor().${getObject}(vectorIndex.vectorIndex(${indexVar})); - <#elseif drillType == "UInt1"> - return ((int) accessor().get(vectorIndex.vectorIndex(${indexVar}))) & 0xFF; - <#else> - return accessor().get(vectorIndex.vectorIndex(${indexVar})); - </#if> - } - <#if drillType == "VarChar"> - - @Override - public String getString(<#if isArray>int index</#if>) { - return new String(getBytes(${indexVar}), Charsets.UTF_8); - } - <#elseif drillType == "Var16Char"> - - @Override - public String getString(<#if isArray>int index</#if>) { - return new String(getBytes(${indexVar}), Charsets.UTF_16); - } - </#if> -</#macro> <#macro build types vectorType accessorType> <#if vectorType == "Repeated"> <#assign fnPrefix = "Array" /> @@ -126,24 +60,27 @@ </#list> } </#macro> +<@copyright /> package org.apache.drill.exec.vector.accessor; import java.math.BigDecimal; import org.apache.drill.common.types.TypeProtos.MajorType; -import org.apache.drill.common.types.TypeProtos.MinorType; +import org.apache.drill.exec.vector.DateUtilities; +import org.apache.drill.exec.record.metadata.ColumnMetadata; import org.apache.drill.exec.vector.*; import org.apache.drill.exec.util.DecimalUtility; -import org.apache.drill.exec.vector.accessor.reader.BaseScalarReader; -import org.apache.drill.exec.vector.accessor.reader.BaseElementReader; +import org.apache.drill.exec.vector.accessor.reader.BaseScalarReader.BaseVarWidthReader; +import org.apache.drill.exec.vector.accessor.reader.BaseScalarReader.BaseFixedWidthReader; import org.apache.drill.exec.vector.accessor.reader.VectorAccessor; -import org.apache.drill.exec.vector.accessor.writer.BaseScalarWriter; import org.apache.drill.exec.vector.accessor.writer.AbstractFixedWidthWriter.BaseFixedWidthWriter; import org.apache.drill.exec.vector.accessor.writer.BaseVarWidthWriter; import com.google.common.base.Charsets; +import io.netty.buffer.DrillBuf; + import org.joda.time.Period; /** @@ -161,8 +98,6 @@ import org.joda.time.Period; * row.) */ -// This class is generated using freemarker and the ${.template_name} template. - public class ColumnAccessors { <#list vv.types as type> @@ -177,56 +112,141 @@ public class ColumnAccessors { <#if accessorType=="BigDecimal"> <#assign label="Decimal"> </#if> - <#if drillType == "VarChar" || drillType == "Var16Char"> + <#assign varWidth = drillType == "VarChar" || drillType == "Var16Char" || drillType == "VarBinary" /> + <#assign decimal = drillType == "Decimal9" || drillType == "Decimal18" || + drillType == "Decimal28Sparse" || drillType == "Decimal38Sparse" /> + <#if varWidth> <#assign accessorType = "byte[]"> <#assign label = "Bytes"> + <#assign putArgs = ", int len"> + <#else> + <#assign putArgs = ""> + </#if> + <#if javaType == "char"> + <#assign putType = "short" /> + <#assign doCast = true /> + <#else> + <#assign putType = javaType /> + <#assign doCast = (cast == "set") /> </#if> <#if ! notyet> //------------------------------------------------------------------------ // ${drillType} readers and writers - public static class ${drillType}ColumnReader extends BaseScalarReader { + <#if varWidth> + public static class ${drillType}ColumnReader extends BaseVarWidthReader { + + <#else> + public static class ${drillType}ColumnReader extends BaseFixedWidthReader { + + private static final int VALUE_WIDTH = ${drillType}Vector.VALUE_WIDTH; - <@bindReader "" drillType false /> + <#if decimal> + private MajorType type; + + </#if> + </#if> + <#if decimal> + @Override + public void bindVector(ColumnMetadata schema, VectorAccessor va) { + super.bindVector(schema, va); + <#if decimal> + type = va.type(); + </#if> + } + </#if> <@getType drillType label /> - <@get drillType accessorType label false/> - } - - public static class Nullable${drillType}ColumnReader extends BaseScalarReader { - - <@bindReader "Nullable" drillType false /> - - <@getType drillType label /> + <#if ! varWidth> + @Override public int width() { return VALUE_WIDTH; } + </#if> @Override - public boolean isNull() { - return accessor().isNull(vectorIndex.vectorIndex()); + public ${accessorType} get${label}() { + <#assign getObject ="getObject"/> + <#assign indexVar = ""/> + final DrillBuf buf = bufferAccessor.buffer(); + <#if ! varWidth> + final int readOffset = vectorIndex.offset(); + <#assign getOffset = "readOffset * VALUE_WIDTH"> + </#if> + <#if varWidth> + final long entry = offsetsReader.getEntry(); + return buf.unsafeGetMemory((int) (entry >> 32), (int) (entry & 0xFFFF_FFFF)); + <#elseif drillType == "Decimal9"> + return DecimalUtility.getBigDecimalFromPrimitiveTypes( + buf.getInt(${getOffset}), + type.getScale(), + type.getPrecision()); + <#elseif drillType == "Decimal18"> + return DecimalUtility.getBigDecimalFromPrimitiveTypes( + buf.getLong(${getOffset}), + type.getScale(), + type.getPrecision()); + <#elseif drillType == "IntervalYear"> + return DateUtilities.fromIntervalYear( + buf.getInt(${getOffset})); + <#elseif drillType == "IntervalDay"> + final int offset = ${getOffset}; + return DateUtilities.fromIntervalDay( + buf.getInt(offset), + buf.getInt(offset + ${minor.millisecondsOffset})); + <#elseif drillType == "Interval"> + final int offset = ${getOffset}; + return DateUtilities.fromInterval( + buf.getInt(offset), + buf.getInt(offset + ${minor.daysOffset}), + buf.getInt(offset + ${minor.millisecondsOffset})); + <#elseif drillType == "Decimal28Sparse" || drillType == "Decimal38Sparse"> + return DecimalUtility.getBigDecimalFromSparse(buf, ${getOffset}, + ${minor.nDecimalDigits}, type.getScale()); + <#elseif drillType == "Decimal28Dense" || drillType == "Decimal38Dense"> + return DecimalUtility.getBigDecimalFromDense(buf, ${getOffset}, + ${minor.nDecimalDigits}, type.getScale(), + ${minor.maxPrecisionDigits}, VALUE_WIDTH); + <#elseif drillType == "UInt1"> + return buf.getByte(${getOffset}) & 0xFF; + <#elseif drillType == "UInt2"> + return buf.getShort(${getOffset}) & 0xFFFF; + <#elseif drillType == "UInt4"> + // Should be the following: + // return ((long) buf.unsafeGetInt(${getOffset})) & 0xFFFF_FFFF; + // else, the unsigned values of 32 bits are mapped to negative. + return buf.getInt(${getOffset}); + <#elseif drillType == "Float4"> + return Float.intBitsToFloat(buf.getInt(${getOffset})); + <#elseif drillType == "Float8"> + return Double.longBitsToDouble(buf.getLong(${getOffset})); + <#else> + return buf.get${putType?cap_first}(${getOffset}); + </#if> } + <#if drillType == "VarChar"> - <@get drillType accessorType label false /> - } - - public static class Repeated${drillType}ColumnReader extends BaseElementReader { - - <@bindReader "" drillType true /> - - <@getType drillType label /> + @Override + public String getString() { + return new String(getBytes(${indexVar}), Charsets.UTF_8); + } + <#elseif drillType == "Var16Char"> - <@get drillType accessorType label true /> + @Override + public String getString() { + return new String(getBytes(${indexVar}), Charsets.UTF_16); + } + </#if> } - <#assign varWidth = drillType == "VarChar" || drillType == "Var16Char" || drillType == "VarBinary" /> <#if varWidth> public static class ${drillType}ColumnWriter extends BaseVarWidthWriter { <#else> public static class ${drillType}ColumnWriter extends BaseFixedWidthWriter { - <#if drillType = "Decimal9" || drillType == "Decimal18" || - drillType == "Decimal28Sparse" || drillType == "Decimal38Sparse"> + + private static final int VALUE_WIDTH = ${drillType}Vector.VALUE_WIDTH; + + <#if decimal> private MajorType type; </#if> - private static final int VALUE_WIDTH = ${drillType}Vector.VALUE_WIDTH; </#if> private final ${drillType}Vector vector; @@ -234,8 +254,7 @@ public class ColumnAccessors { <#if varWidth> super(((${drillType}Vector) vector).getOffsetVector()); <#else> - <#if drillType = "Decimal9" || drillType == "Decimal18" || - drillType == "Decimal28Sparse" || drillType == "Decimal38Sparse"> + <#if decimal> type = vector.getField().getType(); </#if> </#if> @@ -300,12 +319,12 @@ public class ColumnAccessors { <#elseif drillType == "IntervalDay"> final int offset = ${putAddr}; drillBuf.setInt(offset, value.getDays()); - drillBuf.setInt(offset + 4, periodToMillis(value)); + drillBuf.setInt(offset + 4, DateUtilities.periodToMillis(value)); <#elseif drillType == "Interval"> final int offset = ${putAddr}; drillBuf.setInt(offset, value.getYears() * 12 + value.getMonths()); drillBuf.setInt(offset + 4, value.getDays()); - drillBuf.setInt(offset + 8, periodToMillis(value)); + drillBuf.setInt(offset + 8, DateUtilities.periodToMillis(value)); <#elseif drillType == "Float4"> drillBuf.setInt(${putAddr}, Float.floatToRawIntBits((float) value)); <#elseif drillType == "Float8"> @@ -335,18 +354,22 @@ public class ColumnAccessors { </#if> </#list> </#list> - public static int periodToMillis(Period value) { - return ((value.getHours() * 60 + - value.getMinutes()) * 60 + - value.getSeconds()) * 1000 + - value.getMillis(); - } +} +<@pp.changeOutputFile name="/org/apache/drill/exec/vector/accessor/ColumnAccessorUtils.java" /> +<@copyright /> -<@build vv.types "Required" "Reader" /> +package org.apache.drill.exec.vector.accessor; -<@build vv.types "Nullable" "Reader" /> +import org.apache.drill.common.types.TypeProtos.MinorType; +import org.apache.drill.exec.vector.accessor.ColumnAccessors.*; +import org.apache.drill.exec.vector.accessor.reader.BaseScalarReader; +import org.apache.drill.exec.vector.accessor.writer.BaseScalarWriter; -<@build vv.types "Repeated" "Reader" /> +public class ColumnAccessorUtils { + + private ColumnAccessorUtils() { } + +<@build vv.types "Required" "Reader" /> <@build vv.types "Required" "Writer" /> } http://git-wip-us.apache.org/repos/asf/drill/blob/4f2182e4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ArrayReader.java ---------------------------------------------------------------------- diff --git a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ArrayReader.java b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ArrayReader.java index 8f33f0e..0679c3b 100644 --- a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ArrayReader.java +++ b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ArrayReader.java @@ -32,7 +32,7 @@ package org.apache.drill.exec.vector.accessor; * {@see ArrayWriter} */ -public interface ArrayReader { +public interface ArrayReader extends ColumnReader { /** * Number of elements in the array. @@ -50,26 +50,6 @@ public interface ArrayReader { ObjectType entryType(); /** - * Return a reader for the elements of a scalar array. - * @return reader for scalar elements - */ - - ScalarElementReader elements(); - - /** - * Return a generic object reader for the array entry. Not available - * for scalar elements. Positions the reader to read the selected - * element. - * - * @param index array index - * @return generic object reader - */ - - ObjectReader entry(int index); - TupleReader tuple(int index); - ArrayReader array(int index); - - /** * Return the generic object reader for the array element. This * version <i>does not</i> position the reader, the client must * call {@link setPosn()} to set the position. This form allows @@ -77,6 +57,7 @@ public interface ArrayReader { */ ObjectReader entry(); + ScalarReader scalar(); TupleReader tuple(); ArrayReader array(); @@ -88,19 +69,14 @@ public interface ArrayReader { void setPosn(int index); - /** - * Return the entire array as an <tt>List</tt> of objects. - * Note, even if the array is scalar, the elements are still returned - * as a list. This method is primarily for testing. - * @return array as a <tt>List</tt> of objects - */ - - Object getObject(); + void rewind(); /** - * Return the entire array as a string. Primarily for debugging. - * @return string representation of the array + * Move forward one position. + * + * @return true if another position is available, false if + * the end of the array is reached */ - String getAsString(); + boolean next(); } http://git-wip-us.apache.org/repos/asf/drill/blob/4f2182e4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ColumnReader.java ---------------------------------------------------------------------- diff --git a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ColumnReader.java b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ColumnReader.java new file mode 100644 index 0000000..15e5c74 --- /dev/null +++ b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ColumnReader.java @@ -0,0 +1,83 @@ +/* + * 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; + +import org.apache.drill.exec.record.metadata.ColumnMetadata; + +/** + * Base interface for all column readers, defining a generic set of methods + * that all readers provide. In particular, given the metadata and the object + * type, one can determine what to do with each reader when working with readers + * generically. The <tt>getObject()</tt> and <tt>getAsString()</tt> methods provide + * generic data access for tests and debugging. + */ + +public interface ColumnReader { + + ColumnMetadata schema(); + + /** + * The type of this reader. + * + * @return type of reader + */ + + ObjectType type(); + + /** + * Determine if this value is null. + * <ul> + * <li>Nullable scalar: determine if the value is null.</li> + * <li>Non-nullable scalar: always returns <tt>false</tt>.</li> + * <li>Arrays: always returns </tt>false</tt.></li> + * <li>Lists: determine if the list for the current row is null. + * In a list, an array entry can be null, empty, or can contain + * items. In repeated types, the array itself is never null. + * If the array is null, then it implicitly has no entries.</li> + * <li>Map or Repeated Map: Always returns <tt>false</tt>.</li> + * <li>Map inside a union, or in a list that contains a union, + * the tuple itself can be null.</li> + * <li>Union: Determine if the current value is null. Null values have no type + * and no associated reader.</li> + * </ul> + * + * @return <tt>true</tt> if this value is null; <tt>false</tt> otherwise + */ + + boolean isNull(); + + /** + * Return the value of the underlying data as a Java object. + * Primarily for testing + * <ul> + * <li>Array: Return the entire array as an <tt>List</tt> of objects. + * Note, even if the array is scalar, the elements are still returned + * as a list.</li> + * </ul> + * @return the value as a Java object + */ + + Object getObject(); + + /** + * Return the entire object as a string. Primarily for debugging. + * @return string representation of the object + */ + + String getAsString(); +} http://git-wip-us.apache.org/repos/asf/drill/blob/4f2182e4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ColumnReaderIndex.java ---------------------------------------------------------------------- diff --git a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ColumnReaderIndex.java b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ColumnReaderIndex.java index b40b705..edc3623 100644 --- a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ColumnReaderIndex.java +++ b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ColumnReaderIndex.java @@ -18,11 +18,105 @@ package org.apache.drill.exec.vector.accessor; /** - * Index into a vector batch, or an array, at read time. - * Supports direct, indirect and hyper-batches. + * The reader structure is heavily recursive. The top-level reader + * iterates over batches, possibly through an indirection vector + * (SV2 or SV4.) The row is tuple of top-level vectors. Each top-level + * vector may be an array. Iteration through the array works identically + * to iteration over the batch as a whole. (In fact, the scalar readers + * don't know if they are top-level or array readers.) Array nesting + * can continue to any level of depth. + * <p> + * Further, when used with a logical join, the top-level iteration + * may be over an array, with an implicit join out to enclosing nesting + * levels. + * <p> + * Because of this, the same index interface must work at all nesting + * levels: at the top, and within arrays. This interface + * supports a usage model as follows:<pre><code> + * ColumnReaderIndex index = ... + * while (index.hasNext()) { + * index.next(); + * int hyperIndex = index.hyperVectorIndex(); + * int vectorOffset = index.offset(); + * }</code></pre> + * <p> + * When convenient, the following abbreviated form is also + * supported:<pre><code> + * ColumnReaderIndex index = ... + * while (index.next()) { + * int hyperIndex = index.hyperVectorIndex(); + * int vectorOffset = index.offset(); + * }</code></pre> + * <p> + * For a top-level index, the check of <tt>hasNext()</tt> and + * call to <tt>next()</tt> is done by the row set reader. For + * arrays, the call to <tt>hasNext()</tt> is done by the array + * reader. The call to <tt>next()</tt> is done by the scalar + * reader (for scalar arrays) or the array reader (for other + * arrays.) + * <p> + * The hyper-vector index has meaning only for top-level vectors, + * and is ignored by nested vectors. (Nested vectors work by navigating + * down from a top-level vector.) But, as noted above, any given + * reader does not know if it is at the top or nested level, instead + * it is the {@link VectorAccessor} abstraction that works out the + * nesting levels. */ public interface ColumnReaderIndex { - int batchIndex(); - int vectorIndex(); + + /** + * Ordinal index within the batch or array. Increments from -1. + * (The position before the first item.) + * Identifies the logical row number of top-level records, + * or the array element for arrays. Actual physical + * index may be different if an indirection layer is in use. + * + * @return logical read index + */ + + int logicalIndex(); + + /** + * When used with a hyper-vector (SV4) based batch, returns the + * index of the current batch within the hyper-batch. If this is + * a single batch, or a nested index, then always returns 0. + * + * @return batch index of the current row within the + * hyper-batch + */ + + int hyperVectorIndex(); + + /** + * Vector offset to read. For top-level vectors, the offset may be + * through an indirection (SV2 or SV4). For arrays, the offset is the + * absolute position, with the vector of the current array element. + * + * @return vector read index + */ + + int offset(); + + /** + * Advances the index to the next position. Used: + * <ul> + * <li>At the top level for normal readers or</li> + * <liAt a nested level for implicit join readers, and</li> + * <li>An each array level to iterate over arrays.</li> + * </ul> + * + * @return true if another value is available, false if EOF + */ + + boolean next(); + + /** + * Return the number of items that this index indexes: top-level record + * count for the root index; total element count for nested arrays. + * + * @return element count at this index level + */ + + int size(); } http://git-wip-us.apache.org/repos/asf/drill/blob/4f2182e4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ObjectReader.java ---------------------------------------------------------------------- diff --git a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ObjectReader.java b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ObjectReader.java index 9c53e58..e3527c5 100644 --- a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ObjectReader.java +++ b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ObjectReader.java @@ -29,32 +29,8 @@ package org.apache.drill.exec.vector.accessor; * {@see ObjectWriter> */ -public interface ObjectReader { - - /** - * The type of this reader. - * - * @return type of reader - */ - - ObjectType type(); +public interface ObjectReader extends ColumnReader { ScalarReader scalar(); - ScalarElementReader elements(); TupleReader tuple(); ArrayReader array(); - - /** - * Return the value of the underlying data as a Java object. - * Primarily for testing - * @return Java object that represents the underlying value - */ - - Object getObject(); - - /** - * Return the entire object as a string. Primarily for debugging. - * @return string representation of the object - */ - - String getAsString(); } http://git-wip-us.apache.org/repos/asf/drill/blob/4f2182e4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ScalarElementReader.java ---------------------------------------------------------------------- diff --git a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ScalarElementReader.java b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ScalarElementReader.java deleted file mode 100644 index d1f31a8..0000000 --- a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ScalarElementReader.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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; - -import java.math.BigDecimal; - -import org.joda.time.Period; - -/** - * Interface to access the values of an array column. In general, each - * vector implements just one of the get methods. Check the vector type - * to know which method to use. Though, generally, when writing test - * code, the type is known to the test writer. - * <p> - * Arrays allow random access to the values within the array. The index - * passed to each method is the index into the array for the current - * row and column. (This means that arrays are three dimensional: - * the usual (row, column) dimensions plus an array index dimension: - * (row, column, array index). - * <p> - * Note that the <tt>isNull()</tt> method is provided for completeness, - * but no Drill array allows null values at present. - * <p> - * {@see ScalarWriter} - */ - -public interface ScalarElementReader { - /** - * Describe the type of the value. This is a compression of the - * value vector type: it describes which method will return the - * vector value. - * @return the value type which indicates which get method - * is valid for the column - */ - - ValueType valueType(); - int size(); - - boolean isNull(int index); - int getInt(int index); - long getLong(int index); - double getDouble(int index); - String getString(int index); - byte[] getBytes(int index); - BigDecimal getDecimal(int index); - Period getPeriod(int index); - - Object getObject(int index); - String getAsString(int index); -} http://git-wip-us.apache.org/repos/asf/drill/blob/4f2182e4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ScalarReader.java ---------------------------------------------------------------------- diff --git a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ScalarReader.java b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ScalarReader.java index e1c26bf..5b09039 100644 --- a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ScalarReader.java +++ b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ScalarReader.java @@ -44,7 +44,7 @@ import org.joda.time.Period; * {@see ScalarWriter} */ -public interface ScalarReader { +public interface ScalarReader extends ColumnReader { /** * Describe the type of the value. This is a compression of the * value vector type: it describes which method will return the @@ -54,14 +54,6 @@ public interface ScalarReader { */ ValueType valueType(); - - /** - * Report if the column is null. Non-nullable columns always - * return <tt>false</tt>. - * @return true if the column value is null, false if the - * value is set - */ - boolean isNull(); int getInt(); long getLong(); double getDouble(); @@ -69,7 +61,4 @@ public interface ScalarReader { byte[] getBytes(); BigDecimal getDecimal(); Period getPeriod(); - - Object getObject(); - String getAsString(); } http://git-wip-us.apache.org/repos/asf/drill/blob/4f2182e4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/TupleReader.java ---------------------------------------------------------------------- diff --git a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/TupleReader.java b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/TupleReader.java index 8d691c3..c33f579 100644 --- a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/TupleReader.java +++ b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/TupleReader.java @@ -25,15 +25,33 @@ import org.apache.drill.exec.record.metadata.TupleMetadata; * by name or column index (as defined in the tuple schema.) * Also provides two generic methods to get the value as a * Java object or as a string. - * <p> - * {@see TupleWriter} + * + * @see {@link TupleWriter} */ -public interface TupleReader { - TupleMetadata schema(); +public interface TupleReader extends ColumnReader { + TupleMetadata tupleSchema(); int columnCount(); + /** + * Return a column reader by column index as reported by the + * associated metadata. + * + * @param colIndex column index + * @return reader for the column + * @throws IndexOutOfRangeException if the index is invalid + */ + ObjectReader column(int colIndex); + + /** + * Return a column reader by name. + * + * @param colIndex column name + * @return reader for the column, or <tt>null</tt> if no such + * column exists + */ + ObjectReader column(String colName); // Convenience methods @@ -46,9 +64,4 @@ public interface TupleReader { TupleReader tuple(String colName); ArrayReader array(int colIndex); ArrayReader array(String colName); - ScalarElementReader elements(int colIndex); - ScalarElementReader elements(String colName); - - Object getObject(); - String getAsString(); } http://git-wip-us.apache.org/repos/asf/drill/blob/4f2182e4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/UnsupportedConversionError.java ---------------------------------------------------------------------- diff --git a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/UnsupportedConversionError.java b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/UnsupportedConversionError.java new file mode 100644 index 0000000..dee2612 --- /dev/null +++ b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/UnsupportedConversionError.java @@ -0,0 +1,52 @@ +/* + * 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; + +import org.apache.drill.exec.record.metadata.ColumnMetadata; + +/** + * Raised when a column accessor reads or writes the value using the wrong + * Java type (which may indicate an data inconsistency in the input data.) + */ + +public class UnsupportedConversionError extends UnsupportedOperationException { + + private static final long serialVersionUID = 1L; + + private UnsupportedConversionError(String message) { + super(message); + } + + public static UnsupportedConversionError readError(ColumnMetadata schema, String javaType) { + return new UnsupportedConversionError( + String.format("Column `%s`: Unsupported conversion from Drill type %s to Java type %s", + schema.name(), schema.type().name(), javaType)); + } + + public static UnsupportedConversionError writeError(ColumnMetadata schema, String javaType) { + return new UnsupportedConversionError( + String.format("Column `%s`: Unsupported conversion from Java type %s to Drill type %s", + schema.name(), schema.type().name(), javaType)); + } + + public static UnsupportedConversionError nullError(ColumnMetadata schema) { + return new UnsupportedConversionError( + String.format("Column `%s`: Type %s %s is not nullable", + schema.name(), schema.mode().name(), schema.type().name())); + } +} http://git-wip-us.apache.org/repos/asf/drill/blob/4f2182e4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ValueType.java ---------------------------------------------------------------------- diff --git a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ValueType.java b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ValueType.java index e6687dc..5059977 100644 --- a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ValueType.java +++ b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/ValueType.java @@ -27,5 +27,60 @@ package org.apache.drill.exec.vector.accessor; */ public enum ValueType { - INTEGER, LONG, DOUBLE, STRING, BYTES, DECIMAL, PERIOD + + /** + * The value is set from an integer: TINYINT, + * SMALLINT, INT, UINT1, and UINT2. + */ + + INTEGER, + + /** + * The value set from a long: BIGINT and + * UINT4. + */ + + LONG, + + /** + * Type is set from a double: FLOAT4 and FLOAT8. + */ + DOUBLE, + + /** + * The value can be set from a string (for convenience). + * VARCHAR and VAR16CHAR. + */ + + STRING, + + /** + * The value is set from a byte buffer. VARCHAR (in production + * code), VAR16CHAR, VARBINARY. + */ + + BYTES, + + /** + * The value is set from a BigDecimal: any of Drill's decimal + * types. + */ + + DECIMAL, + + /** + * The value is set from a Period. Any of Drill's date/time + * types. (Note: there is a known bug in which Drill incorrectly + * differentiates between local date/times (those without a timezone) + * and absolute date/times (those with a timezone.) Caveat emptor. + */ + + PERIOD, + + /** + * The value has no type. This is typically a dummy writer used + * for unprojected columns. + */ + + NULL } http://git-wip-us.apache.org/repos/asf/drill/blob/4f2182e4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/impl/VectorPrinter.java ---------------------------------------------------------------------- diff --git a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/impl/VectorPrinter.java b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/impl/VectorPrinter.java new file mode 100644 index 0000000..45847bc --- /dev/null +++ b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/impl/VectorPrinter.java @@ -0,0 +1,72 @@ +/* + * 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.impl; + +import org.apache.drill.exec.vector.UInt4Vector; +import org.apache.drill.exec.vector.ValueVector; +import org.apache.drill.exec.vector.VarCharVector; + +import com.google.common.base.Charsets; + +/** + * Handy tool to visualize string and offset vectors for + * debugging. + */ + +public class VectorPrinter { + + public static void printOffsets(UInt4Vector vector, int start, int length) { + header(vector, start, length); + for (int i = start, j = 0; j < length; i++, j++) { + if (j > 0) { + System.out.print(" "); + } + System.out.print(vector.getAccessor().get(i)); + } + System.out.print("], addr = "); + System.out.println(vector.getBuffer().addr()); + } + + public static void printStrings(VarCharVector vector, int start, int length) { + printOffsets(vector.getOffsetVector(), start, length + 1); + header(vector, start, length); + System.out.println(); + for (int i = start, j = 0; j < length; i++, j++) { + System.out.print(" "); + System.out.print(i); + System.out.print(": \""); + System.out.print(stringAt(vector, i)); + System.out.println("\""); + } + System.out.println("]"); + } + + public static void header(ValueVector vector, int start, int length) { + System.out.print(vector.getClass()); + System.out.print(": ("); + System.out.print(start); + System.out.print(" - "); + System.out.print(start + length - 1); + System.out.print("): ["); + } + + public static String stringAt(VarCharVector vector, int i) { + return new String(vector.getAccessor().get(i), Charsets.UTF_8); + } + +} http://git-wip-us.apache.org/repos/asf/drill/blob/4f2182e4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/package-info.java ---------------------------------------------------------------------- diff --git a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/package-info.java b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/package-info.java index c90a734..990ab13 100644 --- a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/package-info.java +++ b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/package-info.java @@ -161,6 +161,190 @@ * the same interface supported the original mutator-based implementation and * the revised Netty-based implementation. The benefit, however, is stark; * the direct-to-Netty version is up to 4x faster (for repeated types). + * + * <h4>Tuple Model</h4> + * + * Drill has the idea of row and of a map. (A Drill map is much like a "struct": + * every instance of the "map" must have the same columns.) Both are instances + * of the relational concept of a "tuple." In relational theory, a tuple is + * a collection of values in which each value has a name and a position. The + * name is for the user, the position (index) allows efficient implementation. + * <p> + * Drill is unusual among query and DB engines in that it does not normally + * use indexes. The reason is easy to understand. Suppose two files contain + * columns a and b. File 1, read by minor fragment 0, contains the columns in + * the order (a, b). But, file 2, read by minor fragment 1, contains the columns + * in the order (b, a). Drill considers this the same schema. Since column + * order can vary, Drill has avoided depending on column order. (But, only + * partly; many bugs have cropped up because some parts of the code do + * require common ordering.) + * <p> + * Here we observe that column order varies only across fragments. We have + * control of the column order within our own fragment. (We can coerce varying + * order into a desired order. If the above two files are read by the same + * scan operator, then the first file sets the order at (a, b), and the second + * files (b, a) order can be coerced into the (a, b) order. + * <p> + * Given this insight, the readers and writers here promote position to a + * first-class concept. Code can access columns by name (for convenience, + * especially in testing) or by position (for efficiency.) + * <p> + * Further, it is often convenient to fetch a column accessor (reader or + * writer) once, then cache it. The design here ensures that such caching works + * well. The goal is that, eventually, operators will code-generate references + * to cached readers and writers instead of generating code that works directly + * with the vectors. + * + * <h4>Lists and Unions</h4> + * + * Drill provides a List and a Union type. These types are incomplete, buggy + * and ill-supported by Drill's operators. However, they are key to Drill's + * JSON-powered, schema-free marketing message. Thus, we must support them + * in the reader/writer level even if they are broken and under-used elsewhere + * in Drill. (If we did not support them, then the JSON readers could not use + * the new model, and we'd have to support both the old and new versions, which + * would create a bigger mess than we started with.) + * <p> + * Drill's other types have a more-or-less simple mapping to the relational + * model, allowing simple reader and writer interfaces. But, the Union and List + * types are not a good fit and cause a very large amount of complexity in the + * reader and writer models. + * <p> + * A Union is just that: it is a container for a variety of typed vectors. It + * is like a "union" in C: it has members for each type, but only one type is + * in use at any one time. However, unlike C, the implementation is more like + * a C "struct" every vector takes space or every row, even if no value is stored + * in that row. That is, a Drill union is as if a naive C programmer used a + * "struct" when s/he should have used a union. + * <p> + * Unions are designed to evolve dynamically as data is read. Suppose we read + * the following JSON:<pre></code> + * {a: 10} {a: "foo"} {a: null} {a: 12.34} + * </code></pre> + * Here, we discover the need for an Int type, then a Varchar, then mark a + * value as null and finally a Float. The union adds the desired types as we + * request them. The writer mimics this behavior, using a listener to do the + * needed vector work. + * <p> + * Further, a union can be null. It carries a types vector that indicates the + * type of each row. A zero-value indicates that the union as a whole is null. + * In this case, null means no value, is is not, say, a null Int or null + * Varchar: it is simply null (as in JSON). Since at most one vector within the union + * carries a value, the element vectors must also be nullable. This means + * that a union has two null bits: one or the union, the other for the + * selected type. It is not clear what Drill semantics are supposed to be. Here + * the writers assume that either the whole union is null, or that exactly one + * member is non-null. Readers are more paranoid: they assume each member is null + * if either the union is null or the member itself is null. (Yes, a bit of a + * mess...) + * <p> + * The current union vector format is highly inefficient. + * If the union concept is needed, then it should + * be redesigned, perhaps as a variable-width vector in which each entry + * consists of a type/value pair. (For variable-width values such as + * strings, the implementation would be a triple of (type, length, + * value). The API here is designed to abstract away the implementation + * and should work equally well for the current "union" implementation and + * the possible "variant" implementation. As a result, when changing the + * API, avoid introducing methods that assume an implementation. + * <p> + * Lists add another layer of complexity. A list is, logically, a repeated + * union. But, for whatever historical reasons, a List can be other things + * as well. First, it can have no type at all: a list of nothing. This likely + * has no meaning, but the semantics of the List vector allow it. Second, the + * List can be an array of a single type in which each entry can be null. + * (Normal Repeated types can have an empty array for a row, but cannot have + * a null entry. Lists can have either an empty array or a null array in + * order to model the JSON <tt>null</tt> and <tt>[]</tt> cases.) + * <p> + * When a List has a single type, it stores the backing vector directly within + * the List. But, a list can also be a list of unions. In this case, the List + * stores a union vector as its backing vector. Here, we now have three ways + * to indicate null: the List's bits vector, the type vector in the union, and + * the bits vector in each element vector. Again, the writer assumes that + * if the List vector is null, the entire value for that row is null. The reader + * is again paranoid and handles all three nullable states. (Again, a huge + * mess.) + * <p> + * The readers can provide a nice API for these cases since we know the List + * format up front. They can present the list as either a nullable array of + * a single type, or as an array of unions. + * <p> + * Writers have more of a challenge. If we knew that a List was being used as + * a list of, say, Nullable Int, we could present the List as an array writer + * with Int elements. But, the List allows dynamic type addition, as with unions. + * (In the case of the list, it has internal special handling for the single vs. + * many type case.) + * <p> + * To isolate the client from the list representation, it is simpler to always + * present a List an array of variants. But, this is awkward in the single-type + * case. The solution is to use metadata. If the metadata promises to use only + * a single type, the writer can use the nullable array of X format. If the + * metadata says to use a union (the default), then the List is presented as + * an array of unions, even when the list has 0 or 1 member types. (The + * complexity here is excessive: Drill should really redesign this feature to make + * it simpler and to better fit the relational model.) + * + * <h4>Vector Evolution</h4> + * + * Drill uses value vector classes created during the rush to ship Drill 1.0. + * They are not optimal: the key value is that the vectors work. + * <p> + * The Apache Arrow project created a refined version of the vector classes. + * Much talk has occurred about ripping out Drill's implementation to use + * Arrow instead. + * <p> + * However, even Arrow has limits: + * <ul> + * <li>Like Drill, it uses twice the number of positions in the offset vector + * as for the values vector. (Drill allocates power-of-two sizes. The offset + * vector has one more entry than values. With a power-of-two number of values, + * offsets are rounded to the next power of two.)</li> + * <li>Like Drill before this work, Arrow does not manage vector sizes; it + * allows vectors to grow without bound, causing the memory problems that this + * project seeks to resolve.</li> + * <li>Like Drill, Arrow implements unions as a space-inefficient collection + * of vectors format.</li> + * </ul> + * If we learn from the above, we might want to create a Value Vectors 2.0 + * based on the following concepts: + * <ul> + * <li>Store vector values as a chain of fixed-size buffers. This avoids + * memory fragmentation, makes memory allocation much more efficient, is + * easier on the client, and avoids internal fragmentation.</li> + * <li>Store offsets as the end value, not the start value. This eliminates + * the extra offset position, simplifies indexing, and can save on internal + * memory fragmentation.</li> + * <li>Store unions using "variant encoding" as described above.</li> + * </ul> + * Such changes would be a huge project if every operator continued to work + * directly with vectors and memory buffers. In fact, the cost would be so + * high that these improvements might never be done. + * <p> + * Therefore, a goal of this reader/writer layer is to isolate the operators + * from vector implementation. For this to work, the accessors must be at least + * as efficient as direct vector access. (They are now more efficient.) + * <p> + * Once all operators use this layer, a switch to Arrow, or an evolution toward + * Value Vectors 2.0 will be much easier. Simply change the vector format and + * update the reader and writer implementations. The rest of the code will + * remain unchanged. (Note, to achieve this goal, it is important to carefully + * design the accessor API [interfaces] to hide implementation details.) + * + * <h4>Simpler Reader API</h4> + * + * A key value of Drill is the ability for users to add custom record readers. + * But, at present, doing so is very complex because the developer must know + * quite a bit about Drill internals. At this level, they must know how to + * allocate vectors, how to write to each kind of vector, how to keep track + * of array sizes, how to set the vector and batch row counts, and more. In + * general, there is only one right way to do this work. (Though some readers + * use the old-style vector writers, others work with direct memory instead + * of with vectors, and so on.) + * <p> + * This layer handles all that work, providing a simple API that encourages + * more custom readers because the work to create the readers becomes far + * simpler. (Other layers tackle other parts of the problem as well.) */ package org.apache.drill.exec.vector.accessor; http://git-wip-us.apache.org/repos/asf/drill/blob/4f2182e4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/reader/AbstractArrayReader.java ---------------------------------------------------------------------- diff --git a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/reader/AbstractArrayReader.java b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/reader/AbstractArrayReader.java deleted file mode 100644 index 7fb0c9d..0000000 --- a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/reader/AbstractArrayReader.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * 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.reader; - -import org.apache.drill.exec.vector.UInt4Vector.Accessor; -import org.apache.drill.exec.vector.accessor.ArrayReader; -import org.apache.drill.exec.vector.accessor.ColumnReaderIndex; -import org.apache.drill.exec.vector.accessor.ObjectReader; -import org.apache.drill.exec.vector.accessor.ObjectType; -import org.apache.drill.exec.vector.accessor.ScalarElementReader; -import org.apache.drill.exec.vector.accessor.TupleReader; -import org.apache.drill.exec.vector.complex.RepeatedValueVector; - -/** - * Reader for an array-valued column. This reader provides access to specific - * array members via an array index. This is an abstract base class; - * subclasses are generated for each repeated value vector type. - */ - -public abstract class AbstractArrayReader implements ArrayReader { - - /** - * Object representation of an array reader. - */ - - public static class ArrayObjectReader extends AbstractObjectReader { - - private AbstractArrayReader arrayReader; - - public ArrayObjectReader(AbstractArrayReader arrayReader) { - this.arrayReader = arrayReader; - } - - @Override - public void bindIndex(ColumnReaderIndex index) { - arrayReader.bindIndex(index); - } - - @Override - public ObjectType type() { - return ObjectType.ARRAY; - } - - @Override - public ArrayReader array() { - return arrayReader; - } - - @Override - public ScalarElementReader elements() { - return arrayReader.elements(); - } - - @Override - public Object getObject() { - return arrayReader.getObject(); - } - - @Override - public String getAsString() { - return arrayReader.getAsString(); - } - - @Override - public void reposition() { - arrayReader.reposition(); - } - } - - public static class BaseElementIndex { - private final ColumnReaderIndex base; - protected int startOffset; - protected int length; - - public BaseElementIndex(ColumnReaderIndex base) { - this.base = base; - } - - public int batchIndex() { - return base.batchIndex(); - } - - public void reset(int startOffset, int length) { - assert length >= 0; - assert startOffset >= 0; - this.startOffset = startOffset; - this.length = length; - } - - public int size() { return length; } - - public int elementIndex(int index) { - if (index < 0 || length <= index) { - throw new IndexOutOfBoundsException("Index = " + index + ", length = " + length); - } - return startOffset + index; - } - } - - private final Accessor accessor; - private final VectorAccessor vectorAccessor; - protected ColumnReaderIndex baseIndex; - protected BaseElementIndex elementIndex; - - public AbstractArrayReader(RepeatedValueVector vector) { - accessor = vector.getOffsetVector().getAccessor(); - vectorAccessor = null; - } - - public AbstractArrayReader(VectorAccessor vectorAccessor) { - accessor = null; - this.vectorAccessor = vectorAccessor; - } - - public void bindIndex(ColumnReaderIndex index) { - baseIndex = index; - if (vectorAccessor != null) { - vectorAccessor.bind(index); - } - } - - private Accessor accessor() { - if (accessor != null) { - return accessor; - } - return ((RepeatedValueVector) (vectorAccessor.vector())).getOffsetVector().getAccessor(); - } - - public void reposition() { - final int index = baseIndex.vectorIndex(); - final Accessor curAccesssor = accessor(); - final int startPosn = curAccesssor.get(index); - elementIndex.reset(startPosn, curAccesssor.get(index + 1) - startPosn); - } - - @Override - public int size() { return elementIndex.size(); } - - @Override - public ScalarElementReader elements() { - throw new UnsupportedOperationException(); - } - - @Override - public ObjectReader entry(int index) { - throw new UnsupportedOperationException(); - } - - @Override - public TupleReader tuple(int index) { - return entry(index).tuple(); - } - - @Override - public ArrayReader array(int index) { - return entry(index).array(); - } - - @Override - public ObjectReader entry() { - throw new UnsupportedOperationException(); - } - - @Override - public TupleReader tuple() { - return entry().tuple(); - } - - @Override - public ArrayReader array() { - return entry().array(); - } -} http://git-wip-us.apache.org/repos/asf/drill/blob/4f2182e4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/reader/AbstractObjectReader.java ---------------------------------------------------------------------- diff --git a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/reader/AbstractObjectReader.java b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/reader/AbstractObjectReader.java index 59a066e..2a80179 100644 --- a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/reader/AbstractObjectReader.java +++ b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/reader/AbstractObjectReader.java @@ -17,18 +17,18 @@ */ package org.apache.drill.exec.vector.accessor.reader; +import org.apache.drill.exec.record.metadata.ColumnMetadata; import org.apache.drill.exec.vector.accessor.ArrayReader; -import org.apache.drill.exec.vector.accessor.ColumnReaderIndex; +import org.apache.drill.exec.vector.accessor.ColumnReader; import org.apache.drill.exec.vector.accessor.ObjectReader; -import org.apache.drill.exec.vector.accessor.ScalarElementReader; +import org.apache.drill.exec.vector.accessor.ObjectType; import org.apache.drill.exec.vector.accessor.ScalarReader; import org.apache.drill.exec.vector.accessor.TupleReader; public abstract class AbstractObjectReader implements ObjectReader { - public abstract void bindIndex(ColumnReaderIndex index); - - public void reposition() { } + @Override + public ColumnMetadata schema() { return reader().schema(); } @Override public ScalarReader scalar() { @@ -45,8 +45,13 @@ public abstract class AbstractObjectReader implements ObjectReader { throw new UnsupportedOperationException(); } + public abstract ReaderEvents events(); + + public abstract ColumnReader reader(); + @Override - public ScalarElementReader elements() { - throw new UnsupportedOperationException(); - } + public boolean isNull() { return reader().isNull(); } + + @Override + public ObjectType type() { return reader().type(); } } http://git-wip-us.apache.org/repos/asf/drill/blob/4f2182e4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/reader/AbstractScalarReader.java ---------------------------------------------------------------------- diff --git a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/reader/AbstractScalarReader.java b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/reader/AbstractScalarReader.java new file mode 100644 index 0000000..203de23 --- /dev/null +++ b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/reader/AbstractScalarReader.java @@ -0,0 +1,205 @@ +/* + * 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.reader; + +import java.math.BigDecimal; + +import org.apache.drill.exec.record.metadata.ColumnMetadata; +import org.apache.drill.exec.vector.accessor.ColumnReader; +import org.apache.drill.exec.vector.accessor.ColumnReaderIndex; +import org.apache.drill.exec.vector.accessor.ObjectType; +import org.apache.drill.exec.vector.accessor.ScalarReader; +import org.apache.drill.exec.vector.accessor.UnsupportedConversionError; +import org.apache.drill.exec.vector.accessor.ValueType; +import org.apache.drill.exec.vector.accessor.impl.AccessorUtilities; +import org.joda.time.Period; + +public abstract class AbstractScalarReader implements ScalarReader, ReaderEvents { + + public static class ScalarObjectReader extends AbstractObjectReader { + + private AbstractScalarReader scalarReader; + + public ScalarObjectReader(AbstractScalarReader scalarReader) { + this.scalarReader = scalarReader; + } + + @Override + public ScalarReader scalar() { + return scalarReader; + } + + @Override + public Object getObject() { + return scalarReader.getObject(); + } + + @Override + public String getAsString() { + return scalarReader.getAsString(); + } + + @Override + public ReaderEvents events() { return scalarReader; } + + @Override + public ColumnReader reader() { return scalarReader; } + } + + public static class NullReader extends AbstractScalarReader { + + protected final ColumnMetadata schema; + + protected NullReader(ColumnMetadata schema) { + this.schema = schema; + } + + @Override + public ValueType valueType() { return ValueType.NULL; } + + @Override + public boolean isNull() { return true; } + + @Override + public void bindIndex(ColumnReaderIndex rowIndex) { } + + @Override + public ColumnMetadata schema() { return schema; } + } + + protected ColumnReaderIndex vectorIndex; + protected NullStateReader nullStateReader; + + public static ScalarObjectReader nullReader(ColumnMetadata schema) { + return new ScalarObjectReader(new NullReader(schema)); + } + + @Override + public void bindIndex(ColumnReaderIndex rowIndex) { + vectorIndex = rowIndex; + nullStateReader.bindIndex(rowIndex); + } + + @Override + public void bindNullState(NullStateReader nullStateReader) { + this.nullStateReader = nullStateReader; + } + + @Override + public ObjectType type() { return ObjectType.SCALAR; } + + @Override + public NullStateReader nullStateReader() { return nullStateReader; } + + @Override + public void reposition() { } + + @Override + public boolean isNull() { + return nullStateReader.isNull(); + } + + protected UnsupportedConversionError conversionError(String javaType) { + return UnsupportedConversionError.writeError(schema(), javaType); + } + + @Override + public int getInt() { + throw conversionError("int"); + } + + @Override + public long getLong() { + throw conversionError("long"); + } + + @Override + public double getDouble() { + throw conversionError("double"); + } + + @Override + public String getString() { + throw conversionError("String"); + } + + @Override + public byte[] getBytes() { + throw conversionError("bytes"); + } + + @Override + public BigDecimal getDecimal() { + throw conversionError("Decimal"); + } + + @Override + public Period getPeriod() { + throw conversionError("Period"); + } + + @Override + public Object getObject() { + if (isNull()) { + return null; + } + switch (valueType()) { + case BYTES: + return getBytes(); + case DECIMAL: + return getDecimal(); + case DOUBLE: + return getDouble(); + case INTEGER: + return getInt(); + case LONG: + return getLong(); + case PERIOD: + return getPeriod(); + case STRING: + return getString(); + default: + throw new IllegalStateException("Unexpected type: " + valueType()); + } + } + + @Override + public String getAsString() { + if (isNull()) { + return "null"; + } + switch (valueType()) { + case BYTES: + return AccessorUtilities.bytesToString(getBytes()); + case DOUBLE: + return Double.toString(getDouble()); + case INTEGER: + return Integer.toString(getInt()); + case LONG: + return Long.toString(getLong()); + case STRING: + return "\"" + getString() + "\""; + case DECIMAL: + return getDecimal().toPlainString(); + case PERIOD: + return getPeriod().normalizedStandard().toString(); + default: + throw new IllegalArgumentException("Unsupported type " + valueType()); + } + } +} http://git-wip-us.apache.org/repos/asf/drill/blob/4f2182e4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/reader/AbstractTupleReader.java ---------------------------------------------------------------------- diff --git a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/reader/AbstractTupleReader.java b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/reader/AbstractTupleReader.java index 0429f3e..2c09e5b 100644 --- a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/reader/AbstractTupleReader.java +++ b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/reader/AbstractTupleReader.java @@ -20,12 +20,11 @@ package org.apache.drill.exec.vector.accessor.reader; import java.util.ArrayList; import java.util.List; -import org.apache.drill.exec.record.metadata.TupleMetadata; import org.apache.drill.exec.vector.accessor.ArrayReader; +import org.apache.drill.exec.vector.accessor.ColumnReader; import org.apache.drill.exec.vector.accessor.ColumnReaderIndex; import org.apache.drill.exec.vector.accessor.ObjectReader; import org.apache.drill.exec.vector.accessor.ObjectType; -import org.apache.drill.exec.vector.accessor.ScalarElementReader; import org.apache.drill.exec.vector.accessor.ScalarReader; import org.apache.drill.exec.vector.accessor.TupleReader; @@ -34,26 +33,15 @@ import org.apache.drill.exec.vector.accessor.TupleReader; * column using either a name or a numeric index. */ -public abstract class AbstractTupleReader implements TupleReader { +public abstract class AbstractTupleReader implements TupleReader, ReaderEvents { public static class TupleObjectReader extends AbstractObjectReader { - private AbstractTupleReader tupleReader; + private final AbstractTupleReader tupleReader; public TupleObjectReader(AbstractTupleReader tupleReader) { this.tupleReader = tupleReader; } - - @Override - public void bindIndex(ColumnReaderIndex index) { - tupleReader.bindIndex(index); - } - - @Override - public ObjectType type() { - return ObjectType.TUPLE; - } - @Override public TupleReader tuple() { return tupleReader; @@ -70,30 +58,42 @@ public abstract class AbstractTupleReader implements TupleReader { } @Override - public void reposition() { - tupleReader.reposition(); - } + public ReaderEvents events() { return tupleReader; } + + @Override + public ColumnReader reader() { return tupleReader; } } - protected final TupleMetadata schema; private final AbstractObjectReader readers[]; + protected NullStateReader nullStateReader; - protected AbstractTupleReader(TupleMetadata schema, AbstractObjectReader readers[]) { - this.schema = schema; + protected AbstractTupleReader(AbstractObjectReader readers[]) { this.readers = readers; } + @Override + public ObjectType type() { return ObjectType.TUPLE; } + + @Override public void bindIndex(ColumnReaderIndex index) { for (int i = 0; i < readers.length; i++) { - readers[i].bindIndex(index); + readers[i].events().bindIndex(index); } } @Override - public TupleMetadata schema() { return schema; } + public void bindNullState(NullStateReader nullStateReader) { + this.nullStateReader = nullStateReader; + } + + @Override + public NullStateReader nullStateReader() { return nullStateReader; } @Override - public int columnCount() { return schema().size(); } + public boolean isNull() { return nullStateReader.isNull(); } + + @Override + public int columnCount() { return tupleSchema().size(); } @Override public ObjectReader column(int colIndex) { @@ -102,13 +102,23 @@ public abstract class AbstractTupleReader implements TupleReader { @Override public ObjectReader column(String colName) { - int index = schema.index(colName); + int index = tupleSchema().index(colName); if (index == -1) { return null; } return readers[index]; } @Override + public ObjectType type(int colIndex) { + return column(colIndex).type(); + } + + @Override + public ObjectType type(String colName) { + return column(colName).type(); + } + + @Override public ScalarReader scalar(int colIndex) { return column(colIndex).scalar(); } @@ -139,28 +149,9 @@ public abstract class AbstractTupleReader implements TupleReader { } @Override - public ObjectType type(int colIndex) { - return column(colIndex).type(); - } - - @Override - public ObjectType type(String colName) { - return column(colName).type(); - } - - @Override - public ScalarElementReader elements(int colIndex) { - return column(colIndex).elements(); - } - - @Override - public ScalarElementReader elements(String colName) { - return column(colName).elements(); - } - public void reposition() { for (int i = 0; i < columnCount(); i++) { - readers[i].reposition(); + readers[i].events().reposition(); } } @@ -176,14 +167,14 @@ public abstract class AbstractTupleReader implements TupleReader { @Override public String getAsString() { StringBuilder buf = new StringBuilder(); - buf.append("("); + buf.append("{"); for (int i = 0; i < columnCount(); i++) { if (i > 0) { buf.append( ", " ); } buf.append(readers[i].getAsString()); } - buf.append(")"); + buf.append("}"); return buf.toString(); } } http://git-wip-us.apache.org/repos/asf/drill/blob/4f2182e4/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/reader/ArrayReaderImpl.java ---------------------------------------------------------------------- diff --git a/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/reader/ArrayReaderImpl.java b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/reader/ArrayReaderImpl.java new file mode 100644 index 0000000..7f2bf39 --- /dev/null +++ b/exec/vector/src/main/java/org/apache/drill/exec/vector/accessor/reader/ArrayReaderImpl.java @@ -0,0 +1,357 @@ +/* + * 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.reader; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.drill.exec.record.metadata.ColumnMetadata; +import org.apache.drill.exec.vector.accessor.ArrayReader; +import org.apache.drill.exec.vector.accessor.ColumnReader; +import org.apache.drill.exec.vector.accessor.ColumnReaderIndex; +import org.apache.drill.exec.vector.accessor.ObjectReader; +import org.apache.drill.exec.vector.accessor.ObjectType; +import org.apache.drill.exec.vector.accessor.ScalarReader; +import org.apache.drill.exec.vector.accessor.TupleReader; + +/** + * Reader for an array-valued column. This reader provides access to specific + * array members via an array index. This class implements all arrays. The + * behavior for specific array types (scalar, map, lists, etc.) is provided + * through composition. + */ + +public class ArrayReaderImpl implements ArrayReader, ReaderEvents { + + /** + * Object representation of an array reader. + */ + + public static class ArrayObjectReader extends AbstractObjectReader { + + private ArrayReaderImpl arrayReader; + + public ArrayObjectReader(ArrayReaderImpl arrayReader) { + this.arrayReader = arrayReader; + } + + @Override + public ArrayReader array() { + return arrayReader; + } + + @Override + public Object getObject() { + return arrayReader.getObject(); + } + + @Override + public String getAsString() { + return arrayReader.getAsString(); + } + + @Override + public ReaderEvents events() { return arrayReader; } + + @Override + public ColumnReader reader() { return arrayReader; } + } + + /** + * Index into the vector of elements for a repeated vector. + * Indexes elements relative to the array. That is, if an array + * has five elements, the index here tracks elements 0..4. + * The actual vector index is given as the start offset plus the + * offset into the array. + * <p> + * Indexes allow random or sequential access. Random access is more + * typical for scalar arrays, while sequential access can be more convenient + * for tuple arrays. + */ + + public static class ElementReaderIndex implements ColumnReaderIndex { + protected final ColumnReaderIndex base; + protected int startOffset; + protected int length; + protected int position; + + public ElementReaderIndex(ColumnReaderIndex base) { + this.base = base; + } + + @Override + public int hyperVectorIndex() { return 0; } + + /** + * Reposition this array index for a new array given the array start + * offset and length. + * + * @param startOffset first location within the array's + * offset vector + * @param length number of offset vector locations associated with this + * array + */ + + public void reset(int startOffset, int length) { + assert length >= 0; + assert startOffset >= 0; + this.startOffset = startOffset; + this.length = length; + position = -1; + } + + public void rewind() { + position = -1; + } + + @Override + public int size() { return length; } + + /** + * Given a 0-based index relative to the current array, return an absolute offset + * vector location for the array value. + * + * @param index 0-based index into the current array + * @return absolute offset vector location for the array value + */ + + @Override + public int offset() { + if (position < 0 || length <= position) { + throw new IndexOutOfBoundsException("Index = " + position + ", length = " + length); + } + return startOffset + position; + } + + @Override + public boolean next() { + if (++position < length) { + return true; + } + position = length; + return false; + } + + /** + * Set the current iterator location to the given index offset. + * + * @param index 0-based index into the current array + */ + + public void set(int index) { + if (index < 0 || length < index) { + throw new IndexOutOfBoundsException("Index = " + index + ", length = " + length); + } + position = index; + } + + @Override + public int logicalIndex() { return position; } + } + + private final ColumnMetadata schema; + private final VectorAccessor arrayAccessor; + private final OffsetVectorReader offsetReader; + private final AbstractObjectReader elementReader; + protected ElementReaderIndex elementIndex; + protected NullStateReader nullStateReader; + + public ArrayReaderImpl(ColumnMetadata schema, VectorAccessor va, + AbstractObjectReader elementReader) { + this.schema = schema; + arrayAccessor = va; + this.elementReader = elementReader; + offsetReader = new OffsetVectorReader( + VectorAccessors.arrayOffsetVectorAccessor(va)); + } + + /** + * Build a scalar array for a Repeated type. Such arrays are not nullable. + * + * @param arrayAccessor vector accessor for the repeated vector that holds + * the scalar values + * @param elementReader scalar reader used to decode each scalar value + * @return object reader which wraps the scalar array reader + */ + + public static ArrayObjectReader buildScalar(ColumnMetadata schema, + VectorAccessor arrayAccessor, + BaseScalarReader elementReader) { + + // Reader is bound to the values vector inside the nullable vector. + + elementReader.bindVector(schema, + VectorAccessors.arrayDataAccessor(arrayAccessor)); + + // The scalar array element can't be null. + + elementReader.bindNullState(NullStateReaders.REQUIRED_STATE_READER); + + // Create the array, giving it an offset vector reader based on the + // repeated vector's offset vector. + + ArrayReaderImpl arrayReader = new ArrayReaderImpl(schema, arrayAccessor, + new AbstractScalarReader.ScalarObjectReader(elementReader)); + + // The array itself can't be null. + + arrayReader.bindNullState(NullStateReaders.REQUIRED_STATE_READER); + + // Wrap it all in an object reader. + + return new ArrayObjectReader(arrayReader); + } + + /** + * Build a repeated map reader. + * + * @param arrayAccessor vector accessor for the repeated map vector + * @param elementReader tuple reader that provides access to each + * tuple in the array + * @return object reader that wraps the map array reader + */ + + public static AbstractObjectReader buildTuple(ColumnMetadata schema, + VectorAccessor arrayAccessor, + AbstractObjectReader elementReader) { + + // Create the array reader over the map vector. + + ArrayReaderImpl arrayReader = new ArrayReaderImpl(schema, arrayAccessor, elementReader); + + // The array itself can't be null. + + arrayReader.bindNullState(NullStateReaders.REQUIRED_STATE_READER); + + // Wrap it all in an object reader. + + return new ArrayObjectReader(arrayReader); + } + + @Override + public void bindIndex(ColumnReaderIndex index) { + arrayAccessor.bind(index); + offsetReader.bindIndex(index); + nullStateReader.bindIndex(index); + elementIndex = new ElementReaderIndex(index); + elementReader.events().bindIndex(elementIndex); + } + + @Override + public void bindNullState(NullStateReader nullStateReader) { + this.nullStateReader = nullStateReader; + } + + @Override + public ObjectType type() { return ObjectType.ARRAY; } + + @Override + public ColumnMetadata schema() { return schema; } + + @Override + public NullStateReader nullStateReader() { return nullStateReader; } + + @Override + public boolean isNull() { return nullStateReader.isNull(); } + + @Override + public void reposition() { + long entry = offsetReader.getEntry(); + elementIndex.reset((int) (entry >> 32), (int) (entry & 0xFFFF_FFFF)); + } + + @Override + public boolean next() { + if (! elementIndex.next()) { + return false; + } + elementReader.events().reposition(); + return true; + } + + public ColumnReaderIndex elementIndex() { return elementIndex; } + + @Override + public int size() { return elementIndex.size(); } + + @Override + public void setPosn(int posn) { + elementIndex.set(posn); + elementReader.events().reposition(); + } + + @Override + public void rewind() { + elementIndex.rewind(); + } + + @Override + public ObjectReader entry() { return elementReader; } + + @Override + public ObjectType entryType() { return elementReader.type(); } + + @Override + public ScalarReader scalar() { + return entry().scalar(); + } + + @Override + public TupleReader tuple() { + return entry().tuple(); + } + + @Override + public ArrayReader array() { + return entry().array(); + } + + @Override + public Object getObject() { + + // Simple: return elements as an object list. + // If really needed, could return as a typed array, but that + // is a bit of a hassle. + + rewind(); + List<Object> elements = new ArrayList<>(); + while (next()) { + elements.add(elementReader.getObject()); + } + return elements; + } + + @Override + public String getAsString() { + if (isNull()) { + return "null"; + } + rewind(); + StringBuilder buf = new StringBuilder(); + buf.append("["); + int i = 0; + while (next()) { + if (i++ > 0) { + buf.append( ", " ); + } + buf.append(elementReader.getAsString()); + } + buf.append("]"); + return buf.toString(); + } +}