| 1534 | } |
| 1535 | |
| 1536 | void InterpreterSystemQuery::dropReplica(ASTSystemQuery & query) |
| 1537 | { |
| 1538 | auto component_guard = Coordination::setCurrentComponent("InterpreterSystemQuery::dropReplica"); |
| 1539 | if (query.replica.empty()) |
| 1540 | throw Exception(ErrorCodes::BAD_ARGUMENTS, "Replica name is empty"); |
| 1541 | |
| 1542 | if (!table_id.empty()) |
| 1543 | { |
| 1544 | getContext()->checkAccess(AccessType::SYSTEM_DROP_REPLICA, table_id); |
| 1545 | StoragePtr table = DatabaseCatalog::instance().getTable(table_id, getContext()); |
| 1546 | |
| 1547 | if (!dropStorageReplica(query.replica, table)) |
| 1548 | throw Exception(ErrorCodes::BAD_ARGUMENTS, table_is_not_replicated.data(), table_id.getNameForLogs()); |
| 1549 | } |
| 1550 | else if (query.database) |
| 1551 | { |
| 1552 | getContext()->checkAccess(AccessType::SYSTEM_DROP_REPLICA, query.getDatabase()); |
| 1553 | DatabasePtr database = DatabaseCatalog::instance().getDatabase(query.getDatabase()); |
| 1554 | dropStorageReplicasFromDatabase(query.replica, database); |
| 1555 | LOG_TRACE(log, "Dropped replica {} from database {}", query.replica, backQuoteIfNeed(database->getDatabaseName())); |
| 1556 | } |
| 1557 | else if (query.is_drop_whole_replica) |
| 1558 | { |
| 1559 | auto databases = DatabaseCatalog::instance().getDatabases(GetDatabasesOptions{.with_remote_databases = false}); |
| 1560 | auto access = getContext()->getAccess(); |
| 1561 | bool access_is_granted_globally = access->isGranted(AccessType::SYSTEM_DROP_REPLICA); |
| 1562 | |
| 1563 | /// Instead of silently failing, check the permissions to delete all databases in advance. |
| 1564 | /// Throw an exception to user if the user doesn't have enough privileges to drop the replica. |
| 1565 | /// Include the databases that the user needs privileges for in the exception |
| 1566 | std::vector<String> required_access; |
| 1567 | for (auto & elem : databases) |
| 1568 | { |
| 1569 | if (!access_is_granted_globally && !access->isGranted(AccessType::SYSTEM_DROP_REPLICA, elem.first)) |
| 1570 | { |
| 1571 | required_access.emplace_back(elem.first); |
| 1572 | LOG_INFO(log, "? Access {} denied, skipping database {}", "SYSTEM DROP REPLICA", elem.first); |
| 1573 | } |
| 1574 | } |
| 1575 | |
| 1576 | if (!required_access.empty()) |
| 1577 | throw Exception( |
| 1578 | ErrorCodes::ACCESS_DENIED, |
| 1579 | "Access denied for {}. Not enough permissions to drop these databases: {}", |
| 1580 | "SYSTEM DROP REPLICA", |
| 1581 | fmt::join(required_access, ", ")); |
| 1582 | |
| 1583 | /// If we are here, then the user has the necessary access to drop the replica, continue with the operation. |
| 1584 | for (auto & elem : databases) |
| 1585 | { |
| 1586 | DatabasePtr & database = elem.second; |
| 1587 | dropStorageReplicasFromDatabase(query.replica, database); |
| 1588 | LOG_TRACE(log, "Dropped replica {} from database {}", query.replica, backQuoteIfNeed(database->getDatabaseName())); |
| 1589 | } |
| 1590 | } |
| 1591 | else if (query.replica_zk_path.empty()) |
| 1592 | { |
| 1593 | throw Exception(ErrorCodes::BAD_ARGUMENTS, "ZooKeeper path is empty"); |
no test coverage detected