blob: c0f4afcd9dd8dd389925874557fc8dc8248ca71b [file] [log] [blame]
/*
* 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.Array;
import java.math.BigDecimal;
import org.apache.drill.exec.record.metadata.ColumnMetadata;
import org.apache.drill.exec.vector.accessor.ColumnWriterIndex;
import org.apache.drill.exec.vector.accessor.ScalarWriter;
import org.apache.drill.exec.vector.accessor.writer.AbstractArrayWriter.BaseArrayWriter;
import org.apache.drill.exec.vector.accessor.writer.AbstractScalarWriterImpl.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(); }
@Override
public final void prevElement() { prev(); }
}
private final ScalarWriter elementWriter;
public ScalarArrayWriter(ColumnMetadata schema,
RepeatedValueVector vector, BaseScalarWriter baseElementWriter) {
super(schema, vector.getOffsetVector(),
new ScalarObjectWriter(baseElementWriter));
// Save the writer from the scalar object writer created above
// which may have wrapped the element writer in a type convertor.
this.elementWriter = elementObjWriter.scalar();
}
public static ArrayObjectWriter build(ColumnMetadata schema,
RepeatedValueVector repeatedVector, BaseScalarWriter baseElementWriter) {
return new ArrayObjectWriter(
new ScalarArrayWriter(schema, repeatedVector, baseElementWriter));
}
@Override
public void bindIndex(ColumnWriterIndex index) {
elementIndex = new ScalarElementWriterIndex();
super.bindIndex(index);
elementObjWriter.events().bindIndex(elementIndex);
}
@Override
public void save() {
// No-op: done when writing each scalar value
// May be called, however, by code that also supports
// lists as a list does require an explicit save.
}
/**
* Set a repeated vector based on a Java array of the proper
* type. This function involves parsing the array type and so is
* suitable only for test code. The array can be either a primitive
* ({@code int [], say}) or a typed array of boxed values
* ({@code Integer[], say}).
*/
@Override
public void setObject(Object array) {
// Accept an empty array (of any type) to mean
// an empty array of the type of this writer.
if (array == null || Array.getLength(array) == 0) {
// Assume null means a 0-element array since Drill does
// not support null for the whole array.
return;
}
final String objClass = array.getClass().getName();
if (! objClass.startsWith("[")) {
throw new IllegalArgumentException(
String.format("Argument must be an array. Column `%s`, value = %s",
schema().name(), array.toString()));
}
// Figure out type
final char second = objClass.charAt(1);
switch ( second ) {
case '[':
// bytes is represented as an array of byte arrays.
final char third = objClass.charAt(2);
switch (third) {
case 'B':
setBytesArray((byte[][]) array);
break;
default:
throw new IllegalArgumentException(
String.format("Unknown Java array type: %s, for column `%s`",
objClass, schema().name()));
}
break;
case 'B':
setByteArray((byte[]) array);
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':
final int posn = objClass.indexOf(';');
// If the array is of type Object, then we have no type info.
final 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 if (memberClassName.equals(Integer.class.getName())) {
setIntObjectArray((Integer[]) array);
} else if (memberClassName.equals(Long.class.getName())) {
setLongObjectArray((Long[]) array);
} else if (memberClassName.equals(Double.class.getName())) {
setDoubleObjectArray((Double[]) array);
} else if (memberClassName.equals(Float.class.getName())) {
setFloatObjectArray((Float[]) array);
} else if (memberClassName.equals(Short.class.getName())) {
setShortObjectArray((Short[]) array);
} else if (memberClassName.equals(Byte.class.getName())) {
setByteObjectArray((Byte[]) array);
} else if (memberClassName.equals(Boolean.class.getName())) {
setBooleanObjectArray((Boolean[]) array);
} else if (memberClassName.equals(Object.class.getName())) {
setObjectArray((Object[]) array);
} else {
throw new IllegalArgumentException( "Unknown Java array type: " + memberClassName );
}
break;
default:
throw new IllegalArgumentException( "Unknown Java array type: " + objClass );
}
}
public void setObjectArray(Object[] value) {
for (int i = 0; i < value.length; i++) {
final Object element = value[i];
if (element != null) {
elementWriter.setObject(element);
}
}
}
public void setBooleanArray(boolean[] value) {
for (int i = 0; i < value.length; i++) {
elementWriter.setInt(value[i] ? 1 : 0);
}
}
public void setBooleanObjectArray(Boolean[] value) {
for (int i = 0; i < value.length; i++) {
final Boolean element = value[i];
if (element != null) {
elementWriter.setBoolean(element);
}
}
}
public void setBytesArray(byte[][] value) {
for (int i = 0; i < value.length; i++) {
elementWriter.setBytes(value[i], value[i].length);
}
}
public void setByteArray(byte[] value) {
for (int i = 0; i < value.length; i++) {
elementWriter.setInt(value[i]);
}
}
public void setByteObjectArray(Byte[] value) {
for (int i = 0; i < value.length; i++) {
final Byte element = value[i];
if (element != null) {
elementWriter.setInt(element);
}
}
}
public void setShortArray(short[] value) {
for (int i = 0; i < value.length; i++) {
elementWriter.setInt(value[i]);
}
}
public void setShortObjectArray(Short[] value) {
for (int i = 0; i < value.length; i++) {
final Short element = value[i];
if (element != null) {
elementWriter.setInt(element);
}
}
}
public void setIntArray(int[] value) {
for (int i = 0; i < value.length; i++) {
elementWriter.setInt(value[i]);
}
}
public void setIntObjectArray(Integer[] value) {
for (int i = 0; i < value.length; i++) {
final Integer element = value[i];
if (element != null) {
elementWriter.setInt(element);
}
}
}
public void setLongArray(long[] value) {
for (int i = 0; i < value.length; i++) {
elementWriter.setLong(value[i]);
}
}
public void setLongObjectArray(Long[] value) {
for (int i = 0; i < value.length; i++) {
final Long element = value[i];
if (element != null) {
elementWriter.setLong(element);
}
}
}
public void setFloatArray(float[] value) {
for (int i = 0; i < value.length; i++) {
elementWriter.setDouble(value[i]);
}
}
public void setFloatObjectArray(Float[] value) {
for (int i = 0; i < value.length; i++) {
final Float element = value[i];
if (element != null) {
elementWriter.setDouble(element);
}
}
}
public void setDoubleArray(double[] value) {
for (int i = 0; i < value.length; i++) {
elementWriter.setDouble(value[i]);
}
}
public void setDoubleObjectArray(Double[] value) {
for (int i = 0; i < value.length; i++) {
final Double element = value[i];
if (element != null) {
elementWriter.setDouble(element);
}
}
}
public void setStringArray(String[] value) {
for (int i = 0; i < value.length; i++) {
final String element = value[i];
if (element != null) {
elementWriter.setString(element);
}
}
}
public void setPeriodArray(Period[] value) {
for (int i = 0; i < value.length; i++) {
final Period element = value[i];
if (element != null) {
elementWriter.setPeriod(element);
}
}
}
public void setBigDecimalArray(BigDecimal[] value) {
for (int i = 0; i < value.length; i++) {
final BigDecimal element = value[i];
if (element != null) {
elementWriter.setDecimal(element);
}
}
}
}