blob: daf2f1a1f6a909e8c66aa23a6d5045a4356a842d [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.projSet;
import org.apache.drill.common.exceptions.UserException;
import org.apache.drill.common.types.Types;
import org.apache.drill.exec.physical.resultSet.ProjectionSet;
import org.apache.drill.exec.physical.resultSet.project.ProjectionType;
import org.apache.drill.exec.physical.resultSet.project.RequestedColumnImpl;
import org.apache.drill.exec.physical.resultSet.project.RequestedTuple;
import org.apache.drill.exec.physical.resultSet.project.RequestedTuple.RequestedColumn;
import org.apache.drill.exec.physical.resultSet.project.RequestedTuple.TupleProjectionType;
import org.apache.drill.exec.record.metadata.ColumnMetadata;
import org.apache.drill.exec.vector.accessor.convert.ColumnConversionFactory;
import org.apache.drill.exec.vector.complex.DictVector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Projection set based on an explicit set of columns provided
* in the physical plan. Columns in the list are projected, others
* are not.
*/
public class ExplicitProjectionSet extends AbstractProjectionSet {
private static final Logger logger = LoggerFactory.getLogger(ExplicitProjectionSet.class);
private final RequestedTuple requestedProj;
public ExplicitProjectionSet(RequestedTuple requestedProj, TypeConverter typeConverter) {
super(typeConverter);
this.requestedProj = requestedProj;
}
@Override
public ColumnReadProjection readProjection(ColumnMetadata col) {
RequestedColumn reqCol = requestedProj.get(col.name());
if (reqCol == null) {
return new UnprojectedReadColumn(col);
}
return getReadProjection(col, reqCol);
}
private ColumnReadProjection getReadProjection(ColumnMetadata col, RequestedColumn reqCol) {
ColumnMetadata outputSchema = outputSchema(col);
validateProjection(reqCol, outputSchema == null ? col : outputSchema);
if (!col.isMap() && !col.isDict()) {
// Non-map column.
ColumnConversionFactory conv = conversion(col, outputSchema);
return new ProjectedReadColumn(col, reqCol, outputSchema, conv);
} else {
// Maps are tuples. Create a tuple projection and wrap it in
// a column projection.
TypeConverter childConverter = childConverter(outputSchema);
ProjectionSet mapProjection;
if (! reqCol.type().isTuple() || reqCol.mapProjection().type() == TupleProjectionType.ALL) {
// Projection is simple: "m". This is equivalent to
// (non-SQL) m.*
// This may also be a projection of the form m.a, m. The
// general projection takes precedence.
mapProjection = new WildcardProjectionSet(childConverter, isStrict);
} else {
// Else, selected map items are projected, say m.a, m.c.
// (Here, we'll never hit the case where none of the map is
// projected; that case, while allowed in the RequestedTuple
// implementation, can never occur in a SELECT list.)
mapProjection = new ExplicitProjectionSet(reqCol.mapProjection(), childConverter);
}
if (col.isMap()) {
return new ProjectedMapColumn(col, reqCol, outputSchema, mapProjection);
} else {
return new ProjectedDictColumn(col, reqCol, outputSchema, mapProjection);
}
}
}
public void validateProjection(RequestedColumn colReq, ColumnMetadata readCol) {
if (colReq == null || readCol == null) {
return;
}
ProjectionType type = colReq.type();
if (type == null) {
return;
}
ProjectionType neededType = ProjectionType.typeFor(readCol.majorType());
if (type.isCompatible(neededType)) {
return;
}
throw UserException.validationError()
.message("Column type not compatible with projection specification")
.addContext("Column:", readCol.name())
.addContext("Projection type:", type.label())
.addContext("Column type:", Types.getSqlTypeName(readCol.majorType()))
.addContext(errorContext)
.build(logger);
}
@Override
public ColumnReadProjection readDictProjection(ColumnMetadata col) {
// Unlike for a MAP, requestedProj contains a key value, rather than nested field's name:
// create DICT's members somewhat artificially
assert DictVector.fieldNames.contains(col.name());
if (col.name().equals(DictVector.FIELD_KEY_NAME)) {
// This field is considered not projected but its
// vector and writer will be instantiated later.
return new UnprojectedReadColumn(col);
}
RequestedColumn reqCol = new RequestedColumnImpl(requestedProj, col.name()); // this is the 'value' column
return getReadProjection(col, reqCol);
}
@Override
public boolean isEmpty() { return requestedProj.projections().isEmpty(); }
}