Java tutorial
/* * 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.master.exec; import com.google.common.base.Preconditions; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.tajo.algebra.AlterTablespaceSetType; import org.apache.tajo.annotation.Nullable; import org.apache.tajo.catalog.*; import org.apache.tajo.catalog.exception.*; import org.apache.tajo.catalog.partition.PartitionMethodDesc; import org.apache.tajo.catalog.proto.CatalogProtos; import org.apache.tajo.catalog.proto.CatalogProtos.AlterTablespaceProto; import org.apache.tajo.conf.TajoConf; import org.apache.tajo.engine.query.QueryContext; import org.apache.tajo.master.TajoMaster; import org.apache.tajo.plan.LogicalPlan; import org.apache.tajo.plan.logical.*; import org.apache.tajo.plan.util.PlannerUtil; import org.apache.tajo.storage.StorageManager; import org.apache.tajo.storage.StorageUtil; import java.io.IOException; import java.util.ArrayList; import java.util.List; import static org.apache.tajo.TajoConstants.DEFAULT_TABLESPACE_NAME; /** * Executor for DDL statements. They are executed on only TajoMaster. */ public class DDLExecutor { private static final Log LOG = LogFactory.getLog(DDLExecutor.class); private final TajoMaster.MasterContext context; private final CatalogService catalog; private final StorageManager storageManager; public DDLExecutor(TajoMaster.MasterContext context) { this.context = context; this.catalog = context.getCatalog(); this.storageManager = context.getStorageManager(); } public boolean execute(QueryContext queryContext, LogicalPlan plan) throws IOException { LogicalNode root = ((LogicalRootNode) plan.getRootBlock().getRoot()).getChild(); switch (root.getType()) { case ALTER_TABLESPACE: AlterTablespaceNode alterTablespace = (AlterTablespaceNode) root; alterTablespace(context, queryContext, alterTablespace); return true; case CREATE_DATABASE: CreateDatabaseNode createDatabase = (CreateDatabaseNode) root; createDatabase(queryContext, createDatabase.getDatabaseName(), null, createDatabase.isIfNotExists()); return true; case DROP_DATABASE: DropDatabaseNode dropDatabaseNode = (DropDatabaseNode) root; dropDatabase(queryContext, dropDatabaseNode.getDatabaseName(), dropDatabaseNode.isIfExists()); return true; case CREATE_TABLE: CreateTableNode createTable = (CreateTableNode) root; createTable(queryContext, createTable, createTable.isIfNotExists()); return true; case DROP_TABLE: DropTableNode dropTable = (DropTableNode) root; dropTable(queryContext, dropTable.getTableName(), dropTable.isIfExists(), dropTable.isPurge()); return true; case TRUNCATE_TABLE: TruncateTableNode truncateTable = (TruncateTableNode) root; truncateTable(queryContext, truncateTable); return true; case ALTER_TABLE: AlterTableNode alterTable = (AlterTableNode) root; alterTable(context, queryContext, alterTable); return true; default: throw new InternalError("updateQuery cannot handle such query: \n" + root.toJson()); } } /** * Alter a given table */ public static void alterTablespace(final TajoMaster.MasterContext context, final QueryContext queryContext, final AlterTablespaceNode alterTablespace) { final CatalogService catalog = context.getCatalog(); final String spaceName = alterTablespace.getTablespaceName(); AlterTablespaceProto.Builder builder = AlterTablespaceProto.newBuilder(); builder.setSpaceName(spaceName); if (alterTablespace.getSetType() == AlterTablespaceSetType.LOCATION) { AlterTablespaceProto.AlterTablespaceCommand.Builder commandBuilder = AlterTablespaceProto.AlterTablespaceCommand .newBuilder(); commandBuilder.setType(AlterTablespaceProto.AlterTablespaceType.LOCATION); commandBuilder.setLocation( AlterTablespaceProto.SetLocation.newBuilder().setUri(alterTablespace.getLocation())); commandBuilder.build(); builder.addCommand(commandBuilder); } else { throw new RuntimeException("This 'ALTER TABLESPACE' is not supported yet."); } catalog.alterTablespace(builder.build()); } //-------------------------------------------------------------------------- // Database Section //-------------------------------------------------------------------------- public boolean createDatabase(@Nullable QueryContext queryContext, String databaseName, @Nullable String tablespace, boolean ifNotExists) throws IOException { String tablespaceName; if (tablespace == null) { tablespaceName = DEFAULT_TABLESPACE_NAME; } else { tablespaceName = tablespace; } // CREATE DATABASE IF NOT EXISTS boolean exists = catalog.existDatabase(databaseName); if (exists) { if (ifNotExists) { LOG.info("database \"" + databaseName + "\" is already exists."); return true; } else { throw new AlreadyExistsDatabaseException(databaseName); } } if (catalog.createDatabase(databaseName, tablespaceName)) { String normalized = databaseName; Path databaseDir = StorageUtil.concatPath(context.getConf().getVar(TajoConf.ConfVars.WAREHOUSE_DIR), normalized); FileSystem fs = databaseDir.getFileSystem(context.getConf()); fs.mkdirs(databaseDir); } return true; } public boolean dropDatabase(QueryContext queryContext, String databaseName, boolean ifExists) { boolean exists = catalog.existDatabase(databaseName); if (!exists) { if (ifExists) { // DROP DATABASE IF EXISTS LOG.info("database \"" + databaseName + "\" does not exists."); return true; } else { // Otherwise, it causes an exception. throw new NoSuchDatabaseException(databaseName); } } if (queryContext.getCurrentDatabase().equals(databaseName)) { throw new RuntimeException("ERROR: Cannot drop the current open database"); } boolean result = catalog.dropDatabase(databaseName); LOG.info("database " + databaseName + " is dropped."); return result; } //-------------------------------------------------------------------------- // Table Section //-------------------------------------------------------------------------- private TableDesc createTable(QueryContext queryContext, CreateTableNode createTable, boolean ifNotExists) throws IOException { TableMeta meta; if (createTable.hasOptions()) { meta = CatalogUtil.newTableMeta(createTable.getStorageType(), createTable.getOptions()); } else { meta = CatalogUtil.newTableMeta(createTable.getStorageType()); } if (PlannerUtil.isFileStorageType(createTable.getStorageType()) && createTable.isExternal()) { Preconditions.checkState(createTable.hasPath(), "ERROR: LOCATION must be given."); } return createTable(queryContext, createTable.getTableName(), createTable.getStorageType(), createTable.getTableSchema(), meta, createTable.getPath(), createTable.isExternal(), createTable.getPartitionMethod(), ifNotExists); } public TableDesc createTable(QueryContext queryContext, String tableName, CatalogProtos.StoreType storeType, Schema schema, TableMeta meta, Path path, boolean isExternal, PartitionMethodDesc partitionDesc, boolean ifNotExists) throws IOException { String databaseName; String simpleTableName; if (CatalogUtil.isFQTableName(tableName)) { String[] splitted = CatalogUtil.splitFQTableName(tableName); databaseName = splitted[0]; simpleTableName = splitted[1]; } else { databaseName = queryContext.getCurrentDatabase(); simpleTableName = tableName; } String qualifiedName = CatalogUtil.buildFQName(databaseName, simpleTableName); boolean exists = catalog.existsTable(databaseName, simpleTableName); if (exists) { if (ifNotExists) { LOG.info("relation \"" + qualifiedName + "\" is already exists."); return catalog.getTableDesc(databaseName, simpleTableName); } else { throw new AlreadyExistsTableException(CatalogUtil.buildFQName(databaseName, tableName)); } } TableDesc desc = new TableDesc(CatalogUtil.buildFQName(databaseName, simpleTableName), schema, meta, (path != null ? path.toUri() : null), isExternal); if (partitionDesc != null) { desc.setPartitionMethod(partitionDesc); } StorageManager.getStorageManager(queryContext.getConf(), storeType).createTable(desc, ifNotExists); if (catalog.createTable(desc)) { LOG.info("Table " + desc.getName() + " is created (" + desc.getStats().getNumBytes() + ")"); return desc; } else { LOG.info("Table creation " + tableName + " is failed."); throw new CatalogException("Cannot create table \"" + tableName + "\"."); } } /** * Drop a given named table * * @param tableName to be dropped * @param purge Remove all data if purge is true. */ public boolean dropTable(QueryContext queryContext, String tableName, boolean ifExists, boolean purge) { CatalogService catalog = context.getCatalog(); String databaseName; String simpleTableName; if (CatalogUtil.isFQTableName(tableName)) { String[] splitted = CatalogUtil.splitFQTableName(tableName); databaseName = splitted[0]; simpleTableName = splitted[1]; } else { databaseName = queryContext.getCurrentDatabase(); simpleTableName = tableName; } String qualifiedName = CatalogUtil.buildFQName(databaseName, simpleTableName); boolean exists = catalog.existsTable(qualifiedName); if (!exists) { if (ifExists) { // DROP TABLE IF EXISTS LOG.info("relation \"" + qualifiedName + "\" is already exists."); return true; } else { // Otherwise, it causes an exception. throw new NoSuchTableException(qualifiedName); } } TableDesc tableDesc = catalog.getTableDesc(qualifiedName); catalog.dropTable(qualifiedName); if (purge) { try { StorageManager.getStorageManager(queryContext.getConf(), tableDesc.getMeta().getStoreType()) .purgeTable(tableDesc); } catch (IOException e) { throw new InternalError(e.getMessage()); } } LOG.info(String.format("relation \"%s\" is " + (purge ? " purged." : " dropped."), qualifiedName)); return true; } /** * Truncate table a given table */ public void truncateTable(final QueryContext queryContext, final TruncateTableNode truncateTableNode) throws IOException { List<String> tableNames = truncateTableNode.getTableNames(); final CatalogService catalog = context.getCatalog(); String databaseName; String simpleTableName; List<TableDesc> tableDescList = new ArrayList<TableDesc>(); for (String eachTableName : tableNames) { if (CatalogUtil.isFQTableName(eachTableName)) { String[] split = CatalogUtil.splitFQTableName(eachTableName); databaseName = split[0]; simpleTableName = split[1]; } else { databaseName = queryContext.getCurrentDatabase(); simpleTableName = eachTableName; } final String qualifiedName = CatalogUtil.buildFQName(databaseName, simpleTableName); if (!catalog.existsTable(databaseName, simpleTableName)) { throw new NoSuchTableException(qualifiedName); } Path warehousePath = new Path(TajoConf.getWarehouseDir(context.getConf()), databaseName); TableDesc tableDesc = catalog.getTableDesc(databaseName, simpleTableName); Path tablePath = new Path(tableDesc.getPath()); if (tablePath.getParent() == null || !tablePath.getParent().toUri().getPath().equals(warehousePath.toUri().getPath())) { throw new IOException("Can't truncate external table:" + eachTableName + ", data dir=" + tablePath + ", warehouse dir=" + warehousePath); } tableDescList.add(tableDesc); } for (TableDesc eachTable : tableDescList) { Path path = new Path(eachTable.getPath()); LOG.info("Truncate table: " + eachTable.getName() + ", delete all data files in " + path); FileSystem fs = path.getFileSystem(context.getConf()); FileStatus[] files = fs.listStatus(path); if (files != null) { for (FileStatus eachFile : files) { fs.delete(eachFile.getPath(), true); } } } } /** * ALTER TABLE SET ... */ public void alterTable(TajoMaster.MasterContext context, final QueryContext queryContext, final AlterTableNode alterTable) throws IOException { final CatalogService catalog = context.getCatalog(); final String tableName = alterTable.getTableName(); String databaseName; String simpleTableName; if (CatalogUtil.isFQTableName(tableName)) { String[] split = CatalogUtil.splitFQTableName(tableName); databaseName = split[0]; simpleTableName = split[1]; } else { databaseName = queryContext.getCurrentDatabase(); simpleTableName = tableName; } final String qualifiedName = CatalogUtil.buildFQName(databaseName, simpleTableName); if (!catalog.existsTable(databaseName, simpleTableName)) { throw new NoSuchTableException(qualifiedName); } switch (alterTable.getAlterTableOpType()) { case RENAME_TABLE: if (!catalog.existsTable(databaseName, simpleTableName)) { throw new NoSuchTableException(alterTable.getTableName()); } if (catalog.existsTable(databaseName, alterTable.getNewTableName())) { throw new AlreadyExistsTableException(alterTable.getNewTableName()); } TableDesc desc = catalog.getTableDesc(databaseName, simpleTableName); if (!desc.isExternal()) { // if the table is the managed table Path oldPath = StorageUtil.concatPath(context.getConf().getVar(TajoConf.ConfVars.WAREHOUSE_DIR), databaseName, simpleTableName); Path newPath = StorageUtil.concatPath(context.getConf().getVar(TajoConf.ConfVars.WAREHOUSE_DIR), databaseName, alterTable.getNewTableName()); FileSystem fs = oldPath.getFileSystem(context.getConf()); if (!fs.exists(oldPath)) { throw new IOException("No such a table directory: " + oldPath); } if (fs.exists(newPath)) { throw new IOException("Already table directory exists: " + newPath); } fs.rename(oldPath, newPath); } catalog.alterTable(CatalogUtil.renameTable(qualifiedName, alterTable.getNewTableName(), AlterTableType.RENAME_TABLE)); break; case RENAME_COLUMN: if (existColumnName(qualifiedName, alterTable.getNewColumnName())) { throw new ColumnNameAlreadyExistException(alterTable.getNewColumnName()); } catalog.alterTable(CatalogUtil.renameColumn(qualifiedName, alterTable.getColumnName(), alterTable.getNewColumnName(), AlterTableType.RENAME_COLUMN)); break; case ADD_COLUMN: if (existColumnName(qualifiedName, alterTable.getAddNewColumn().getSimpleName())) { throw new ColumnNameAlreadyExistException(alterTable.getAddNewColumn().getSimpleName()); } catalog.alterTable(CatalogUtil.addNewColumn(qualifiedName, alterTable.getAddNewColumn(), AlterTableType.ADD_COLUMN)); break; default: //TODO } } private boolean existColumnName(String tableName, String columnName) { final TableDesc tableDesc = catalog.getTableDesc(tableName); return tableDesc.getSchema().containsByName(columnName) ? true : false; } }