blob: f9d099676404f21e0cdb181ec5906d6feccc9343 [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 org.apache.drill.common.types.TypeProtos.MinorType;
import org.apache.drill.exec.record.metadata.ColumnMetadata;
import org.apache.drill.exec.vector.ValueVector;
import org.apache.drill.exec.vector.accessor.ColumnWriterIndex;
import org.apache.drill.exec.vector.accessor.ObjectWriter;
import org.apache.drill.exec.vector.accessor.VariantWriter.VariantWriterListener;
import org.apache.drill.exec.vector.accessor.impl.HierarchicalFormatter;
import org.apache.drill.exec.vector.accessor.writer.AbstractFixedWidthWriter.BaseFixedWidthWriter;
import org.apache.drill.exec.vector.accessor.writer.UnionWriterImpl.UnionShim;
import org.apache.drill.exec.vector.complex.UnionVector;
/**
* Lists can operate in three modes: no type, one type or many
* types (that is, a list of unions.) This shim implements the
* variant writer when the backing vector is a union or a list
* backed by a union. It is a distinct class so that a List
* writer can have a uniform variant-style interface even as the
* list itself evolves from no type, to a single type and to
* a union.
*/
public class UnionVectorShim implements UnionShim {
static class DefaultListener implements VariantWriterListener {
private final UnionVectorShim shim;
private DefaultListener(UnionVectorShim shim) {
this.shim = shim;
}
@Override
public ObjectWriter addType(MinorType type) {
// This vector layer does not have visibility to the implementation
// classes for metadata. Use the methods provided to add the type
// which will create the member metadata. This means that the type
// will already be in the variant schema by the time we add the
// writer to the variant writer in a few steps from now.
final ValueVector memberVector = shim.vector.getMember(type);
final ColumnMetadata memberSchema = shim.writer.variantSchema().addType(type);
return ColumnWriterFactory.buildColumnWriter(memberSchema, memberVector);
}
@Override
public ObjectWriter addMember(ColumnMetadata schema) {
throw new UnsupportedOperationException();
}
}
private final UnionVector vector;
private final AbstractObjectWriter variants[];
private UnionWriterImpl writer;
/**
* Writer for the type vector associated with the union. The type vector
* says which union member holds the value for each row. The type vector
* can also indicate if the value is null.
*/
private final BaseScalarWriter typeWriter;
public UnionVectorShim(UnionVector vector) {
this.vector = vector;
typeWriter = ColumnWriterFactory.newWriter(vector.getTypeVector());
variants = new AbstractObjectWriter[MinorType.values().length];
}
public UnionVectorShim(UnionVector vector,
AbstractObjectWriter variants[]) {
this.vector = vector;
typeWriter = ColumnWriterFactory.newWriter(vector.getTypeVector());
if (variants == null) {
this.variants = new AbstractObjectWriter[MinorType.values().length];
} else {
this.variants = variants;
}
}
@Override
public void bindWriter(UnionWriterImpl writer) {
this.writer = writer;
final ColumnWriterIndex index = writer.index();
if (index != null) {
bindIndex(index);
}
}
@Override
public void bindIndex(ColumnWriterIndex index) {
typeWriter.bindIndex(index);
for (int i = 0; i < variants.length; i++) {
if (variants[i] != null) {
variants[i].events().bindIndex(index);
}
}
}
// Unions are complex: the listener should bind to the individual components
// as they are created.
@Override
public void bindListener(ColumnWriterListener listener) { }
@Override
public void setNull() {
// Not really necessary: the default value is 0.
// This lets a caller change its mind after setting a
// value.
typeWriter.setInt(UnionVector.NULL_MARKER);
}
@Override
public boolean hasType(MinorType type) {
return variants[type.ordinal()] != null;
}
@Override
public ObjectWriter member(MinorType type) {
final AbstractObjectWriter colWriter = variants[type.ordinal()];
if (colWriter != null) {
return colWriter;
}
if (writer.listener() == null) {
writer.bindListener(new DefaultListener(this));
}
return addMember(type);
}
@Override
public void setType(MinorType type) {
typeWriter.setInt(type.getNumber());
}
@Override
public AbstractObjectWriter addMember(ColumnMetadata schema) {
final AbstractObjectWriter colWriter = (AbstractObjectWriter) writer.listener().addMember(schema);
addMember(colWriter);
return colWriter;
}
@Override
public AbstractObjectWriter addMember(MinorType type) {
final AbstractObjectWriter colWriter = (AbstractObjectWriter) writer.listener().addType(type);
addMember(colWriter);
return colWriter;
}
/**
* Add a column writer to an existing union 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
*/
@Override
public void addMember(AbstractObjectWriter colWriter) {
addMemberWriter(colWriter);
writer.addMember(colWriter);
}
/**
* Performs just the work of adding a vector to the list of existing
* variants. Called when adding a type via the writer, but also when
* the result set loader promotes a list from single type to a union,
* and provides this shim with the writer from the single-list shim.
* In the latter case, the writer is already initialized and is already
* part of the metadata for this list; so we don't want to call the
* list's {@code addMember()} and repeat those operations.
*
* @param colWriter the column (type) writer to add
*/
public void addMemberWriter(AbstractObjectWriter colWriter) {
final MinorType type = colWriter.schema().type();
assert variants[type.ordinal()] == null;
variants[type.ordinal()] = colWriter;
}
@Override
public void startWrite() {
typeWriter.startWrite();
for (int i = 0; i < variants.length; i++) {
if (variants[i] != null) {
variants[i].events().startWrite();
}
}
}
@Override
public void startRow() {
typeWriter.startRow();
for (int i = 0; i < variants.length; i++) {
if (variants[i] != null) {
variants[i].events().startRow();
}
}
}
@Override
public void endArrayValue() {
typeWriter.endArrayValue();
for (int i = 0; i < variants.length; i++) {
if (variants[i] != null) {
variants[i].events().endArrayValue();
}
}
}
@Override
public void restartRow() {
typeWriter.restartRow();
for (int i = 0; i < variants.length; i++) {
if (variants[i] != null) {
variants[i].events().restartRow();
}
}
}
@Override
public void saveRow() {
typeWriter.saveRow();
for (int i = 0; i < variants.length; i++) {
if (variants[i] != null) {
variants[i].events().saveRow();
}
}
}
@Override
public void preRollover() {
typeWriter.preRollover();
for (int i = 0; i < variants.length; i++) {
if (variants[i] != null) {
variants[i].events().preRollover();
}
}
}
@Override
public void postRollover() {
typeWriter.postRollover();
for (int i = 0; i < variants.length; i++) {
if (variants[i] != null) {
variants[i].events().postRollover();
}
}
}
@Override
public void endWrite() {
typeWriter.endWrite();
for (int i = 0; i < variants.length; i++) {
if (variants[i] != null) {
variants[i].events().endWrite();
}
}
}
/**
* Return the writer for the types vector. To be used only by the row set
* loader overflow logic; never by the application (which is why the method
* is not defined in the interface.)
*
* @return the writer for the types vector
*/
public AbstractScalarWriterImpl typeWriter() { return typeWriter; }
@Override
public int writeIndex() { return typeWriter.writeIndex(); }
@Override
public int lastWriteIndex() { return typeWriter.lastWriteIndex(); }
@Override
public int rowStartIndex() { return typeWriter.rowStartIndex(); }
/**
* When promoting the list to a union, the union's
* type vector was initialized for any rows written thus
* far. Tell the type writer that those positions have been
* written so that they are not zero-filled.
*/
public void initTypeIndex(int typeFillCount) {
((BaseFixedWidthWriter) typeWriter).setLastWriteIndex(typeFillCount);
}
@Override
public void dump(HierarchicalFormatter format) {
format.startObject(this).attribute("typeWriter");
typeWriter.dump(format);
format.endObject();
}
}