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")