| /* |
| * 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; |
| } |
| } |
| } |