public void dropTable()

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));
        }
    }