blob: cb43c648a182786bf7e78474fd2cb1e7a40f0e33 [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.record.metadata;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import org.apache.drill.exec.record.MaterializedField;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Defines the schema of a tuple: either the top-level row or a nested
* "map" (really structure). A schema is a collection of columns (backed
* by vectors in the loader itself.) Columns are accessible by name or
* index. New columns may be added at any time; the new column takes the
* next available index.
*/
@JsonAutoDetect(
fieldVisibility = JsonAutoDetect.Visibility.NONE,
getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE,
setterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
@JsonPropertyOrder({"columns", "properties"})
public class TupleSchema extends AbstractPropertied implements TupleMetadata {
public static final String TYPE = "tuple_schema";
private AbstractMapColumnMetadata parentMap;
private final TupleNameSpace<ColumnMetadata> nameSpace = new TupleNameSpace<>();
public TupleSchema() { }
@JsonCreator
public TupleSchema(@JsonProperty("columns") List<AbstractColumnMetadata> columns,
@JsonProperty("properties") Map<String, String> properties) {
if (columns != null) {
columns.forEach(this::addColumn);
}
setProperties(properties);
}
public void bind(AbstractMapColumnMetadata parentMap) {
this.parentMap = parentMap;
}
public TupleMetadata copy() {
TupleMetadata tuple = new TupleSchema();
for (ColumnMetadata md : this) {
tuple.addColumn(md.copy());
}
tuple.setProperties(properties());
return tuple;
}
@Override
public ColumnMetadata add(MaterializedField field) {
ColumnMetadata md = MetadataUtils.fromField(field);
add(md);
return md;
}
public ColumnMetadata addView(MaterializedField field) {
ColumnMetadata md = MetadataUtils.fromView(field);
add(md);
return md;
}
/**
* Add a column metadata column created by the caller. Used for specialized
* cases beyond those handled by {@link #add(MaterializedField)}.
*
* @param md the custom column metadata which must have the correct
* index set (from {@link #size()}
*/
public void add(ColumnMetadata md) {
md.bind(this);
nameSpace.add(md.name(), md);
}
@Override
public int addColumn(ColumnMetadata column) {
add(column);
return size() - 1;
}
@Override
public MaterializedField column(String name) {
ColumnMetadata md = metadata(name);
return md == null ? null : md.schema();
}
@Override
public ColumnMetadata metadata(String name) {
return nameSpace.get(name);
}
@Override
public int index(String name) {
return nameSpace.indexOf(name);
}
@Override
public MaterializedField column(int index) {
return metadata(index).schema();
}
@Override
public ColumnMetadata metadata(int index) {
return nameSpace.get(index);
}
@Override
public AbstractMapColumnMetadata parent() { return parentMap; }
@Override
public int size() { return nameSpace.count(); }
@Override
public boolean isEmpty() { return nameSpace.count( ) == 0; }
@Override
public Iterator<ColumnMetadata> iterator() {
return nameSpace.iterator();
}
@Override
public boolean isEquivalent(TupleMetadata other) {
TupleSchema otherSchema = (TupleSchema) other;
if (nameSpace.count() != otherSchema.nameSpace.count()) {
return false;
}
for (int i = 0; i < nameSpace.count(); i++) {
if (! nameSpace.get(i).isEquivalent(otherSchema.nameSpace.get(i))) {
return false;
}
}
return true;
}
@Override
public List<MaterializedField> toFieldList() {
List<MaterializedField> cols = new ArrayList<>();
for (ColumnMetadata md : nameSpace) {
cols.add(md.schema());
}
return cols;
}
@JsonProperty("columns")
@Override
public List<ColumnMetadata> toMetadataList() {
return new ArrayList<>(nameSpace.entries());
}
@Override
public String fullName(int index) {
return fullName(metadata(index));
}
@Override
public String fullName(ColumnMetadata column) {
String quotedName = column.name();
if (quotedName.contains(".")) {
quotedName = "`" + quotedName + "`";
}
if (isRoot()) {
return column.name();
} else {
return fullName() + "." + quotedName;
}
}
public String fullName() {
if (isRoot()) {
return "<root>";
} else {
return parentMap.parentTuple().fullName(parentMap);
}
}
public boolean isRoot() { return parentMap == null; }
@Override
public String toString() {
StringBuilder builder = new StringBuilder()
.append("[")
.append(getClass().getSimpleName())
.append(" ");
builder.append(nameSpace.entries().stream()
.map(ColumnMetadata::toString)
.collect(Collectors.joining(", ")));
if (hasProperties()) {
if (!nameSpace.entries().isEmpty()) {
builder.append(", ");
}
builder.append("properties: ").append(properties());
}
builder.append("]");
return builder.toString();
}
@JsonProperty("properties")
@Override
public Map<String, String> properties() {
return super.properties();
}
}