leveldb::DestroyDB will now delete empty directories.

Env's that filtered out dot files ("." and "..") would return an
empty vector of children causing DestroyDB to do nothing. This fixes
https://github.com/google/leveldb/issues/215

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=172501335
This commit is contained in:
cmumford 2017-10-17 13:05:47 -07:00 committed by Victor Costan
parent 23162ca1c6
commit 0509414f85
3 changed files with 89 additions and 4 deletions

View File

@ -1537,15 +1537,15 @@ Snapshot::~Snapshot() {
Status DestroyDB(const std::string& dbname, const Options& options) { Status DestroyDB(const std::string& dbname, const Options& options) {
Env* env = options.env; Env* env = options.env;
std::vector<std::string> filenames; std::vector<std::string> filenames;
// Ignore error in case directory does not exist Status result = env->GetChildren(dbname, &filenames);
env->GetChildren(dbname, &filenames); if (!result.ok()) {
if (filenames.empty()) { // Ignore error in case directory does not exist
return Status::OK(); return Status::OK();
} }
FileLock* lock; FileLock* lock;
const std::string lockname = LockFileName(dbname); const std::string lockname = LockFileName(dbname);
Status result = env->LockFile(lockname, &lock); result = env->LockFile(lockname, &lock);
if (result.ok()) { if (result.ok()) {
uint64_t number; uint64_t number;
FileType type; FileType type;

View File

@ -61,6 +61,36 @@ void DelayMilliseconds(int millis) {
} }
} }
// Test Env to override default Env behavior for testing.
class TestEnv : public EnvWrapper {
public:
explicit TestEnv(Env* base) : EnvWrapper(base), ignore_dot_files_(false) {}
void SetIgnoreDotFiles(bool ignored) { ignore_dot_files_ = ignored; }
Status GetChildren(const std::string& dir,
std::vector<std::string>* result) override {
Status s = target()->GetChildren(dir, result);
if (!s.ok() || !ignore_dot_files_) {
return s;
}
std::vector<std::string>::iterator it = result->begin();
while (it != result->end()) {
if ((*it == ".") || (*it == "..")) {
it = result->erase(it);
} else {
++it;
}
}
return s;
}
private:
bool ignore_dot_files_;
};
// Special Env used to delay background operations // Special Env used to delay background operations
class SpecialEnv : public EnvWrapper { class SpecialEnv : public EnvWrapper {
public: public:
@ -1561,6 +1591,58 @@ TEST(DBTest, DBOpen_Options) {
db = NULL; db = NULL;
} }
TEST(DBTest, DestroyEmptyDir) {
std::string dbname = test::TmpDir() + "/db_empty_dir";
TestEnv env(Env::Default());
env.DeleteDir(dbname);
ASSERT_TRUE(!env.FileExists(dbname));
Options opts;
opts.env = &env;
ASSERT_OK(env.CreateDir(dbname));
ASSERT_TRUE(env.FileExists(dbname));
std::vector<std::string> children;
ASSERT_OK(env.GetChildren(dbname, &children));
// The POSIX env does not filter out '.' and '..' special files.
ASSERT_EQ(2, children.size());
ASSERT_OK(DestroyDB(dbname, opts));
ASSERT_TRUE(!env.FileExists(dbname));
// Should also be destroyed if Env is filtering out dot files.
env.SetIgnoreDotFiles(true);
ASSERT_OK(env.CreateDir(dbname));
ASSERT_TRUE(env.FileExists(dbname));
ASSERT_OK(env.GetChildren(dbname, &children));
ASSERT_EQ(0, children.size());
ASSERT_OK(DestroyDB(dbname, opts));
ASSERT_TRUE(!env.FileExists(dbname));
}
TEST(DBTest, DestroyOpenDB) {
std::string dbname = test::TmpDir() + "/open_db_dir";
env_->DeleteDir(dbname);
ASSERT_TRUE(!env_->FileExists(dbname));
Options opts;
opts.create_if_missing = true;
DB* db = NULL;
ASSERT_OK(DB::Open(opts, dbname, &db));
ASSERT_TRUE(db != NULL);
// Must fail to destroy an open db.
ASSERT_TRUE(env_->FileExists(dbname));
ASSERT_TRUE(!DestroyDB(dbname, Options()).ok());
ASSERT_TRUE(env_->FileExists(dbname));
delete db;
db = NULL;
// Should succeed destroying a closed db.
ASSERT_OK(DestroyDB(dbname, Options()));
ASSERT_TRUE(!env_->FileExists(dbname));
}
TEST(DBTest, Locking) { TEST(DBTest, Locking) {
DB* db2 = NULL; DB* db2 = NULL;
Status s = DB::Open(CurrentOptions(), dbname_, &db2); Status s = DB::Open(CurrentOptions(), dbname_, &db2);

View File

@ -151,6 +151,9 @@ class LEVELDB_EXPORT DB {
// Destroy the contents of the specified database. // Destroy the contents of the specified database.
// Be very careful using this method. // Be very careful using this method.
//
// Note: For backwards compatibility, if DestroyDB is unable to list the
// database files, Status::OK() will still be returned masking this failure.
LEVELDB_EXPORT Status DestroyDB(const std::string& name, LEVELDB_EXPORT Status DestroyDB(const std::string& name,
const Options& options); const Options& options);