blob: 7138505b2fc9eba0feb4497fb06f6ad80c5e3439 [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.physical.impl.scan.project;
import java.util.ArrayList;
import java.util.List;
import org.apache.drill.common.types.TypeProtos.MajorType;
import org.apache.drill.exec.physical.impl.scan.project.NullColumnLoader.NullColumnSpec;
import org.apache.drill.exec.physical.resultSet.ResultVectorCache;
import org.apache.drill.exec.record.VectorContainer;
import org.apache.drill.exec.record.metadata.ColumnMetadata;
import org.apache.drill.exec.record.metadata.TupleMetadata;
import org.apache.drill.exec.vector.ValueVector;
import com.google.common.annotations.VisibleForTesting;
/**
* Manages null columns by creating a null column loader for each
* set of non-empty null columns. This class acts as a scan-wide
* facade around the per-schema null column loader.
*/
public class NullColumnBuilder implements VectorSource {
public static class NullBuilderBuilder {
protected MajorType nullType;
protected boolean allowRequiredNullColumns;
protected TupleMetadata outputSchema;
public NullBuilderBuilder setNullType(MajorType nullType) {
this.nullType = nullType;
return this;
}
public NullBuilderBuilder allowRequiredNullColumns(boolean flag) {
allowRequiredNullColumns = flag;
return this;
}
public NullBuilderBuilder setOutputSchema(TupleMetadata outputSchema) {
this.outputSchema = outputSchema;
return this;
}
public NullColumnBuilder build() {
return new NullColumnBuilder(this);
}
}
/**
* Creates null columns if needed.
*/
protected final List<NullColumnSpec> nullCols = new ArrayList<>();
private NullColumnLoader nullColumnLoader;
private VectorContainer outputContainer;
protected TupleMetadata outputSchema;
/**
* The reader-specified null type if other than the default.
*/
private final MajorType nullType;
private final boolean allowRequiredNullColumns;
public NullColumnBuilder(NullBuilderBuilder builder) {
this.nullType = builder.nullType;
this.allowRequiredNullColumns = builder.allowRequiredNullColumns;
this.outputSchema = builder.outputSchema;
}
public NullColumnBuilder newChild(String mapName) {
NullBuilderBuilder builder = new NullBuilderBuilder()
.setNullType(nullType)
.allowRequiredNullColumns(allowRequiredNullColumns);
// Pass along the schema of the child map if 1) we have an output schema,
// 2) the column is defined in that schema, and 3) the column is a map.
if (outputSchema != null) {
ColumnMetadata colSchema = outputSchema.metadata(mapName);
if (colSchema != null) {
builder.setOutputSchema(colSchema.tupleSchema());
}
}
return builder.build();
}
public ResolvedNullColumn add(String name) {
return add(name, null);
}
public ResolvedNullColumn add(ColumnMetadata colDefn) {
final ResolvedNullColumn col = new ResolvedNullColumn(
colDefn, this, nullCols.size());
nullCols.add(col);
return col;
}
public ResolvedNullColumn add(String name, MajorType type) {
// If type is provided, use it. (Used during schema smoothing.)
// Else if there is an output schema, and the column appears in
// that schema, use that type.
ResolvedNullColumn col = null;
if (outputSchema != null) {
ColumnMetadata outputCol = outputSchema.metadata(name);
if (outputCol != null) {
if (type == null) {
// No preferred type, so no conflict
col = new ResolvedNullColumn(outputCol, this, nullCols.size());
} else if (type.getMinorType() != outputCol.type()) {
// Conflict in type, can't use default value.
} else if (type.getMode() != outputCol.mode()) {
// Conflict in mode. Use the specified name and type, but
// use the default value from the output schema.
col = new ResolvedNullColumn(name, type,
outputCol.defaultValue(), this, nullCols.size());
} else {
// Type and modes matches, just the output column
col = new ResolvedNullColumn(outputCol, this, nullCols.size());
}
}
}
if (col == null) {
col = new ResolvedNullColumn(name, type, null, this, nullCols.size());
}
nullCols.add(col);
return col;
}
public void build(ResultVectorCache vectorCache) {
close();
// If no null columns for this schema, no need to create
// the loader.
if (hasColumns()) {
nullColumnLoader = new NullColumnLoader(vectorCache, nullCols, nullType, allowRequiredNullColumns);
outputContainer = nullColumnLoader.output();
}
}
public boolean hasColumns() {
return nullCols != null && ! nullCols.isEmpty();
}
public void load(int rowCount) {
if (nullColumnLoader != null) {
final VectorContainer output = nullColumnLoader.load(rowCount);
assert output == outputContainer;
}
}
@Override
public ValueVector vector(int index) {
return outputContainer.getValueVector(index).getValueVector();
}
@VisibleForTesting
public VectorContainer output() { return outputContainer; }
public void close() {
if (nullColumnLoader != null) {
nullColumnLoader.close();
nullColumnLoader = null;
}
if (outputContainer != null) {
outputContainer.clear();
outputContainer = null;
}
}
}