/*
* Licensed 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 com.basho.riak.presto.models;
import com.facebook.presto.spi.ColumnHandle;
import com.facebook.presto.spi.ColumnMetadata;
import com.facebook.presto.spi.type.VarcharType;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import io.airlift.log.Logger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.isNullOrEmpty;
// Presto-Riak style table, stored in Riak and also exchanged between presto nodes
public class PRTable {
private static final Logger log = Logger.get(PRTable.class);
private final String name;
private final List<RiakColumn> columns;
private final Optional<String> comment;
private final Optional<List<PRSubTable>> subtables;
private String pkey;
@JsonCreator
public PRTable(
@JsonProperty(value = "name", required = true) String name,
@JsonProperty(value = "columns", required = true) List<RiakColumn> columns,
@JsonProperty(value = "comment", required = false) String comment,
@JsonProperty(value = "subtables", required = false) List<PRSubTable> subtables) {
checkArgument(!isNullOrEmpty(name), "name is null or is empty");
this.name = checkNotNull(name, "name is null");
this.columns = ImmutableList.copyOf(checkNotNull(columns, "columns is null"));
this.comment = Optional.ofNullable(comment);
this.subtables = Optional.ofNullable(ImmutableList.copyOf(subtables));
for (RiakColumn column : this.columns) {
if (column.getPkey() &&
column.getType() == VarcharType.VARCHAR &&
this.pkey == null) {
this.pkey = column.getName();
}
}
if (pkey == null) {
log.warn("Primary Key is not defined in columns effectively. Some queries become slow.");
}
// TODO: verify all subtable definitions here
}
public static Function<PRTable, String> nameGetter() {
return new Function<PRTable, String>() {
@Override
public String apply(PRTable table) {
return table.getName();
}
};
}
@JsonProperty
public String getName() {
return name;
}
@JsonProperty
public List<RiakColumn> getColumns() {
return columns;
}
@JsonProperty
public List<PRSubTable> getSubtables() {
return subtables.get();
}
public List<String> getSubtableNames() {
List<String> l = new ArrayList<>();
if (!subtables.isPresent()) {
return l;
}
for (PRSubTable subtable : subtables.get()) {
l.add(name + PRSubTable.SEPARATOR + subtable.getName());
}
return l;
}
public PRSubTable getSubtable(String fullTableName) {
if (fullTableName.equals(name)) {
return null;
}
for (PRSubTable subtable : subtables.get()) {
//fullTableName is lowercase due to Presto's constraint
if (subtable.match(fullTableName)) {
return subtable;
}
}
return null;
}
@JsonProperty
public String getComment() {
return comment.get();
}
public List<ColumnMetadata> getColumnsMetadata(String connectorId) {
Map<String, ColumnHandle> columnHandles = getColumnHandles(connectorId);
ImmutableList.Builder<ColumnMetadata> builder = ImmutableList.builder();
for (ColumnHandle columnHandle : columnHandles.values()) {
RiakColumnHandle handle = (RiakColumnHandle) columnHandle;
boolean hidden = false;
if (handle.getOrdinalPosition() < 2)
hidden = true;
String comment = handle.getColumn().getComment();
if (handle.getColumn().getPkey()) {
if (comment == null) {
comment = "(key)";
} else {
comment += " (key)";
}
}
builder.add(new ColumnMetadata(handle.getColumn().getName(),
handle.getColumn().getType(),
handle.getColumn().getPkey(),
comment, hidden));
}
return builder.build();
}
public Map<String, ColumnHandle> getColumnHandles(String connectorId) {
//TODO: For now we assume keys are all in UTF-8
ImmutableMap.Builder<String, ColumnHandle> columnHandles = ImmutableMap.builder();
columnHandles.put(RiakColumnHandle.PKEY_COLUMN_NAME,
new RiakColumnHandle(connectorId,
new RiakColumn(RiakColumnHandle.PKEY_COLUMN_NAME,
VarcharType.VARCHAR, pkey, true, true),
0));
//columnsMetadata.add(new ColumnMetadata(RiakColumnHandle.PKEY_COLUMN_NAME, VarcharType.VARCHAR, 0, true, pkey, true));
columnHandles.put(RiakColumnHandle.VTAG_COLUMN_NAME,
new RiakColumnHandle(connectorId,
new RiakColumn(RiakColumnHandle.VTAG_COLUMN_NAME,
VarcharType.VARCHAR, "vtag", false, false),
1));
//columnsMetadata.add(new ColumnMetadata(RiakColumnHandle.VTAG_COLUMN_NAME, VarcharType.VARCHAR, 1, false, "vtag", true));
int index = 2;
for (RiakColumn column : this.columns) {
// Column metadata should remember whichi is primary key, but for now it's
// impossible, as Presto database has no concept of primary key like this.
columnHandles.put(column.getName(),
new RiakColumnHandle(connectorId, column, index));
//columnsMetadata.add(new ColumnMetadata(column.getName(), column.getType(),
// index, column.getIndex(), column.getComment(), false));
index++;
}
return columnHandles.build();
}
public String toString() {
String ret = name + ":{";
for (RiakColumn col : columns) {
ret += col.toString();
ret += " ";
}
ret += "} ";
for (PRSubTable subtable : subtables.get()) {
ret += subtable.toString();
}
return ret;
}
}