/**
* 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.tajo.catalog.store;
import com.google.common.collect.Maps;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.tajo.TajoConstants;
import org.apache.tajo.catalog.CatalogUtil;
import org.apache.tajo.catalog.FunctionDesc;
import org.apache.tajo.catalog.exception.*;
import org.apache.tajo.catalog.proto.CatalogProtos;
import org.apache.tajo.catalog.proto.CatalogProtos.IndexDescProto;
import java.io.IOException;
import java.util.*;
import static org.apache.tajo.catalog.proto.CatalogProtos.AlterTablespaceProto.AlterTablespaceType;
import static org.apache.tajo.catalog.proto.CatalogProtos.TablespaceProto;
/**
* CatalogServer guarantees that all operations are thread-safe.
* So, we don't need to consider concurrency problem here.
*/
public class MemStore implements CatalogStore {
private final Map<String, String> tablespaces = Maps.newHashMap();
private final Map<String, Map<String, CatalogProtos.TableDescProto>> databases = Maps.newHashMap();
private final Map<String, CatalogProtos.FunctionDescProto> functions = Maps.newHashMap();
private final Map<String, Map<String, IndexDescProto>> indexes = Maps.newHashMap();
private final Map<String, Map<String, IndexDescProto>> indexesByColumn = Maps.newHashMap();
public MemStore(Configuration conf) {
}
public void close() throws IOException {
databases.clear();
functions.clear();
indexes.clear();
}
@Override
public void createTablespace(String spaceName, String spaceUri) throws CatalogException {
if (tablespaces.containsKey(spaceName)) {
throw new AlreadyExistsTablespaceException(spaceName);
}
tablespaces.put(spaceName, spaceUri);
}
@Override
public boolean existTablespace(String spaceName) throws CatalogException {
return tablespaces.containsKey(spaceName);
}
@Override
public void dropTablespace(String spaceName) throws CatalogException {
if (!tablespaces.containsKey(spaceName)) {
throw new NoSuchTablespaceException(spaceName);
}
tablespaces.remove(spaceName);
}
@Override
public Collection<String> getAllTablespaceNames() throws CatalogException {
return tablespaces.keySet();
}
@Override
public TablespaceProto getTablespace(String spaceName) throws CatalogException {
if (!tablespaces.containsKey(spaceName)) {
throw new NoSuchTablespaceException(spaceName);
}
TablespaceProto.Builder builder = TablespaceProto.newBuilder();
builder.setSpaceName(spaceName);
builder.setUri(tablespaces.get(spaceName));
return builder.build();
}
@Override
public void alterTablespace(CatalogProtos.AlterTablespaceProto alterProto) throws CatalogException {
if (!tablespaces.containsKey(alterProto.getSpaceName())) {
throw new NoSuchTablespaceException(alterProto.getSpaceName());
}
if (alterProto.getCommandList().size() > 0) {
for (CatalogProtos.AlterTablespaceProto.AlterTablespaceCommand cmd : alterProto.getCommandList()) {
if(cmd.getType() == AlterTablespaceType.LOCATION) {
CatalogProtos.AlterTablespaceProto.SetLocation setLocation = cmd.getLocation();
tablespaces.put(alterProto.getSpaceName(), setLocation.getUri());
}
}
}
}
@Override
public void createDatabase(String databaseName, String tablespaceName) throws CatalogException {
if (databases.containsKey(databaseName)) {
throw new AlreadyExistsDatabaseException(databaseName);
}
databases.put(databaseName, new HashMap<String, CatalogProtos.TableDescProto>());
}
@Override
public boolean existDatabase(String databaseName) throws CatalogException {
return databases.containsKey(databaseName);
}
@Override
public void dropDatabase(String databaseName) throws CatalogException {
if (!databases.containsKey(databaseName)) {
throw new NoSuchDatabaseException(databaseName);
}
databases.remove(databaseName);
}
@Override
public Collection<String> getAllDatabaseNames() throws CatalogException {
return databases.keySet();
}
/**
* Get a database namespace from a Map instance.
*/
private <T> Map<String, T> checkAndGetDatabaseNS(final Map<String, Map<String, T>> databaseMap,
String databaseName) {
if (databaseMap.containsKey(databaseName)) {
return databaseMap.get(databaseName);
} else {
throw new NoSuchDatabaseException(databaseName);
}
}
@Override
public void createTable(CatalogProtos.TableDescProto request) throws CatalogException {
String [] splitted = CatalogUtil.splitTableName(request.getTableName());
if (splitted.length == 1) {
throw new IllegalArgumentException("createTable() requires a qualified table name, but it is \""
+ request.getTableName() + "\".");
}
String databaseName = splitted[0];
String tableName = splitted[1];
Map<String, CatalogProtos.TableDescProto> database = checkAndGetDatabaseNS(databases, databaseName);
String tbName = tableName;
if (database.containsKey(tbName)) {
throw new AlreadyExistsTableException(tbName);
}
database.put(tbName, request);
}
@Override
public boolean existTable(String dbName, String tbName) throws CatalogException {
Map<String, CatalogProtos.TableDescProto> database = checkAndGetDatabaseNS(databases, dbName);
return database.containsKey(tbName);
}
@Override
public void dropTable(String dbName, String tbName) throws CatalogException {
Map<String, CatalogProtos.TableDescProto> database = checkAndGetDatabaseNS(databases, dbName);
if (database.containsKey(tbName)) {
database.remove(tbName);
} else {
throw new NoSuchTableException(tbName);
}
}
/* (non-Javadoc)
* @see CatalogStore#alterTable(AlterTableDesc)
*/
@Override
public void alterTable(CatalogProtos.AlterTableDescProto alterTableDescProto) throws CatalogException {
String[] split = CatalogUtil.splitTableName(alterTableDescProto.getTableName());
if (split.length == 1) {
throw new IllegalArgumentException("alterTable() requires a qualified table name, but it is \""
+ alterTableDescProto.getTableName() + "\".");
}
String databaseName = split[0];
String tableName = split[1];
final Map<String, CatalogProtos.TableDescProto> database = checkAndGetDatabaseNS(databases, databaseName);
final CatalogProtos.TableDescProto tableDescProto = database.get(tableName);
CatalogProtos.TableDescProto newTableDescProto;
CatalogProtos.SchemaProto schemaProto;
switch (alterTableDescProto.getAlterTableType()) {
case RENAME_TABLE:
if (database.containsKey(alterTableDescProto.getNewTableName())) {
throw new AlreadyExistsTableException(alterTableDescProto.getNewTableName());
}
// Currently, we only use the default table space (i.e., WAREHOUSE directory).
String spaceUri = tablespaces.get(TajoConstants.DEFAULT_TABLESPACE_NAME);
// Create a new table directory.
String newPath = new Path(spaceUri, new Path(databaseName, alterTableDescProto.getNewTableName())).toString();
newTableDescProto = tableDescProto.toBuilder()
.setTableName(alterTableDescProto.getNewTableName())
.setPath(newPath).build();
database.remove(tableName);
database.put(alterTableDescProto.getNewTableName(), newTableDescProto);
break;
case RENAME_COLUMN:
schemaProto = tableDescProto.getSchema();
final int index = getIndexOfColumnToBeRenamed(schemaProto.getFieldsList(),
alterTableDescProto.getAlterColumnName().getOldColumnName());
final CatalogProtos.ColumnProto columnProto = schemaProto.getFields(index);
final CatalogProtos.ColumnProto newcolumnProto =
columnProto.toBuilder().setName(alterTableDescProto.getAlterColumnName().getNewColumnName()).build();
newTableDescProto = tableDescProto.toBuilder().setSchema(schemaProto.toBuilder().
setFields(index, newcolumnProto).build()).build();
database.put(tableName, newTableDescProto);
break;
case ADD_COLUMN:
schemaProto = tableDescProto.getSchema();
CatalogProtos.SchemaProto newSchemaProto =
schemaProto.toBuilder().addFields(alterTableDescProto.getAddColumn()).build();
newTableDescProto = tableDescProto.toBuilder().setSchema(newSchemaProto).build();
database.put(tableName, newTableDescProto);
break;
default:
//TODO
}
}
private int getIndexOfColumnToBeRenamed(List<CatalogProtos.ColumnProto> fieldList, String columnName) {
int fieldCount = fieldList.size();
for (int index = 0; index < fieldCount; index++) {
CatalogProtos.ColumnProto columnProto = fieldList.get(index);
if (null != columnProto && columnProto.getName().equalsIgnoreCase(columnName)) {
return index;
}
}
return -1;
}
/* (non-Javadoc)
* @see CatalogStore#getTable(java.lang.String)
*/
@Override
public CatalogProtos.TableDescProto getTable(String databaseName, String tableName)
throws CatalogException {
Map<String, CatalogProtos.TableDescProto> database = checkAndGetDatabaseNS(databases, databaseName);
if (database.containsKey(tableName)) {
CatalogProtos.TableDescProto unqualified = database.get(tableName);
CatalogProtos.TableDescProto.Builder builder = CatalogProtos.TableDescProto.newBuilder();
CatalogProtos.SchemaProto schemaProto =
CatalogUtil.getQualfiedSchema(databaseName + "." + tableName, unqualified.getSchema());
builder.mergeFrom(unqualified);
builder.setSchema(schemaProto);
return builder.build();
} else {
throw new NoSuchTableException(tableName);
}
}
/* (non-Javadoc)
* @see CatalogStore#getAllTableNames()
*/
@Override
public List<String> getAllTableNames(String databaseName) throws CatalogException {
Map<String, CatalogProtos.TableDescProto> database = checkAndGetDatabaseNS(databases, databaseName);
return new ArrayList<String>(database.keySet());
}
@Override
public void addPartitionMethod(CatalogProtos.PartitionMethodProto partitionMethodProto) throws CatalogException {
throw new RuntimeException("not supported!");
}
@Override
public CatalogProtos.PartitionMethodProto getPartitionMethod(String databaseName, String tableName)
throws CatalogException {
Map<String, CatalogProtos.TableDescProto> database = checkAndGetDatabaseNS(databases, databaseName);
if (database.containsKey(tableName)) {
CatalogProtos.TableDescProto table = database.get(tableName);
return table.hasPartition() ? table.getPartition() : null;
} else {
throw new NoSuchTableException(tableName);
}
}
@Override
public boolean existPartitionMethod(String databaseName, String tableName)
throws CatalogException {
Map<String, CatalogProtos.TableDescProto> database = checkAndGetDatabaseNS(databases, databaseName);
if (database.containsKey(tableName)) {
CatalogProtos.TableDescProto table = database.get(tableName);
return table.hasPartition();
} else {
throw new NoSuchTableException(tableName);
}
}
@Override
public void dropPartitionMethod(String databaseName, String tableName) throws CatalogException {
throw new RuntimeException("not supported!");
}
@Override
public void addPartitions(CatalogProtos.PartitionsProto partitionDescList) throws CatalogException {
throw new RuntimeException("not supported!");
}
@Override
public void addPartition(String databaseName, String tableName, CatalogProtos.PartitionDescProto
partitionDescProto) throws CatalogException {
throw new RuntimeException("not supported!");
}
@Override
public CatalogProtos.PartitionsProto getPartitions(String tableName) throws CatalogException {
throw new RuntimeException("not supported!");
}
@Override
public CatalogProtos.PartitionDescProto getPartition(String partitionName) throws CatalogException {
throw new RuntimeException("not supported!");
}
@Override
public void delPartition(String partitionName) throws CatalogException {
throw new RuntimeException("not supported!");
}
@Override
public void dropPartitions(String tableName) throws CatalogException {
throw new RuntimeException("not supported!");
}
/* (non-Javadoc)
* @see CatalogStore#createIndex(nta.catalog.proto.CatalogProtos.IndexDescProto)
*/
@Override
public void createIndex(IndexDescProto proto) throws CatalogException {
final String databaseName = proto.getTableIdentifier().getDatabaseName();
Map<String, IndexDescProto> index = checkAndGetDatabaseNS(indexes, databaseName);
Map<String, IndexDescProto> indexByColumn = checkAndGetDatabaseNS(indexesByColumn, databaseName);
if (index.containsKey(proto.getIndexName())) {
throw new AlreadyExistsIndexException(proto.getIndexName());
}
index.put(proto.getIndexName(), proto);
indexByColumn.put(proto.getTableIdentifier().getTableName() + "."
+ CatalogUtil.extractSimpleName(proto.getColumn().getName()), proto);
}
/* (non-Javadoc)
* @see CatalogStore#dropIndex(java.lang.String)
*/
@Override
public void dropIndex(String databaseName, String indexName) throws CatalogException {
Map<String, IndexDescProto> index = checkAndGetDatabaseNS(indexes, databaseName);
if (!index.containsKey(indexName)) {
throw new NoSuchIndexException(indexName);
}
index.remove(indexName);
}
/* (non-Javadoc)
* @see CatalogStore#getIndexByName(java.lang.String)
*/
@Override
public IndexDescProto getIndexByName(String databaseName, String indexName) throws CatalogException {
Map<String, IndexDescProto> index = checkAndGetDatabaseNS(indexes, databaseName);
if (!index.containsKey(indexName)) {
throw new NoSuchIndexException(indexName);
}
return index.get(indexName);
}
/* (non-Javadoc)
* @see CatalogStore#getIndexByName(java.lang.String, java.lang.String)
*/
@Override
public IndexDescProto getIndexByColumn(String databaseName, String tableName, String columnName)
throws CatalogException {
Map<String, IndexDescProto> indexByColumn = checkAndGetDatabaseNS(indexesByColumn, databaseName);
if (!indexByColumn.containsKey(columnName)) {
throw new NoSuchIndexException(columnName);
}
return indexByColumn.get(columnName);
}
@Override
public boolean existIndexByName(String databaseName, String indexName) throws CatalogException {
Map<String, IndexDescProto> index = checkAndGetDatabaseNS(indexes, databaseName);
return index.containsKey(indexName);
}
@Override
public boolean existIndexByColumn(String databaseName, String tableName, String columnName)
throws CatalogException {
Map<String, IndexDescProto> indexByColumn = checkAndGetDatabaseNS(indexesByColumn, databaseName);
return indexByColumn.containsKey(columnName);
}
@Override
public IndexDescProto[] getIndexes(String databaseName, String tableName) throws CatalogException {
List<IndexDescProto> protos = new ArrayList<IndexDescProto>();
Map<String, IndexDescProto> indexByColumn = checkAndGetDatabaseNS(indexesByColumn, databaseName);
for (IndexDescProto proto : indexByColumn.values()) {
if (proto.getTableIdentifier().getTableName().equals(tableName)) {
protos.add(proto);
}
}
return protos.toArray(new IndexDescProto[protos.size()]);
}
@Override
public void addFunction(FunctionDesc func) throws CatalogException {
// to be implemented
}
@Override
public void deleteFunction(FunctionDesc func) throws CatalogException {
// to be implemented
}
@Override
public void existFunction(FunctionDesc func) throws CatalogException {
// to be implemented
}
@Override
public List<String> getAllFunctionNames() throws CatalogException {
// to be implemented
return null;
}
}