blob: a37cb7bcb7d0a4c458ac83b11a91c02518c8bd1c [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.v3.schema;
import java.util.Collection;
import org.apache.drill.common.expression.PathSegment;
import org.apache.drill.common.expression.PathSegment.ArraySegment;
import org.apache.drill.common.expression.PathSegment.NameSegment;
import org.apache.drill.common.expression.SchemaPath;
import org.apache.drill.exec.record.metadata.ColumnMetadata;
import org.apache.drill.exec.record.metadata.Propertied;
import org.apache.drill.exec.record.metadata.TupleMetadata;
import org.apache.drill.exec.record.metadata.TupleSchema;
import com.google.common.base.Preconditions;
/**
* Parse the projection list into a dynamic tuple schema. Using
* an enhanced form of dynamic column which records projection list
* information (such as map members and array indexes.)
* <p>
* A wildcard project list can contain implicit columns in
* addition to the wildcard. The wildcard defines the
* <i>insert point</i>: the point at which reader-defined
* columns are inserted as found.
*/
public class ScanProjectionParser {
public static final String PROJECTION_TYPE_PROP = Propertied.DRILL_PROP_PREFIX + "proj-type";
public static final String PROJECT_ALL = "all";
public static final String PROJECT_NONE = "none";
public static class ProjectionParseResult {
public final int wildcardPosn;
public final TupleMetadata dynamicSchema;
public ProjectionParseResult(int wildcardPosn,
TupleMetadata dynamicSchema) {
this.wildcardPosn = wildcardPosn;
this.dynamicSchema = dynamicSchema;
}
public boolean isProjectAll() { return wildcardPosn != -1; }
}
private int wildcardPosn = -1;
public static ProjectionParseResult parse(Collection<SchemaPath> projList) {
if (projList == null) {
return SchemaUtils.projectAll();
}
if (projList.isEmpty()) {
return SchemaUtils.projectNone();
}
return new ScanProjectionParser().parseProjection(projList);
}
private ProjectionParseResult parseProjection(Collection<SchemaPath> projList) {
TupleMetadata tupleProj = new TupleSchema();
for (SchemaPath col : projList) {
parseMember(tupleProj, 0, col.getRootSegment());
}
return new ProjectionParseResult(wildcardPosn, tupleProj);
}
private void parseMember(TupleMetadata tuple, int depth, NameSegment nameSeg) {
String colName = nameSeg.getPath();
if (colName.equals(SchemaPath.DYNAMIC_STAR)) {
tuple.setProperty(PROJECTION_TYPE_PROP, PROJECT_ALL);
if (depth == 0) {
Preconditions.checkState(wildcardPosn == -1);
wildcardPosn = tuple.size();
}
} else {
ProjectedColumn col = project(tuple, nameSeg.getPath());
parseChildSeg(col, depth + 1, nameSeg);
}
}
protected ProjectedColumn project(TupleMetadata tuple, String colName) {
ColumnMetadata col = tuple.metadata(colName);
ProjectedColumn projCol;
if (col == null) {
projCol = new ProjectedColumn(colName);
tuple.addColumn(projCol);
} else {
projCol = (ProjectedColumn) col;
projCol.bumpRefCount();
}
return projCol;
}
private void parseChildSeg(ProjectedColumn column, int depth, PathSegment parentPath) {
if (parentPath.isLastPath()) {
parseLeaf(column, depth);
} else {
PathSegment seg = parentPath.getChild();
if (seg.isArray()) {
parseArraySeg(column, depth, (ArraySegment) seg);
} else {
parseMemberSeg(column, depth, (NameSegment) seg);
}
}
}
/**
* Parse a projection of the form {@code a}: that is, just a bare column.
*/
private void parseLeaf(ProjectedColumn parent, int depth) {
if (parent.isSimple()) {
// Nothing to do
} else if (parent.isArray() && depth == 1) {
parent.projectAllElements();
} else if (parent.isMap()) {
parent.projectAllMembers();
}
}
private void parseArraySeg(ProjectedColumn column, int depth, ArraySegment arraySeg) {
boolean wasArray = column.isArray();
column.becomeArray(Math.max(depth, column.arrayDims()));
// Record only outermost dimension indexes
if (depth == 1) {
if (column.refCount() > 1 && !wasArray) {
column.projectAllElements();
} else {
column.addIndex(arraySeg.getIndex());
}
}
parseChildSeg(column, depth + 1, arraySeg);
}
private void parseMemberSeg(ProjectedColumn column, int depth, NameSegment memberSeg) {
if (column.refCount() > 1 && !column.isMap()) {
column.projectAllMembers();
}
TupleMetadata tuple = column.explicitMembers();
if (tuple != null) {
parseMember(tuple, depth, memberSeg);
}
}
}