in phoenix-core-server/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java [2889:3131]
public void dropTable(RpcController controller, DropTableRequest request,
RpcCallback<MetaDataResponse> done) {
MetaDataResponse.Builder builder = MetaDataResponse.newBuilder();
boolean isCascade = request.getCascade();
byte[][] rowKeyMetaData = new byte[3][];
String tableType = request.getTableType();
byte[] schemaName = null;
byte[] tableOrViewName = null;
boolean dropTableStats = false;
final int clientVersion = request.getClientVersion();
try {
List<Mutation> tableMetadata = ProtobufUtil.getMutations(request);
List<Mutation> childLinkMutations = Lists.newArrayList();
MetaDataUtil.getTenantIdAndSchemaAndTableName(tableMetadata, rowKeyMetaData);
byte[] tenantIdBytes = rowKeyMetaData[PhoenixDatabaseMetaData.TENANT_ID_INDEX];
schemaName = rowKeyMetaData[PhoenixDatabaseMetaData.SCHEMA_NAME_INDEX];
tableOrViewName = rowKeyMetaData[PhoenixDatabaseMetaData.TABLE_NAME_INDEX];
String fullTableName = SchemaUtil.getTableName(schemaName, tableOrViewName);
PTableType pTableType = PTableType.fromSerializedValue(tableType);
// Disallow deletion of a system table
if (pTableType == PTableType.SYSTEM) {
builder.setReturnCode(MetaDataProtos.MutationCode.UNALLOWED_TABLE_MUTATION);
builder.setMutationTime(EnvironmentEdgeManager.currentTimeMillis());
done.run(builder.build());
return;
}
List<byte[]> tableNamesToDelete = Lists.newArrayList();
List<SharedTableState> sharedTablesToDelete = Lists.newArrayList();
byte[] lockKey = SchemaUtil.getTableKey(tenantIdBytes, schemaName, tableOrViewName);
Region region = env.getRegion();
MetaDataMutationResult result = checkTableKeyInRegion(lockKey, region);
if (result != null) {
done.run(MetaDataMutationResult.toProto(result));
return;
}
byte[] parentTableName = MetaDataUtil.getParentTableName(tableMetadata);
byte[] parentLockKey = null;
// Only lock parent table for indexes
if (parentTableName != null && pTableType == PTableType.INDEX) {
parentLockKey = SchemaUtil.getTableKey(tenantIdBytes, schemaName, parentTableName);
result = checkTableKeyInRegion(parentLockKey, region);
if (result != null) {
done.run(MetaDataMutationResult.toProto(result));
return;
}
}
long clientTimeStamp = MetaDataUtil.getClientTimeStamp(tableMetadata);
PTable loadedTable = doGetTable(tenantIdBytes, schemaName, tableOrViewName,
clientTimeStamp, null, request.getClientVersion());
if (loadedTable == null) {
builder.setReturnCode(MetaDataProtos.MutationCode.TABLE_NOT_FOUND);
builder.setMutationTime(EnvironmentEdgeManager.currentTimeMillis());
done.run(builder.build());
return;
}
getCoprocessorHost().preDropTable(Bytes.toString(tenantIdBytes),
SchemaUtil.getTableName(schemaName, tableOrViewName),
TableName.valueOf(loadedTable.getPhysicalName().getBytes()),
getParentPhysicalTableName(loadedTable), pTableType, loadedTable.getIndexes());
if (pTableType == PTableType.TABLE || pTableType == PTableType.VIEW) {
// check to see if the table has any child views
try (Table hTable = ServerUtil.getHTableForCoprocessorScan(env,
getSystemTableForChildLinks(clientVersion, env.getConfiguration()))) {
// This call needs to be done before acquiring the row lock on the header row
// for the table/view being dropped, otherwise the calls to resolve its child
// views via PhoenixRuntime.getTableNoCache() will deadlock since this call
// itself needs to get the parent table which needs to acquire a write lock
// on the same header row
Pair<List<PTable>, List<TableInfo>> descendantViews =
findAllDescendantViews(hTable, env.getConfiguration(),
tenantIdBytes, schemaName, tableOrViewName, clientTimeStamp,
true, true);
List<PTable> legitimateChildViews = descendantViews.getFirst();
List<TableInfo> orphanChildViews = descendantViews.getSecond();
if (!legitimateChildViews.isEmpty()) {
if (!isCascade) {
LOGGER.error("DROP without CASCADE on tables or views with child views "
+ "is not permitted");
// DROP without CASCADE on tables/views with child views is
// not permitted
builder.setReturnCode(
MetaDataProtos.MutationCode.UNALLOWED_TABLE_MUTATION);
builder.setMutationTime(EnvironmentEdgeManager.currentTimeMillis());
done.run(builder.build());
return;
}
if (clientVersion < MIN_SPLITTABLE_SYSTEM_CATALOG &&
!SchemaUtil.getPhysicalTableName(SYSTEM_CHILD_LINK_NAME_BYTES,
env.getConfiguration()).equals(hTable.getName())) {
// (See PHOENIX-5544) For an old client connecting to a non-upgraded
// server, we disallow dropping a base table/view that has child views.
LOGGER.error("Dropping a table or view that has child views is "
+ "not permitted for old clients connecting to a new server "
+ "with old metadata (even if CASCADE is provided). "
+ "Please upgrade the client at least to "
+ MIN_SPLITTABLE_SYSTEM_CATALOG_VERSION);
builder.setReturnCode(
MetaDataProtos.MutationCode.UNALLOWED_TABLE_MUTATION);
builder.setMutationTime(EnvironmentEdgeManager.currentTimeMillis());
done.run(builder.build());
return;
}
}
// If the CASCADE option is provided and we have at least one legitimate/orphan
// view stemming from this parent and the client is 4.15+ (or older but
// connecting to an upgraded server), we use the SYSTEM.TASK table to
// asynchronously drop child views
if (isCascade && !(legitimateChildViews.isEmpty() && orphanChildViews.isEmpty())
&& (clientVersion >= MIN_SPLITTABLE_SYSTEM_CATALOG ||
SchemaUtil.getPhysicalTableName(SYSTEM_CHILD_LINK_NAME_BYTES,
env.getConfiguration()).equals(hTable.getName()))) {
try (PhoenixConnection conn = getServerConnectionForMetaData(
env.getConfiguration()).unwrap(PhoenixConnection.class)) {
ServerTask.addTask(new SystemTaskParams.SystemTaskParamsBuilder()
.setConn(conn)
.setTaskType(PTable.TaskType.DROP_CHILD_VIEWS)
.setTenantId(Bytes.toString(tenantIdBytes))
.setSchemaName(Bytes.toString(schemaName))
.setTableName(Bytes.toString(tableOrViewName))
.setTaskStatus(
PTable.TaskStatus.CREATED.toString())
.setData(null)
.setPriority(null)
.setStartTs(null)
.setEndTs(null)
.setAccessCheckEnabled(this.accessCheckEnabled)
.build());
} catch (Throwable t) {
LOGGER.error("Adding a task to drop child views failed!", t);
}
}
}
}
List<RowLock> locks = Lists.newArrayList();
try {
acquireLock(region, lockKey, locks, false);
if (parentLockKey != null) {
acquireLock(region, parentLockKey, locks, false);
}
List<InvalidateServerMetadataCacheRequest> requests = new ArrayList<>();
requests.add(new InvalidateServerMetadataCacheRequest(tenantIdBytes, schemaName,
tableOrViewName));
if (pTableType == INDEX) {
requests.add(new InvalidateServerMetadataCacheRequest(tenantIdBytes, schemaName,
parentTableName));
long currentTimestamp = EnvironmentEdgeManager.currentTimeMillis();
// If table type is index, then update the last ddl timestamp of the parent
// table or immediate parent view.
tableMetadata.add(MetaDataUtil.getLastDDLTimestampUpdate(parentLockKey,
currentTimestamp, currentTimestamp));
}
invalidateServerMetadataCache(requests);
List<ImmutableBytesPtr> invalidateList = new ArrayList<>();
result = doDropTable(lockKey, tenantIdBytes, schemaName, tableOrViewName,
parentTableName, PTableType.fromSerializedValue(tableType), tableMetadata,
childLinkMutations, invalidateList, tableNamesToDelete,
sharedTablesToDelete, request.getClientVersion());
if (result.getMutationCode() != MutationCode.TABLE_ALREADY_EXISTS) {
done.run(MetaDataMutationResult.toProto(result));
return;
}
Cache<ImmutableBytesPtr, PMetaDataEntity> metaDataCache =
GlobalCache.getInstance(this.env).getMetaDataCache();
List<Mutation> localMutations =
Lists.newArrayListWithExpectedSize(tableMetadata.size());
List<Mutation> remoteMutations = Lists.newArrayList();
separateLocalAndRemoteMutations(region, tableMetadata, localMutations,
remoteMutations);
if (!remoteMutations.isEmpty()) {
// while dropping a table all the mutations should be local
String msg = "Found unexpected mutations while dropping table "
+ SchemaUtil.getTableName(schemaName, tableOrViewName);
LOGGER.error(msg);
for (Mutation m : remoteMutations) {
LOGGER.debug("Mutation rowkey : " + Bytes.toStringBinary(m.getRow()));
LOGGER.debug("Mutation family cell map : " + m.getFamilyCellMap());
}
throw new IllegalStateException(msg);
}
// Update SYSTEM.CATALOG indexes only for
// 1. ordinary table/index mutations (drop table/index).
// 2. When dropping system indexes itself, no further index processing is required.
boolean
updateCatalogIndexes =
(pTableType != INDEX) || (!Bytes.toString(schemaName)
.equalsIgnoreCase(PhoenixDatabaseMetaData.SYSTEM_CATALOG_SCHEMA));
// drop rows from catalog on this region
mutateRowsWithLocks(this.accessCheckEnabled, env, region, localMutations,
Collections.<byte[]>emptySet(), HConstants.NO_NONCE, HConstants.NO_NONCE,
updateCatalogIndexes);
long currentTime = MetaDataUtil.getClientTimeStamp(tableMetadata);
for (ImmutableBytesPtr ckey : invalidateList) {
PTable table = newDeletedTableMarker(currentTime);
metaDataCache.put(ckey, table);
metricsSource.incrementMetadataCacheAddCount();
metricsSource.incrementMetadataCacheUsedSize(table.getEstimatedSize());
}
if (parentLockKey != null) {
ImmutableBytesPtr parentCacheKey = new ImmutableBytesPtr(parentLockKey);
metaDataCache.invalidate(parentCacheKey);
}
// after the view metadata is dropped, drop parent->child link
MetaDataResponse response = processRemoteRegionMutations(
getSystemTableForChildLinks(request.getClientVersion(),
env.getConfiguration()).getName(), childLinkMutations,
MetaDataProtos.MutationCode.UNABLE_TO_DELETE_CHILD_LINK);
if (response != null) {
done.run(response);
return;
}
done.run(MetaDataMutationResult.toProto(result));
dropTableStats = true;
updateDropTableDdlSuccessMetrics(pTableType);
LOGGER.info("{} dropped successfully, tableName: {}", pTableType, fullTableName);
} finally {
ServerUtil.releaseRowLocks(locks);
if (dropTableStats) {
Thread statsDeleteHandler = new Thread(new StatsDeleteHandler(env,
loadedTable, tableNamesToDelete, sharedTablesToDelete),
"thread-statsdeletehandler");
statsDeleteHandler.setDaemon(true);
statsDeleteHandler.start();
}
}
} catch (Throwable t) {
LOGGER.error("dropTable failed", t);
ProtobufUtil.setControllerException(controller, ClientUtil.createIOException(
SchemaUtil.getTableName(schemaName, tableOrViewName), t));
}
}