diff --git a/dbms/src/Databases/DatabaseOrdinary.cpp b/dbms/src/Databases/DatabaseOrdinary.cpp index 25b3eb652b5f6ff58c2d94a87190f1bf1f3c3e21..0bf703887ef079896ce09e55760b912914afc208 100644 --- a/dbms/src/Databases/DatabaseOrdinary.cpp +++ b/dbms/src/Databases/DatabaseOrdinary.cpp @@ -135,7 +135,25 @@ void DatabaseOrdinary::loadTables( if (endsWith(dir_it.name(), ".sql.bak")) continue; - /// There are files .sql.tmp - delete. + // There are files that we tried to delete previously + const std::string tmp_drop_ext = ".sql.tmp_drop"; + if (endsWith(dir_it.name(), ".sql.tmp_drop")) + { + const std::string table_name = dir_it.name().substr(0, dir_it.name().size() - tmp_drop_ext.size()); + if (Poco::File(data_path + '/' + table_name).exists()) + { + Poco::File(dir_it->path()).renameTo(table_name + ".sql"); + LOG_WARNING(log, "Table was not dropped previously"); + } + else + { + LOG_INFO(log, "Removing file " << dir_it->path()); + Poco::File(dir_it->path()).remove(); + } + continue; + } + + /// There are files .sql.tmp and .sql.tmp_drop - delete if (endsWith(dir_it.name(), ".sql.tmp")) { LOG_INFO(log, "Removing file " << dir_it->path()); @@ -302,6 +320,12 @@ void DatabaseOrdinary::removeTable( } catch (...) { + try + { + Poco::File(table_metadata_path + ".tmp_drop").remove(); + return; + } + catch (...) {} attachTable(table_name, res); throw; } @@ -363,7 +387,6 @@ void DatabaseOrdinary::renameTable( throw Exception("Moving tables between databases of different engines is not supported", ErrorCodes::NOT_IMPLEMENTED); StoragePtr table = tryGetTable(context, table_name); - if (!table) throw Exception("Table " + name + "." + table_name + " doesn't exist.", ErrorCodes::UNKNOWN_TABLE); diff --git a/dbms/src/Interpreters/InterpreterDropQuery.cpp b/dbms/src/Interpreters/InterpreterDropQuery.cpp index 91213b6100e67ae8d275aebcc0066ba2fed932d4..13911d930e1318f3010f712d374783d76a0dca33 100644 --- a/dbms/src/Interpreters/InterpreterDropQuery.cpp +++ b/dbms/src/Interpreters/InterpreterDropQuery.cpp @@ -90,11 +90,26 @@ BlockIO InterpreterDropQuery::executeToTable(String & database_name_, String & t /// If table was already dropped by anyone, an exception will be thrown auto table_lock = database_and_table.second->lockExclusively(context.getCurrentQueryId()); - /// Delete table metadata and table itself from memory + const auto prev_metadata_name = database_and_table.first->getMetadataPath() + database_and_table.second->getTableName() + ".sql"; + const auto drop_metadata_name = database_and_table.first->getMetadataPath() + database_and_table.second->getTableName() + ".sql.tmp_drop"; + + try + { + Poco::File(prev_metadata_name).renameTo(drop_metadata_name); + std::cout << "RENAMED" << std::endl; + /// Delete table data + database_and_table.second->drop(); + } + catch (...) + { + Poco::File(drop_metadata_name).renameTo(prev_metadata_name); + std::cout << "RENAMED BACK" << std::endl; + throw; + } + + /// Delete table metadata and table itself from memory database_and_table.first->removeTable(context, database_and_table.second->getTableName()); - /// Delete table data - database_and_table.second->drop(); database_and_table.second->is_dropped = true; String database_data_path = database_and_table.first->getDataPath(); diff --git a/dbms/tests/integration/test_atomic_drop_table/__init__.py b/dbms/tests/integration/test_atomic_drop_table/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/dbms/tests/integration/test_atomic_drop_table/configs/config.d/zookeeper_session_timeout.xml b/dbms/tests/integration/test_atomic_drop_table/configs/config.d/zookeeper_session_timeout.xml new file mode 100644 index 0000000000000000000000000000000000000000..071725b5391fa58decdadb7fda9c735256a244b3 --- /dev/null +++ b/dbms/tests/integration/test_atomic_drop_table/configs/config.d/zookeeper_session_timeout.xml @@ -0,0 +1,6 @@ + + + + 3000 + + diff --git a/dbms/tests/integration/test_atomic_drop_table/configs/remote_servers.xml b/dbms/tests/integration/test_atomic_drop_table/configs/remote_servers.xml new file mode 100644 index 0000000000000000000000000000000000000000..538aa72d386081e28ca7edd3a33f052028f570a0 --- /dev/null +++ b/dbms/tests/integration/test_atomic_drop_table/configs/remote_servers.xml @@ -0,0 +1,14 @@ + + + + + true + + shard_0 + node1 + 9000 + + + + + diff --git a/dbms/tests/integration/test_atomic_drop_table/test.py b/dbms/tests/integration/test_atomic_drop_table/test.py new file mode 100644 index 0000000000000000000000000000000000000000..4098cce446bbaf27067e8446293f5039c681d680 --- /dev/null +++ b/dbms/tests/integration/test_atomic_drop_table/test.py @@ -0,0 +1,39 @@ +import time +import pytest + +from helpers.network import PartitionManager +from helpers.cluster import ClickHouseCluster + + +cluster = ClickHouseCluster(__file__) +node1 = cluster.add_instance('node1', config_dir="configs", with_zookeeper=True) + + +@pytest.fixture(scope="module") +def start_cluster(): + try: + cluster.start() + node1.query("CREATE DATABASE zktest;") + node1.query( + ''' + CREATE TABLE zktest.atomic_drop_table (n UInt32) + ENGINE = ReplicatedMergeTree('/clickhouse/zktest/tables/atomic_drop_table', 'node1') + PARTITION BY n ORDER BY n + ''' + ) + yield cluster + finally: + cluster.shutdown() + +def test_atomic_delete_with_stopped_zookeeper(start_cluster): + + node1.query("select * from zktest.atomic_drop_table") + node1.query("insert into zktest.atomic_drop_table values (8192)") + + with PartitionManager() as pm: + pm.drop_instance_zk_connections(node1) + error = node1.query_and_get_error("DROP TABLE zktest.atomic_drop_table") + assert error != "" + + time.sleep(5) + assert '8192' in node1.query("select * from zktest.atomic_drop_table")