/**
* 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.protobuf.InvalidProtocolBufferException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.tajo.annotation.Nullable;
import org.apache.tajo.catalog.CatalogConstants;
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.*;
import org.apache.tajo.common.TajoDataTypes.Type;
import org.apache.tajo.exception.InternalException;
import org.apache.tajo.exception.UnimplementedException;
import org.apache.tajo.util.FileUtil;
import org.apache.tajo.util.Pair;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import static org.apache.tajo.catalog.proto.CatalogProtos.AlterTablespaceProto.AlterTablespaceCommand;
public abstract class AbstractDBStore extends CatalogConstants implements CatalogStore {
protected final Log LOG = LogFactory.getLog(getClass());
protected final Configuration conf;
protected final String connectionId;
protected final String connectionPassword;
protected final String catalogUri;
private Connection conn;
protected Map<String, Boolean> baseTableMaps = new HashMap<String, Boolean>();
protected abstract String getCatalogDriverName();
protected abstract Connection createConnection(final Configuration conf) throws SQLException;
protected abstract boolean isInitialized() throws CatalogException;
protected abstract void createBaseTable() throws CatalogException;
protected abstract void dropBaseTable() throws CatalogException;
public AbstractDBStore(Configuration conf) throws InternalException {
this.conf = conf;
if (conf.get(CatalogConstants.DEPRECATED_CATALOG_URI) != null) {
LOG.warn("Configuration parameter " + CatalogConstants.DEPRECATED_CATALOG_URI + " " +
"is deprecated. Use " + CatalogConstants.CATALOG_URI + " instead.");
this.catalogUri = conf.get(CatalogConstants.DEPRECATED_CATALOG_URI);
} else {
this.catalogUri = conf.get(CatalogConstants.CATALOG_URI);
}
if (conf.get(CatalogConstants.DEPRECATED_CONNECTION_ID) != null) {
LOG.warn("Configuration parameter " + CatalogConstants.DEPRECATED_CONNECTION_ID + " " +
"is deprecated. Use " + CatalogConstants.CONNECTION_ID + " instead.");
this.connectionId = conf.get(CatalogConstants.DEPRECATED_CONNECTION_ID);
} else {
this.connectionId = conf.get(CatalogConstants.CONNECTION_ID);
}
if (conf.get(CatalogConstants.DEPRECATED_CONNECTION_PASSWORD) != null) {
LOG.warn("Configuration parameter " + CatalogConstants.DEPRECATED_CONNECTION_PASSWORD + " " +
"is deprecated. Use " + CatalogConstants.CONNECTION_PASSWORD + " instead.");
this.connectionPassword = conf.get(CatalogConstants.DEPRECATED_CONNECTION_PASSWORD);
} else {
this.connectionPassword = conf.get(CatalogConstants.CONNECTION_PASSWORD);
}
String catalogDriver = getCatalogDriverName();
try {
Class.forName(getCatalogDriverName()).newInstance();
LOG.info("Loaded the Catalog driver (" + catalogDriver + ")");
} catch (Exception e) {
throw new CatalogException("Cannot load Catalog driver " + catalogDriver, e);
}
try {
LOG.info("Trying to connect database (" + catalogUri + ")");
conn = createConnection(conf);
LOG.info("Connected to database (" + catalogUri + ")");
} catch (SQLException e) {
throw new CatalogException("Cannot connect to database (" + catalogUri
+ ")", e);
}
try {
if (isInitialized()) {
LOG.info("The base tables of CatalogServer already is initialized.");
verifySchemaVersion();
} else {
try {
createBaseTable();
LOG.info("The base tables of CatalogServer are created.");
} catch (CatalogException ce) {
try {
dropBaseTable();
} catch (Throwable t) {
LOG.error(t);
}
throw ce;
}
}
} catch (Exception se) {
throw new CatalogException("Cannot initialize the persistent storage of Catalog", se);
}
}
public abstract int getDriverVersion();
public String readSchemaFile(String path) throws CatalogException {
try {
return FileUtil.readTextFileFromResource("schemas/" + path);
} catch (IOException e) {
throw new CatalogException(e);
}
}
protected String getCatalogUri() {
return catalogUri;
}
public Connection getConnection() {
try {
boolean isValid = conn.isValid(100);
if (!isValid) {
CatalogUtil.closeQuietly(conn);
conn = createConnection(conf);
}
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
private void verifySchemaVersion() throws CatalogException {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet result = null;
try {
String sql = "SELECT version FROM META";
if (LOG.isDebugEnabled()) {
LOG.debug(sql.toString());
}
conn = getConnection();
pstmt = conn.prepareStatement(sql);
result = pstmt.executeQuery();
boolean noVersion = !result.next();
int schemaVersion = result.getInt(1);
if (noVersion || schemaVersion != getDriverVersion()) {
LOG.error(String.format("Catalog version (%d) and current driver version (%d) are mismatch to each other",
schemaVersion, getDriverVersion()));
LOG.error("=========================================================================");
LOG.error("| Catalog Store Migration Is Needed |");
LOG.error("=========================================================================");
LOG.error("| You might downgrade or upgrade Apache Tajo. Downgrading or upgrading |");
LOG.error("| Tajo without migration process is only available in some versions. |");
LOG.error("| In order to learn how to migration Apache Tajo instance, |");
LOG.error("| please refer http://s.apache.org/0_8_migration. |");
LOG.error("=========================================================================");
throw new CatalogException("Migration Needed. Please refer http://s.apache.org/0_8_migration.");
}
} catch (SQLException e) {
throw new CatalogException(e);
} finally {
CatalogUtil.closeQuietly(pstmt, result);
}
LOG.info(String.format("The compatibility of the catalog schema (version: %d) has been verified.",
getDriverVersion()));
}
/**
* Insert the version of the current catalog schema
*/
protected void insertSchemaVersion() throws CatalogException {
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement("INSERT INTO META VALUES (?)");
pstmt.setInt(1, getDriverVersion());
pstmt.executeUpdate();
} catch (SQLException se) {
throw new CatalogException("cannot insert catalog schema version", se);
} finally {
CatalogUtil.closeQuietly(pstmt);
}
}
@Override
public void createTablespace(String spaceName, String spaceUri) throws CatalogException {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet res = null;
try {
conn = getConnection();
conn.setAutoCommit(false);
String sql = String.format("INSERT INTO %s (SPACE_NAME, SPACE_URI) VALUES (?, ?)", TB_SPACES);
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, spaceName);
pstmt.setString(2, spaceUri);
pstmt.executeUpdate();
pstmt.close();
conn.commit();
} catch (SQLException se) {
if (conn != null) {
try {
conn.rollback();
} catch (SQLException e) {
LOG.error(e);
}
}
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt, res);
}
}
@Override
public boolean existTablespace(String tableSpaceName) throws CatalogException {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet res = null;
boolean exist = false;
try {
StringBuilder sql = new StringBuilder();
sql.append("SELECT SPACE_NAME FROM " + TB_SPACES + " WHERE SPACE_NAME = ?");
if (LOG.isDebugEnabled()) {
LOG.debug(sql.toString());
}
conn = getConnection();
pstmt = conn.prepareStatement(sql.toString());
pstmt.setString(1, tableSpaceName);
res = pstmt.executeQuery();
exist = res.next();
} catch (SQLException se) {
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt, res);
}
return exist;
}
@Override
public void dropTablespace(String tableSpaceName) throws CatalogException {
Connection conn = null;
PreparedStatement pstmt = null;
try {
TableSpaceInternal tableSpace = getTableSpaceInfo(tableSpaceName);
Collection<String> databaseNames = getAllDatabaseNamesInternal(COL_TABLESPACE_PK + " = " + tableSpace.spaceId);
conn = getConnection();
conn.setAutoCommit(false);
for (String databaseName : databaseNames) {
dropDatabase(databaseName);
}
String sql = "DELETE FROM " + TB_SPACES + " WHERE " + COL_TABLESPACE_PK + "= ?";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, tableSpace.getSpaceId());
pstmt.executeUpdate();
conn.commit();
} catch (SQLException se) {
if (conn != null) {
try {
conn.rollback();
} catch (SQLException e) {
LOG.error(e);
}
}
throw new CatalogException(String.format("Failed to drop tablespace \"%s\"", tableSpaceName), se);
} finally {
CatalogUtil.closeQuietly(pstmt);
}
}
@Override
public Collection<String> getAllTablespaceNames() throws CatalogException {
return getAllTablespaceNamesInternal(null);
}
private Collection<String> getAllTablespaceNamesInternal(@Nullable String whereCondition) throws CatalogException {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet resultSet = null;
List<String> tablespaceNames = new ArrayList<String>();
try {
String sql = "SELECT SPACE_NAME FROM " + TB_SPACES;
if (whereCondition != null) {
sql += " WHERE " + whereCondition;
}
conn = getConnection();
pstmt = conn.prepareStatement(sql);
resultSet = pstmt.executeQuery();
while (resultSet.next()) {
tablespaceNames.add(resultSet.getString(1));
}
} catch (SQLException se) {
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt, resultSet);
}
return tablespaceNames;
}
@Override
public TablespaceProto getTablespace(String spaceName) throws CatalogException {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet resultSet = null;
try {
String sql = "SELECT SPACE_NAME, SPACE_URI FROM " + TB_SPACES + " WHERE SPACE_NAME=?";
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, spaceName);
resultSet = pstmt.executeQuery();
if (!resultSet.next()) {
throw new NoSuchTablespaceException(spaceName);
}
String retrieveSpaceName = resultSet.getString("SPACE_NAME");
String uri = resultSet.getString("SPACE_URI");
TablespaceProto.Builder builder = TablespaceProto.newBuilder();
builder.setSpaceName(retrieveSpaceName);
builder.setUri(uri);
return builder.build();
} catch (SQLException se) {
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt, resultSet);
}
}
@Override
public void alterTablespace(AlterTablespaceProto alterProto) throws CatalogException {
Connection conn;
PreparedStatement pstmt = null;
if (alterProto.getCommandList().size() == 1) {
AlterTablespaceCommand command = alterProto.getCommand(0);
if (command.getType() == AlterTablespaceProto.AlterTablespaceType.LOCATION) {
AlterTablespaceProto.SetLocation setLocation = command.getLocation();
try {
String sql = "UPDATE " + TB_SPACES + " SET SPACE_URI=? WHERE SPACE_NAME=?";
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, setLocation.getUri());
pstmt.setString(2, alterProto.getSpaceName());
pstmt.executeUpdate();
} catch (SQLException se) {
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt);
}
}
}
}
@Override
public void createDatabase(String databaseName, String tablespaceName) throws CatalogException {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet res = null;
try {
TableSpaceInternal spaceInfo = getTableSpaceInfo(tablespaceName);
String sql = "INSERT INTO " + TB_DATABASES + " (DB_NAME, SPACE_ID) VALUES (?, ?)";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
conn = getConnection();
conn.setAutoCommit(false);
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, databaseName);
pstmt.setInt(2, spaceInfo.getSpaceId());
pstmt.executeUpdate();
pstmt.close();
conn.commit();
} catch (SQLException se) {
if (conn != null) {
try {
conn.rollback();
} catch (SQLException e) {
LOG.error(e);
}
}
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt, res);
}
}
@Override
public boolean existDatabase(String databaseName) throws CatalogException {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet res = null;
boolean exist = false;
try {
StringBuilder sql = new StringBuilder();
sql.append("SELECT DB_NAME FROM " + TB_DATABASES + " WHERE DB_NAME = ?");
if (LOG.isDebugEnabled()) {
LOG.debug(sql.toString());
}
conn = getConnection();
pstmt = conn.prepareStatement(sql.toString());
pstmt.setString(1, databaseName);
res = pstmt.executeQuery();
exist = res.next();
} catch (SQLException se) {
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt, res);
}
return exist;
}
@Override
public void dropDatabase(String databaseName) throws CatalogException {
Collection<String> tableNames = getAllTableNames(databaseName);
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = getConnection();
conn.setAutoCommit(false);
for (String tableName : tableNames) {
dropTableInternal(conn, databaseName, tableName);
}
String sql = "DELETE FROM " + TB_DATABASES + " WHERE DB_NAME = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, databaseName);
pstmt.executeUpdate();
conn.commit();
} catch (SQLException se) {
if (conn != null) {
try {
conn.rollback();
} catch (SQLException e) {
LOG.error(e);
}
}
throw new CatalogException(String.format("Failed to drop database \"%s\"", databaseName), se);
} finally {
CatalogUtil.closeQuietly(pstmt);
}
}
@Override
public Collection<String> getAllDatabaseNames() throws CatalogException {
return getAllDatabaseNamesInternal(null);
}
private Collection<String> getAllDatabaseNamesInternal(@Nullable String whereCondition) throws CatalogException {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet resultSet = null;
List<String> databaseNames = new ArrayList<String>();
try {
String sql = "SELECT DB_NAME FROM " + TB_DATABASES;
if (whereCondition != null) {
sql += " WHERE " + whereCondition;
}
conn = getConnection();
pstmt = conn.prepareStatement(sql);
resultSet = pstmt.executeQuery();
while (resultSet.next()) {
databaseNames.add(resultSet.getString(1));
}
} catch (SQLException se) {
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt, resultSet);
}
return databaseNames;
}
private static class TableSpaceInternal {
private final int spaceId;
private final String spaceURI;
private final String handler;
TableSpaceInternal(int spaceId, String spaceURI, String handler) {
this.spaceId = spaceId;
this.spaceURI = spaceURI;
this.handler = handler;
}
public int getSpaceId() {
return spaceId;
}
public String getSpaceURI() {
return spaceURI;
}
public String getHandler() {
return handler;
}
}
private TableSpaceInternal getTableSpaceInfo(String spaceName) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet res = null;
try {
String sql = "SELECT SPACE_ID, SPACE_URI, SPACE_HANDLER from " + TB_SPACES + " WHERE SPACE_NAME = ?";
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, spaceName);
res = pstmt.executeQuery();
if (!res.next()) {
throw new CatalogException("ERROR: there is no SPACE_ID matched to the space name \"" + spaceName + "\"");
}
return new TableSpaceInternal(res.getInt(1), res.getString(2), res.getString(3));
} catch (SQLException se) {
throw new NoSuchTablespaceException(spaceName);
} finally {
CatalogUtil.closeQuietly(pstmt, res);
}
}
private int getTableId(int databaseId, String databaseName, String tableName) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet res = null;
try {
String tidSql = "SELECT TID from TABLES WHERE db_id = ? AND " + COL_TABLES_NAME + "=?";
conn = getConnection();
pstmt = conn.prepareStatement(tidSql);
pstmt.setInt(1, databaseId);
pstmt.setString(2, tableName);
res = pstmt.executeQuery();
if (!res.next()) {
throw new CatalogException("ERROR: there is no tid matched to " + tableName);
}
return res.getInt(1);
} catch (SQLException se) {
throw new NoSuchTableException(databaseName, tableName);
} finally {
CatalogUtil.closeQuietly(pstmt, res);
}
}
enum TableType {
BASE_TABLE,
EXTERNAL_TABLE
}
@Override
public void createTable(final CatalogProtos.TableDescProto table) throws CatalogException {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet res = null;
try {
conn = getConnection();
conn.setAutoCommit(false);
String[] splitted = CatalogUtil.splitTableName(table.getTableName());
if (splitted.length == 1) {
throw new IllegalArgumentException("createTable() requires a qualified table name, but it is \""
+ table.getTableName() + "\".");
}
String databaseName = splitted[0];
String tableName = splitted[1];
int dbid = getDatabaseId(databaseName);
if (table.getIsExternal()) {
String sql = "INSERT INTO TABLES (DB_ID, " + COL_TABLES_NAME + ", TABLE_TYPE, PATH, STORE_TYPE) VALUES(?, ?, ?, ?, ?) ";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, dbid);
pstmt.setString(2, tableName);
pstmt.setString(3, TableType.EXTERNAL_TABLE.name());
pstmt.setString(4, table.getPath());
pstmt.setString(5, table.getMeta().getStoreType().name());
pstmt.executeUpdate();
pstmt.close();
} else {
String sql = "INSERT INTO TABLES (DB_ID, " + COL_TABLES_NAME + ", TABLE_TYPE, STORE_TYPE) VALUES(?, ?, ?, ?) ";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, dbid);
pstmt.setString(2, tableName);
pstmt.setString(3, TableType.BASE_TABLE.name());
pstmt.setString(4, table.getMeta().getStoreType().name());
pstmt.executeUpdate();
pstmt.close();
}
String tidSql =
"SELECT TID from " + TB_TABLES + " WHERE " + COL_DATABASES_PK + "=? AND " + COL_TABLES_NAME + "=?";
pstmt = conn.prepareStatement(tidSql);
pstmt.setInt(1, dbid);
pstmt.setString(2, tableName);
res = pstmt.executeQuery();
if (!res.next()) {
throw new CatalogException("ERROR: there is no TID matched to " + table.getTableName());
}
int tableId = res.getInt("TID");
res.close();
pstmt.close();
String colSql =
"INSERT INTO " + TB_COLUMNS + " (TID, COLUMN_NAME, ORDINAL_POSITION, DATA_TYPE, TYPE_LENGTH) VALUES(?, ?, ?, ?, ?) ";
if (LOG.isDebugEnabled()) {
LOG.debug(colSql);
}
pstmt = conn.prepareStatement(colSql);
for (int i = 0; i < table.getSchema().getFieldsCount(); i++) {
ColumnProto col = table.getSchema().getFields(i);
pstmt.setInt(1, tableId);
pstmt.setString(2, CatalogUtil.extractSimpleName(col.getName()));
pstmt.setInt(3, i);
pstmt.setString(4, col.getDataType().getType().name());
pstmt.setInt(5, (col.getDataType().hasLength() ? col.getDataType().getLength() : 0));
pstmt.addBatch();
pstmt.clearParameters();
}
pstmt.executeBatch();
pstmt.close();
if (table.getMeta().hasParams()) {
String propSQL = "INSERT INTO " + TB_OPTIONS + "(TID, KEY_, VALUE_) VALUES(?, ?, ?)";
if (LOG.isDebugEnabled()) {
LOG.debug(propSQL);
}
pstmt = conn.prepareStatement(propSQL);
for (CatalogProtos.KeyValueProto entry : table.getMeta().getParams().getKeyvalList()) {
pstmt.setInt(1, tableId);
pstmt.setString(2, entry.getKey());
pstmt.setString(3, entry.getValue());
pstmt.addBatch();
pstmt.clearParameters();
}
pstmt.executeBatch();
pstmt.close();
}
if (table.hasStats()) {
String statSql = "INSERT INTO " + TB_STATISTICS + " (TID, NUM_ROWS, NUM_BYTES) VALUES(?, ?, ?)";
if (LOG.isDebugEnabled()) {
LOG.debug(statSql);
}
pstmt = conn.prepareStatement(statSql);
pstmt.setInt(1, tableId);
pstmt.setLong(2, table.getStats().getNumRows());
pstmt.setLong(3, table.getStats().getNumBytes());
pstmt.executeUpdate();
pstmt.close();
}
if (table.hasPartition()) {
String partSql =
"INSERT INTO PARTITION_METHODS (TID, PARTITION_TYPE, EXPRESSION, EXPRESSION_SCHEMA) VALUES(?, ?, ?, ?)";
if (LOG.isDebugEnabled()) {
LOG.debug(partSql);
}
pstmt = conn.prepareStatement(partSql);
pstmt.setInt(1, tableId);
pstmt.setString(2, table.getPartition().getPartitionType().name());
pstmt.setString(3, table.getPartition().getExpression());
pstmt.setBytes(4, table.getPartition().getExpressionSchema().toByteArray());
pstmt.executeUpdate();
}
// If there is no error, commit the changes.
conn.commit();
} catch (SQLException se) {
if (conn != null) {
try {
conn.rollback();
} catch (SQLException e) {
LOG.error(e);
}
}
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt, res);
}
}
@Override
public void alterTable(CatalogProtos.AlterTableDescProto alterTableDescProto) throws CatalogException {
String[] splitted = CatalogUtil.splitTableName(alterTableDescProto.getTableName());
if (splitted.length == 1) {
throw new IllegalArgumentException("alterTable() requires a qualified table name, but it is \""
+ alterTableDescProto.getTableName() + "\".");
}
String databaseName = splitted[0];
String tableName = splitted[1];
try {
int databaseId = getDatabaseId(databaseName);
int tableId = getTableId(databaseId, databaseName, tableName);
switch (alterTableDescProto.getAlterTableType()) {
case RENAME_TABLE:
if (existTable(databaseName,alterTableDescProto.getNewTableName())) {
throw new AlreadyExistsTableException(alterTableDescProto.getNewTableName());
}
renameTable(tableId, alterTableDescProto.getNewTableName());
break;
case RENAME_COLUMN:
if (existColumn(tableId, alterTableDescProto.getAlterColumnName().getNewColumnName())) {
throw new ColumnNameAlreadyExistException(alterTableDescProto.getAlterColumnName().getNewColumnName());
}
renameColumn(tableId, alterTableDescProto.getAlterColumnName());
break;
case ADD_COLUMN:
if (existColumn(tableId, alterTableDescProto.getAddColumn().getName())) {
throw new ColumnNameAlreadyExistException(alterTableDescProto.getAddColumn().getName());
}
addNewColumn(tableId, alterTableDescProto.getAddColumn());
break;
default:
}
} catch (SQLException sqlException) {
throw new CatalogException(sqlException);
}
}
private void renameTable(final int tableId, final String tableName) throws CatalogException {
final String updtaeRenameTableSql = "UPDATE " + TB_TABLES + " SET " + COL_TABLES_NAME + " = ? " + " WHERE TID = ?";
if (LOG.isDebugEnabled()) {
LOG.debug(updtaeRenameTableSql);
}
Connection conn;
PreparedStatement pstmt = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(updtaeRenameTableSql);
pstmt.setString(1, tableName);
pstmt.setInt(2, tableId);
pstmt.executeUpdate();
} catch (SQLException sqlException) {
throw new CatalogException(sqlException);
} finally {
CatalogUtil.closeQuietly(pstmt);
}
}
private void renameColumn(final int tableId, final CatalogProtos.AlterColumnProto alterColumnProto)
throws CatalogException {
final String selectColumnSql =
"SELECT COLUMN_NAME, DATA_TYPE, TYPE_LENGTH, ORDINAL_POSITION from " + TB_COLUMNS +
" WHERE " + COL_TABLES_PK + " = ?" + " AND COLUMN_NAME = ?" ;
final String deleteColumnNameSql =
"DELETE FROM " + TB_COLUMNS + " WHERE TID = ? AND COLUMN_NAME = ?";
final String insertNewColumnSql =
"INSERT INTO " + TB_COLUMNS +
" (TID, COLUMN_NAME, ORDINAL_POSITION, DATA_TYPE, TYPE_LENGTH) VALUES(?, ?, ?, ?, ?) ";
if (LOG.isDebugEnabled()) {
LOG.debug(selectColumnSql);
LOG.debug(deleteColumnNameSql);
LOG.debug(insertNewColumnSql);
}
Connection conn;
PreparedStatement pstmt = null;
ResultSet resultSet = null;
try {
conn = getConnection();
conn.setAutoCommit(false);
//SELECT COLUMN
pstmt = conn.prepareStatement(selectColumnSql);
pstmt.setInt(1, tableId);
pstmt.setString(2, alterColumnProto.getOldColumnName());
resultSet = pstmt.executeQuery();
CatalogProtos.ColumnProto columnProto = null;
int ordinalPostion = -1;
if (resultSet.next()) {
columnProto = resultToColumnProto(resultSet);
//NOTE ==> Setting new column Name
columnProto = columnProto.toBuilder().setName(alterColumnProto.getNewColumnName()).build();
ordinalPostion = resultSet.getInt("ORDINAL_POSITION");
} else {
throw new NoSuchColumnException(alterColumnProto.getOldColumnName());
}
resultSet.close();
pstmt.close();
resultSet = null;
//DELETE COLUMN
pstmt = conn.prepareStatement(deleteColumnNameSql);
pstmt.setInt(1, tableId);
pstmt.setString(2, alterColumnProto.getOldColumnName());
pstmt.executeUpdate();
pstmt.close();
//INSERT COLUMN
pstmt = conn.prepareStatement(insertNewColumnSql);
pstmt.setInt(1, tableId);
pstmt.setString(2, CatalogUtil.extractSimpleName(columnProto.getName()));
pstmt.setInt(3, ordinalPostion);
pstmt.setString(4, columnProto.getDataType().getType().name());
pstmt.setInt(5, (columnProto.getDataType().hasLength() ? columnProto.getDataType().getLength() : 0));
pstmt.executeUpdate();
conn.commit();
} catch (SQLException sqlException) {
throw new CatalogException(sqlException);
} finally {
CatalogUtil.closeQuietly(pstmt,resultSet);
}
}
private void addNewColumn(int tableId, CatalogProtos.ColumnProto columnProto) throws CatalogException {
final String insertNewColumnSql = "INSERT INTO " + TB_COLUMNS + " (TID, COLUMN_NAME, ORDINAL_POSITION, DATA_TYPE, TYPE_LENGTH) VALUES(?, ?, ?, ?, ?) ";
final String columnCountSql = "SELECT COLUMN_NAME, MAX(ORDINAL_POSITION) AS POSITION FROM " + TB_COLUMNS + " WHERE TID = ? GROUP BY COLUMN_NAME";
if (LOG.isDebugEnabled()) {
LOG.debug(insertNewColumnSql);
LOG.debug(columnCountSql);
}
Connection conn;
PreparedStatement pstmt = null;
ResultSet resultSet = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(columnCountSql);
pstmt.setInt(1 , tableId);
resultSet = pstmt.executeQuery();
int position = resultSet.next() ? resultSet.getInt("POSITION") : 0;
resultSet.close();
pstmt.close();
resultSet = null;
pstmt = conn.prepareStatement(insertNewColumnSql);
pstmt.setInt(1, tableId);
pstmt.setString(2, CatalogUtil.extractSimpleName(columnProto.getName()));
pstmt.setInt(3, position + 1);
pstmt.setString(4, columnProto.getDataType().getType().name());
pstmt.setInt(5, (columnProto.getDataType().hasLength() ? columnProto.getDataType().getLength() : 0));
pstmt.executeUpdate();
} catch (SQLException sqlException) {
throw new CatalogException(sqlException);
} finally {
CatalogUtil.closeQuietly(pstmt,resultSet);
}
}
private int getDatabaseId(String databaseName) throws SQLException {
String sql = String.format("SELECT DB_ID from %s WHERE DB_NAME = ?", TB_DATABASES);
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet res = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, databaseName);
res = pstmt.executeQuery();
if (!res.next()) {
throw new NoSuchDatabaseException(databaseName);
}
return res.getInt("DB_ID");
} finally {
CatalogUtil.closeQuietly(pstmt, res);
}
}
@Override
public boolean existTable(String databaseName, final String tableName) throws CatalogException {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet res = null;
boolean exist = false;
try {
int dbid = getDatabaseId(databaseName);
String sql = "SELECT TID FROM TABLES WHERE DB_ID = ? AND " + COL_TABLES_NAME + "=?";
if (LOG.isDebugEnabled()) {
LOG.debug(sql.toString());
}
conn = getConnection();
pstmt = conn.prepareStatement(sql.toString());
pstmt.setInt(1, dbid);
pstmt.setString(2, tableName);
res = pstmt.executeQuery();
exist = res.next();
} catch (SQLException se) {
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt, res);
}
return exist;
}
public void dropTableInternal(Connection conn, String databaseName, final String tableName)
throws SQLException {
PreparedStatement pstmt = null;
try {
int databaseId = getDatabaseId(databaseName);
int tableId = getTableId(databaseId, databaseName, tableName);
String sql = "DELETE FROM " + TB_COLUMNS + " WHERE " + COL_TABLES_PK + " = ?";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, tableId);
pstmt.executeUpdate();
pstmt.close();
sql = "DELETE FROM " + TB_OPTIONS + " WHERE " + COL_TABLES_PK + " = ? ";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, tableId);
pstmt.executeUpdate();
pstmt.close();
sql = "DELETE FROM " + TB_STATISTICS + " WHERE " + COL_TABLES_PK + " = ? ";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, tableId);
pstmt.executeUpdate();
pstmt.close();
sql = "DELETE FROM " + TB_PARTTIONS + " WHERE " + COL_TABLES_PK + " = ? ";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, tableId);
pstmt.executeUpdate();
pstmt.close();
sql = "DELETE FROM " + TB_PARTITION_METHODS + " WHERE " + COL_TABLES_PK + " = ? ";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, tableId);
pstmt.executeUpdate();
pstmt.close();
sql = "DELETE FROM TABLES WHERE DB_ID = ? AND " + COL_TABLES_PK + " = ?";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, databaseId);
pstmt.setInt(2, tableId);
pstmt.executeUpdate();
} finally {
CatalogUtil.closeQuietly(pstmt);
}
}
@Override
public void dropTable(String databaseName, final String tableName) throws CatalogException {
Connection conn = null;
try {
conn = getConnection();
conn.setAutoCommit(false);
dropTableInternal(conn, databaseName, tableName);
conn.commit();
} catch (SQLException se) {
try {
conn.rollback();
} catch (SQLException e) {
LOG.error(e);
}
} finally {
CatalogUtil.closeQuietly(conn);
}
}
public Pair<Integer, String> getDatabaseIdAndUri(String databaseName) throws SQLException {
String sql =
"SELECT DB_ID, SPACE_URI from " + TB_DATABASES + " natural join " + TB_SPACES + " WHERE db_name = ?";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet res = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, databaseName);
res = pstmt.executeQuery();
if (!res.next()) {
throw new NoSuchDatabaseException(databaseName);
}
return new Pair<Integer, String>(res.getInt(1), res.getString(2) + "/" + databaseName);
} finally {
CatalogUtil.closeQuietly(pstmt, res);
}
}
@Override
public CatalogProtos.TableDescProto getTable(String databaseName, String tableName)
throws CatalogException {
Connection conn = null;
ResultSet res = null;
PreparedStatement pstmt = null;
CatalogProtos.TableDescProto.Builder tableBuilder = null;
StoreType storeType;
try {
tableBuilder = CatalogProtos.TableDescProto.newBuilder();
Pair<Integer, String> databaseIdAndUri = getDatabaseIdAndUri(databaseName);
//////////////////////////////////////////
// Geting Table Description
//////////////////////////////////////////
String sql =
"SELECT TID, " + COL_TABLES_NAME + ", TABLE_TYPE, PATH, STORE_TYPE FROM TABLES " +
"WHERE DB_ID = ? AND " + COL_TABLES_NAME + "=?";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, databaseIdAndUri.getFirst());
pstmt.setString(2, tableName);
res = pstmt.executeQuery();
if (!res.next()) { // there is no table of the given name.
return null;
}
int tableId = res.getInt(1);
tableBuilder.setTableName(CatalogUtil.buildFQName(databaseName, res.getString(2).trim()));
TableType tableType = TableType.valueOf(res.getString(3));
if (tableType == TableType.EXTERNAL_TABLE) {
tableBuilder.setIsExternal(true);
}
if (tableType == TableType.BASE_TABLE) {
tableBuilder.setPath(databaseIdAndUri.getSecond() + "/" + tableName);
} else {
tableBuilder.setPath(res.getString(4).trim());
}
storeType = CatalogUtil.getStoreType(res.getString(5).trim());
res.close();
pstmt.close();
//////////////////////////////////////////
// Geting Column Descriptions
//////////////////////////////////////////
CatalogProtos.SchemaProto.Builder schemaBuilder = CatalogProtos.SchemaProto.newBuilder();
sql = "SELECT COLUMN_NAME, DATA_TYPE, TYPE_LENGTH from " + TB_COLUMNS +
" WHERE " + COL_TABLES_PK + " = ? ORDER BY ORDINAL_POSITION ASC";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, tableId);
res = pstmt.executeQuery();
while (res.next()) {
schemaBuilder.addFields(resultToColumnProto(res));
}
tableBuilder.setSchema(
CatalogUtil.getQualfiedSchema(databaseName + "." + tableName, schemaBuilder.build()));
res.close();
pstmt.close();
//////////////////////////////////////////
// Geting Table Properties
//////////////////////////////////////////
CatalogProtos.TableProto.Builder metaBuilder = CatalogProtos.TableProto.newBuilder();
metaBuilder.setStoreType(storeType);
sql = "SELECT key_, value_ FROM " + TB_OPTIONS + " WHERE " + COL_TABLES_PK + " = ?";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, tableId);
res = pstmt.executeQuery();
metaBuilder.setParams(resultToKeyValueSetProto(res));
tableBuilder.setMeta(metaBuilder);
res.close();
pstmt.close();
//////////////////////////////////////////
// Geting Table Stats
//////////////////////////////////////////
sql = "SELECT num_rows, num_bytes FROM " + TB_STATISTICS + " WHERE " + COL_TABLES_PK + " = ?";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, tableId);
res = pstmt.executeQuery();
if (res.next()) {
TableStatsProto.Builder statBuilder = TableStatsProto.newBuilder();
statBuilder.setNumRows(res.getLong("num_rows"));
statBuilder.setNumBytes(res.getLong("num_bytes"));
tableBuilder.setStats(statBuilder);
}
res.close();
pstmt.close();
//////////////////////////////////////////
// Getting Table Partition Method
//////////////////////////////////////////
sql = " SELECT partition_type, expression, expression_schema FROM " + TB_PARTITION_METHODS +
" WHERE " + COL_TABLES_PK + " = ?";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, tableId);
res = pstmt.executeQuery();
if (res.next()) {
tableBuilder.setPartition(resultToPartitionMethodProto(databaseName, tableName, res));
}
} catch (InvalidProtocolBufferException e) {
throw new CatalogException(e);
} catch (SQLException se) {
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt, res);
}
return tableBuilder.build();
}
private Type getDataType(final String typeStr) {
try {
return Enum.valueOf(Type.class, typeStr);
} catch (IllegalArgumentException iae) {
LOG.error("Cannot find a matched type aginst from '" + typeStr + "'");
return null;
}
}
@Override
public List<String> getAllTableNames(String databaseName) throws CatalogException {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet res = null;
List<String> tables = new ArrayList<String>();
try {
int dbid = getDatabaseId(databaseName);
String sql = "SELECT " + COL_TABLES_NAME + " FROM TABLES WHERE DB_ID = ?";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, dbid);
res = pstmt.executeQuery();
while (res.next()) {
tables.add(res.getString(COL_TABLES_NAME).trim());
}
} catch (SQLException se) {
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt, res);
}
return tables;
}
private static final String ADD_PARTITION_SQL =
"INSERT INTO " + TB_PARTTIONS + " (TID, PARTITION_NAME, ORDINAL_POSITION, PATH) VALUES (?,?,?,?)";
@Override
public void addPartitions(CatalogProtos.PartitionsProto partitionsProto) throws CatalogException {
Connection conn = null;
PreparedStatement pstmt = null;
try {
if (LOG.isDebugEnabled()) {
LOG.debug(ADD_PARTITION_SQL);
}
String databaseName = partitionsProto.getTableIdentifier().getDatabaseName();
String tableName = partitionsProto.getTableIdentifier().getTableName();
int databaseId = getDatabaseId(databaseName);
int tableId = getTableId(databaseId, databaseName, tableName);
conn = getConnection();
pstmt = conn.prepareStatement(ADD_PARTITION_SQL);
for (CatalogProtos.PartitionDescProto partition : partitionsProto.getPartitionList()) {
addPartitionInternal(pstmt, tableId, partition);
}
pstmt.executeBatch();
conn.commit();
} catch (SQLException se) {
if (conn != null) {
try {
conn.rollback();
} catch (SQLException e) {
LOG.error(e);
}
}
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt);
}
}
private static void addPartitionInternal(PreparedStatement pstmt, int tableId, PartitionDescProto partition) throws
SQLException {
pstmt.setInt(1, tableId);
pstmt.setString(2, partition.getPartitionName());
pstmt.setInt(3, partition.getOrdinalPosition());
pstmt.setString(4, partition.getPath());
pstmt.addBatch();
pstmt.clearParameters();
}
@Override
public void addPartitionMethod(CatalogProtos.PartitionMethodProto proto) throws CatalogException {
Connection conn = null;
PreparedStatement pstmt = null;
try {
String sql = "INSERT INTO " + TB_PARTITION_METHODS + " (TID, PARTITION_TYPE, EXPRESSION, EXPRESSION_SCHEMA) " +
"VALUES (?,?,?,?)";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
String databaseName = proto.getTableIdentifier().getDatabaseName();
String tableName = proto.getTableIdentifier().getTableName();
int databaseId = getDatabaseId(databaseName);
int tableId = getTableId(databaseId, databaseName, tableName);
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, tableId);
pstmt.setString(2, proto.getPartitionType().name());
pstmt.setString(3, proto.getExpression());
pstmt.setBytes(4, proto.getExpressionSchema().toByteArray());
pstmt.executeUpdate();
} catch (SQLException se) {
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt);
}
}
@Override
public void dropPartitionMethod(String databaseName, String tableName) throws CatalogException {
Connection conn = null;
PreparedStatement pstmt = null;
try {
String sql = "DELETE FROM " + TB_PARTITION_METHODS + " WHERE " + COL_TABLES_NAME + " = ? ";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, tableName);
pstmt.executeUpdate();
} catch (SQLException se) {
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt);
}
}
@Override
public CatalogProtos.PartitionMethodProto getPartitionMethod(String databaseName, String tableName)
throws CatalogException {
Connection conn = null;
ResultSet res = null;
PreparedStatement pstmt = null;
try {
String sql = "SELECT partition_type, expression, expression_schema FROM " + TB_PARTITION_METHODS +
" WHERE " + COL_TABLES_NAME + " = ? ";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, tableName);
res = pstmt.executeQuery();
if (res.next()) {
return resultToPartitionMethodProto(databaseName, tableName, res);
}
} catch (InvalidProtocolBufferException e) {
throw new CatalogException(e);
} catch (SQLException se) {
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt, res);
}
return null;
}
@Override
public boolean existPartitionMethod(String databaseName, String tableName) throws CatalogException {
Connection conn = null;
ResultSet res = null;
PreparedStatement pstmt = null;
boolean exist = false;
try {
String sql = "SELECT partition_type, expression, expression_schema FROM " + TB_PARTITION_METHODS +
" WHERE " + COL_TABLES_NAME + "= ?";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, tableName);
res = pstmt.executeQuery();
exist = res.next();
} catch (SQLException se) {
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt, res);
}
return exist;
}
@Override
public void addPartition(String databaseName, String tableName,
CatalogProtos.PartitionDescProto partition) throws CatalogException {
Connection conn = null;
PreparedStatement pstmt = null;
try {
if (LOG.isDebugEnabled()) {
LOG.debug(ADD_PARTITION_SQL);
}
int databaseId = getDatabaseId(databaseName);
int tableId = getTableId(databaseId, databaseName, tableName);
conn = getConnection();
pstmt = conn.prepareStatement(ADD_PARTITION_SQL);
addPartitionInternal(pstmt, tableId, partition);
pstmt.executeUpdate();
} catch (SQLException se) {
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt);
}
}
@Override
public CatalogProtos.PartitionDescProto getPartition(String partitionName) throws CatalogException {
// TODO
throw new UnimplementedException("getPartition is not implemented");
}
@Override
public CatalogProtos.PartitionsProto getPartitions(String tableName) throws CatalogException {
// TODO
throw new UnimplementedException("getPartitions is not implemented");
}
@Override
public void delPartition(String partitionName) throws CatalogException {
Connection conn = null;
PreparedStatement pstmt = null;
try {
String sql = "DELETE FROM " + TB_PARTTIONS + " WHERE PARTITION_NAME = ? ";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, partitionName);
pstmt.executeUpdate();
} catch (SQLException se) {
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt);
}
}
@Override
public void dropPartitions(String tableName) throws CatalogException {
Connection conn = null;
PreparedStatement pstmt = null;
try {
String sql = "DELETE FROM " + TB_PARTTIONS + " WHERE " + COL_TABLES_NAME + "= ? ";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, tableName);
pstmt.executeUpdate();
} catch (SQLException se) {
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt);
}
}
@Override
public void createIndex(final IndexDescProto proto) throws CatalogException {
Connection conn = null;
PreparedStatement pstmt = null;
String databaseName = proto.getTableIdentifier().getDatabaseName();
String tableName = proto.getTableIdentifier().getTableName();
String columnName = CatalogUtil.extractSimpleName(proto.getColumn().getName());
try {
int databaseId = getDatabaseId(databaseName);
int tableId = getTableId(databaseId, databaseName, tableName);
String sql = "INSERT INTO " + TB_INDEXES +
" (" + COL_DATABASES_PK + ", " + COL_TABLES_PK + ", INDEX_NAME, " +
"COLUMN_NAME, DATA_TYPE, INDEX_TYPE, IS_UNIQUE, IS_CLUSTERED, IS_ASCENDING) VALUES (?,?,?,?,?,?,?,?,?)";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
conn = getConnection();
conn.setAutoCommit(false);
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, databaseId);
pstmt.setInt(2, tableId);
pstmt.setString(3, proto.getIndexName());
pstmt.setString(4, columnName);
pstmt.setString(5, proto.getColumn().getDataType().getType().name());
pstmt.setString(6, proto.getIndexMethod().toString());
pstmt.setBoolean(7, proto.hasIsUnique() && proto.getIsUnique());
pstmt.setBoolean(8, proto.hasIsClustered() && proto.getIsClustered());
pstmt.setBoolean(9, proto.hasIsAscending() && proto.getIsAscending());
pstmt.executeUpdate();
conn.commit();
} catch (SQLException se) {
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt);
}
}
@Override
public void dropIndex(String databaseName, final String indexName) throws CatalogException {
Connection conn = null;
PreparedStatement pstmt = null;
try {
int databaseId = getDatabaseId(databaseName);
String sql = "DELETE FROM " + TB_INDEXES + " WHERE " + COL_DATABASES_PK + "=? AND INDEX_NAME=?";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, databaseId);
pstmt.setString(2, indexName);
pstmt.executeUpdate();
} catch (SQLException se) {
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt);
}
}
public static String getTableName(Connection conn, int tableId) throws SQLException {
ResultSet res = null;
PreparedStatement pstmt = null;
try {
pstmt =
conn.prepareStatement("SELECT " + COL_TABLES_NAME + " FROM " + TB_TABLES + " WHERE " + COL_TABLES_PK + "=?");
pstmt.setInt(1, tableId);
res = pstmt.executeQuery();
if (!res.next()) {
throw new CatalogException("Cannot get any table name from TID");
}
return res.getString(1);
} finally {
CatalogUtil.closeQuietly(pstmt, res);
}
}
final static String GET_INDEXES_SQL =
"SELECT " + COL_TABLES_PK + ", INDEX_NAME, COLUMN_NAME, DATA_TYPE, INDEX_TYPE, IS_UNIQUE, " +
"IS_CLUSTERED, IS_ASCENDING FROM " + TB_INDEXES;
@Override
public IndexDescProto getIndexByName(String databaseName, final String indexName)
throws CatalogException {
Connection conn = null;
ResultSet res = null;
PreparedStatement pstmt = null;
IndexDescProto proto = null;
try {
int databaseId = getDatabaseId(databaseName);
String sql = GET_INDEXES_SQL + " WHERE " + COL_DATABASES_PK + "=? AND INDEX_NAME=?";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, databaseId);
pstmt.setString(2, indexName);
res = pstmt.executeQuery();
if (!res.next()) {
throw new CatalogException("ERROR: there is no index matched to " + indexName);
}
IndexDescProto.Builder builder = IndexDescProto.newBuilder();
resultToIndexDescProtoBuilder(builder, res);
String tableName = getTableName(conn, res.getInt(COL_TABLES_PK));
builder.setTableIdentifier(CatalogUtil.buildTableIdentifier(databaseName, tableName));
proto = builder.build();
} catch (SQLException se) {
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt, res);
}
return proto;
}
@Override
public IndexDescProto getIndexByColumn(final String databaseName,
final String tableName,
final String columnName) throws CatalogException {
Connection conn = null;
ResultSet res = null;
PreparedStatement pstmt = null;
IndexDescProto proto = null;
try {
int databaseId = getDatabaseId(databaseName);
String sql = GET_INDEXES_SQL + " WHERE " + COL_DATABASES_PK + "=? AND COLUMN_NAME=?";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, databaseId);
;
pstmt.setString(2, columnName);
res = pstmt.executeQuery();
if (!res.next()) {
throw new CatalogException("ERROR: there is no index matched to " + columnName);
}
IndexDescProto.Builder builder = IndexDescProto.newBuilder();
resultToIndexDescProtoBuilder(builder, res);
builder.setTableIdentifier(CatalogUtil.buildTableIdentifier(databaseName, tableName));
proto = builder.build();
} catch (SQLException se) {
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt, res);
}
return proto;
}
@Override
public boolean existIndexByName(String databaseName, final String indexName) throws CatalogException {
Connection conn = null;
ResultSet res = null;
PreparedStatement pstmt = null;
boolean exist = false;
try {
int databaseId = getDatabaseId(databaseName);
String sql =
"SELECT INDEX_NAME FROM " + TB_INDEXES + " WHERE " + COL_DATABASES_PK + "=? AND INDEX_NAME=?";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, databaseId);
pstmt.setString(2, indexName);
res = pstmt.executeQuery();
exist = res.next();
} catch (SQLException se) {
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt, res);
}
return exist;
}
@Override
public boolean existIndexByColumn(String databaseName, String tableName, String columnName)
throws CatalogException {
Connection conn = null;
ResultSet res = null;
PreparedStatement pstmt = null;
boolean exist = false;
try {
int databaseId = getDatabaseId(databaseName);
String sql =
"SELECT INDEX_NAME FROM " + TB_INDEXES + " WHERE " + COL_DATABASES_PK + "=? AND COLUMN_NAME=?";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, databaseId);
pstmt.setString(2, columnName);
res = pstmt.executeQuery();
exist = res.next();
} catch (SQLException se) {
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt, res);
}
return exist;
}
@Override
public IndexDescProto[] getIndexes(String databaseName, final String tableName)
throws CatalogException {
Connection conn = null;
ResultSet res = null;
PreparedStatement pstmt = null;
final List<IndexDescProto> protos = new ArrayList<IndexDescProto>();
try {
final int databaseId = getDatabaseId(databaseName);
final int tableId = getTableId(databaseId, databaseName, tableName);
final TableIdentifierProto tableIdentifier = CatalogUtil.buildTableIdentifier(databaseName, tableName);
String sql = GET_INDEXES_SQL + " WHERE " + COL_DATABASES_PK + "=? AND " + COL_TABLES_PK + "=?";
if (LOG.isDebugEnabled()) {
LOG.debug(sql);
}
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, databaseId);
pstmt.setInt(2, tableId);
res = pstmt.executeQuery();
while (res.next()) {
IndexDescProto.Builder builder = IndexDescProto.newBuilder();
resultToIndexDescProtoBuilder(builder, res);
builder.setTableIdentifier(tableIdentifier);
protos.add(builder.build());
}
} catch (SQLException se) {
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt, res);
}
return protos.toArray(new IndexDescProto[protos.size()]);
}
private void resultToIndexDescProtoBuilder(IndexDescProto.Builder builder,
final ResultSet res) throws SQLException {
builder.setIndexName(res.getString("index_name"));
builder.setColumn(indexResultToColumnProto(res));
builder.setIndexMethod(getIndexMethod(res.getString("index_type").trim()));
builder.setIsUnique(res.getBoolean("is_unique"));
builder.setIsClustered(res.getBoolean("is_clustered"));
builder.setIsAscending(res.getBoolean("is_ascending"));
}
/**
* INDEXS table doesn't store type_length, so we need another resultToColumnProto method
*/
private ColumnProto indexResultToColumnProto(final ResultSet res) throws SQLException {
ColumnProto.Builder builder = ColumnProto.newBuilder();
builder.setName(res.getString("column_name").trim());
Type type = getDataType(res.getString("data_type").trim());
builder.setDataType(CatalogUtil.newSimpleDataType(type));
return builder.build();
}
private ColumnProto resultToColumnProto(final ResultSet res) throws SQLException {
ColumnProto.Builder builder = ColumnProto.newBuilder();
builder.setName(res.getString("column_name").trim());
Type type = getDataType(res.getString("data_type").trim());
int typeLength = res.getInt("type_length");
if (typeLength > 0) {
builder.setDataType(CatalogUtil.newDataTypeWithLen(type, typeLength));
} else {
builder.setDataType(CatalogUtil.newSimpleDataType(type));
}
return builder.build();
}
private CatalogProtos.KeyValueSetProto resultToKeyValueSetProto(final ResultSet res) throws SQLException {
CatalogProtos.KeyValueSetProto.Builder setBuilder = CatalogProtos.KeyValueSetProto.newBuilder();
CatalogProtos.KeyValueProto.Builder builder = CatalogProtos.KeyValueProto.newBuilder();
while (res.next()) {
builder.setKey(res.getString("key_"));
builder.setValue(res.getString("value_"));
setBuilder.addKeyval(builder.build());
}
return setBuilder.build();
}
private IndexMethod getIndexMethod(final String typeStr) {
if (typeStr.equals(IndexMethod.TWO_LEVEL_BIN_TREE.toString())) {
return IndexMethod.TWO_LEVEL_BIN_TREE;
} else {
LOG.error("Cannot find a matched type aginst from '"
+ typeStr + "'");
// TODO - needs exception handling
return null;
}
}
private CatalogProtos.PartitionMethodProto resultToPartitionMethodProto(final String databaseName,
final String tableName,
final ResultSet res)
throws SQLException, InvalidProtocolBufferException {
CatalogProtos.PartitionMethodProto.Builder partBuilder = CatalogProtos.PartitionMethodProto.newBuilder();
partBuilder.setTableIdentifier(CatalogUtil.buildTableIdentifier(databaseName, tableName));
partBuilder.setPartitionType(CatalogProtos.PartitionType.valueOf(res.getString("partition_type")));
partBuilder.setExpression(res.getString("expression"));
partBuilder.setExpressionSchema(SchemaProto.parseFrom(res.getBytes("expression_schema")));
return partBuilder.build();
}
public void close() {
CatalogUtil.closeQuietly(conn);
LOG.info("Shutdown database (" + catalogUri + ")");
}
@Override
public final void addFunction(final FunctionDesc func) throws CatalogException {
// TODO - not implemented yet
}
@Override
public final void deleteFunction(final FunctionDesc func) throws CatalogException {
// TODO - not implemented yet
}
@Override
public final void existFunction(final FunctionDesc func) throws CatalogException {
// TODO - not implemented yet
}
@Override
public final List<String> getAllFunctionNames() throws CatalogException {
// TODO - not implemented yet
return null;
}
private boolean existColumn(final int tableId, final String columnName) throws CatalogException {
Connection conn ;
PreparedStatement pstmt = null;
ResultSet res = null;
boolean exist = false;
try {
String sql = "SELECT COLUMN_NAME FROM " + TB_COLUMNS + " WHERE TID = ? AND COLUMN_NAME = ?";
if (LOG.isDebugEnabled()) {
LOG.debug(sql.toString());
}
conn = getConnection();
pstmt = conn.prepareStatement(sql.toString());
pstmt.setInt(1, tableId);
pstmt.setString(2, columnName);
res = pstmt.executeQuery();
exist = res.next();
} catch (SQLException se) {
throw new CatalogException(se);
} finally {
CatalogUtil.closeQuietly(pstmt, res);
}
return exist;
}
}