}
public void alterTable(RawStore msdb, Warehouse wh, String dbname,
String name, Table newt) throws InvalidOperationException, MetaException {
if (newt == null) {
throw new InvalidOperationException("New table is invalid: " + newt);
}
if (!MetaStoreUtils.validateName(newt.getTableName())) {
throw new InvalidOperationException(newt.getTableName()
+ " is not a valid object name");
}
String validate = MetaStoreUtils.validateTblColumns(newt.getSd().getCols());
if (validate != null) {
throw new InvalidOperationException("Invalid column " + validate);
}
Path srcPath = null;
FileSystem srcFs = null;
Path destPath = null;
FileSystem destFs = null;
boolean success = false;
boolean moveData = false;
boolean rename = false;
Table oldt = null;
List<ObjectPair<Partition, String>> altps = new ArrayList<ObjectPair<Partition, String>>();
try {
msdb.openTransaction();
name = name.toLowerCase();
dbname = dbname.toLowerCase();
// check if table with the new name already exists
if (!newt.getTableName().equalsIgnoreCase(name)
|| !newt.getDbName().equalsIgnoreCase(dbname)) {
if (msdb.getTable(newt.getDbName(), newt.getTableName()) != null) {
throw new InvalidOperationException("new table " + newt.getDbName()
+ "." + newt.getTableName() + " already exists");
}
rename = true;
}
// get old table
oldt = msdb.getTable(dbname, name);
if (oldt == null) {
throw new InvalidOperationException("table " + newt.getDbName() + "."
+ newt.getTableName() + " doesn't exist");
}
if (HiveConf.getBoolVar(hiveConf,
HiveConf.ConfVars.METASTORE_DISALLOW_INCOMPATIBLE_COL_TYPE_CHANGES,
false)) {
// Throws InvalidOperationException if the new column types are not
// compatible with the current column types.
MetaStoreUtils.throwExceptionIfIncompatibleColTypeChange(
oldt.getSd().getCols(), newt.getSd().getCols());
}
//check that partition keys have not changed, except for virtual views
//however, allow the partition comments to change
boolean partKeysPartiallyEqual = checkPartialPartKeysEqual(oldt.getPartitionKeys(),
newt.getPartitionKeys());
if(!oldt.getTableType().equals(TableType.VIRTUAL_VIEW.toString())){
if (oldt.getPartitionKeys().size() != newt.getPartitionKeys().size()
|| !partKeysPartiallyEqual) {
throw new InvalidOperationException(
"partition keys can not be changed.");
}
}
// if this alter is a rename, the table is not a virtual view, the user
// didn't change the default location (or new location is empty), and
// table is not an external table, that means user is asking metastore to
// move data to the new location corresponding to the new name
if (rename
&& !oldt.getTableType().equals(TableType.VIRTUAL_VIEW.toString())
&& (oldt.getSd().getLocation().compareTo(newt.getSd().getLocation()) == 0
|| StringUtils.isEmpty(newt.getSd().getLocation()))
&& !MetaStoreUtils.isExternalTable(oldt)) {
srcPath = new Path(oldt.getSd().getLocation());
srcFs = wh.getFs(srcPath);
// that means user is asking metastore to move data to new location
// corresponding to the new name
// get new location
Path databasePath = constructRenamedPath(
wh.getDefaultDatabasePath(newt.getDbName()), srcPath);
destPath = new Path(databasePath, newt.getTableName());
destFs = wh.getFs(destPath);
newt.getSd().setLocation(destPath.toString());
moveData = true;
// check that destination does not exist otherwise we will be
// overwriting data
// check that src and dest are on the same file system
if (!FileUtils.equalsFileSystem(srcFs, destFs)) {
throw new InvalidOperationException("table new location " + destPath
+ " is on a different file system than the old location "
+ srcPath + ". This operation is not supported");
}
try {
srcFs.exists(srcPath); // check that src exists and also checks
// permissions necessary
if (destFs.exists(destPath)) {
throw new InvalidOperationException("New location for this table "
+ newt.getDbName() + "." + newt.getTableName()
+ " already exists : " + destPath);
}
} catch (IOException e) {
Warehouse.closeFs(srcFs);
Warehouse.closeFs(destFs);
throw new InvalidOperationException("Unable to access new location "
+ destPath + " for table " + newt.getDbName() + "."
+ newt.getTableName());
}
String oldTblLocPath = srcPath.toUri().getPath();
String newTblLocPath = destPath.toUri().getPath();
// also the location field in partition
List<Partition> parts = msdb.getPartitions(dbname, name, -1);
for (Partition part : parts) {
String oldPartLoc = part.getSd().getLocation();
if (oldPartLoc.contains(oldTblLocPath)) {
URI oldUri = new Path(oldPartLoc).toUri();
String newPath = oldUri.getPath().replace(oldTblLocPath, newTblLocPath);
Path newPartLocPath = new Path(oldUri.getScheme(), oldUri.getAuthority(), newPath);
altps.add(ObjectPair.create(part, part.getSd().getLocation()));
part.getSd().setLocation(newPartLocPath.toString());
msdb.alterPartition(dbname, name, part.getValues(), part);
}
}
} else if (MetaStoreUtils.requireCalStats(hiveConf, null, null, newt) &&
(newt.getPartitionKeysSize() == 0)) {
Database db = msdb.getDatabase(newt.getDbName());
// Update table stats. For partitioned table, we update stats in
// alterPartition()
MetaStoreUtils.updateUnpartitionedTableStatsFast(db, newt, wh, false, true);
}
// now finally call alter table
msdb.alterTable(dbname, name, newt);
// commit the changes
success = msdb.commitTransaction();
} catch (InvalidObjectException e) {
LOG.debug(e);
throw new InvalidOperationException(
"Unable to change partition or table."
+ " Check metastore logs for detailed stack." + e.getMessage());
} catch (NoSuchObjectException e) {
LOG.debug(e);
throw new InvalidOperationException(
"Unable to change partition or table. Database " + dbname + " does not exist"
+ " Check metastore logs for detailed stack." + e.getMessage());
} finally {
if (!success) {
msdb.rollbackTransaction();
}
if (success && moveData) {
// change the file name in hdfs
// check that src exists otherwise there is no need to copy the data
// rename the src to destination
try {
if (srcFs.exists(srcPath) && !srcFs.rename(srcPath, destPath)) {
throw new IOException("Renaming " + srcPath + " to " + destPath + " is failed");
}
} catch (IOException e) {
boolean revertMetaDataTransaction = false;
try {
msdb.openTransaction();
msdb.alterTable(dbname, newt.getTableName(), oldt);
for (ObjectPair<Partition, String> pair : altps) {
Partition part = pair.getFirst();
part.getSd().setLocation(pair.getSecond());
msdb.alterPartition(dbname, name, part.getValues(), part);
}
revertMetaDataTransaction = msdb.commitTransaction();
} catch (Exception e1) {
// we should log this for manual rollback by administrator
LOG.error("Reverting metadata by HDFS operation failure failed During HDFS operation failed", e1);
LOG.error("Table " + Warehouse.getQualifiedName(newt) +
" should be renamed to " + Warehouse.getQualifiedName(oldt));
LOG.error("Table " + Warehouse.getQualifiedName(newt) +
" should have path " + srcPath);
for (ObjectPair<Partition, String> pair : altps) {
LOG.error("Partition " + Warehouse.getQualifiedName(pair.getFirst()) +
" should have path " + pair.getSecond());
}
if (!revertMetaDataTransaction) {
msdb.rollbackTransaction();
}
}
throw new InvalidOperationException("Unable to access old location "
+ srcPath + " for table " + dbname + "." + name);
}
}
}
if (!success) {