ff463742b5
Summary: The WBWI has two differing modes of operation dependent on the value of the constructor parameter `overwrite_key`. Currently, regardless of the parameter, neither mode performs as expected when using Merge. This PR remedies this by correctly invoking the appropriate Merge Operator before returning results from the WBWI. Examples of issues that exist which are solved by this PR: ## Example 1 with `overwrite_key=false` Currently, from an empty database, the following sequence: ``` Put('k1', 'v1') Merge('k1', 'v2') Get('k1') ``` Incorrectly yields `v2`, that is to say that the Merge behaves like a Put. ## Example 2 with o`verwrite_key=true` Currently, from an empty database, the following sequence: ``` Put('k1', 'v1') Merge('k1', 'v2') Get('k1') ``` Incorrectly yields `ERROR: kMergeInProgress`. ## Example 3 with `overwrite_key=false` Currently, with a database containing `('k1' -> 'v1')`, the following sequence: ``` Merge('k1', 'v2') GetFromBatchAndDB('k1') ``` Incorrectly yields `v1,v2` ## Example 4 with `overwrite_key=true` Currently, with a database containing `('k1' -> 'v1')`, the following sequence: ``` Merge('k1', 'v1') GetFromBatchAndDB('k1') ``` Incorrectly yields `ERROR: kMergeInProgress`. ## Example 5 with `overwrite_key=false` Currently, from an empty database, the following sequence: ``` Put('k1', 'v1') Merge('k1', 'v2') GetFromBatchAndDB('k1') ``` Incorrectly yields `v1,v2` ## Example 6 with `overwrite_key=true` Currently, from an empty database, `('k1' -> 'v1')`, the following sequence: ``` Put('k1', 'v1') Merge('k1', 'v2') GetFromBatchAndDB('k1') ``` Incorrectly yields `ERROR: kMergeInProgress`. Pull Request resolved: https://github.com/facebook/rocksdb/pull/8135 Reviewed By: pdillinger Differential Revision: D27657938 Pulled By: mrambacher fbshipit-source-id: 0fbda6bbc66bedeba96a84786d90141d776297df
2128 lines
63 KiB
C++
2128 lines
63 KiB
C++
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
|
// This source code is licensed under both the GPLv2 (found in the
|
|
// COPYING file in the root directory) and Apache 2.0 License
|
|
// (found in the LICENSE.Apache file in the root directory).
|
|
//
|
|
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
|
|
|
#ifndef ROCKSDB_LITE
|
|
|
|
#include "rocksdb/utilities/write_batch_with_index.h"
|
|
|
|
#include <map>
|
|
#include <memory>
|
|
|
|
#include "db/column_family.h"
|
|
#include "port/stack_trace.h"
|
|
#include "test_util/testharness.h"
|
|
#include "util/random.h"
|
|
#include "util/string_util.h"
|
|
#include "utilities/merge_operators.h"
|
|
#include "utilities/merge_operators/string_append/stringappend.h"
|
|
#include "utilities/write_batch_with_index/write_batch_with_index_internal.h"
|
|
|
|
namespace ROCKSDB_NAMESPACE {
|
|
|
|
namespace {
|
|
class ColumnFamilyHandleImplDummy : public ColumnFamilyHandleImpl {
|
|
public:
|
|
explicit ColumnFamilyHandleImplDummy(int id, const Comparator* comparator)
|
|
: ColumnFamilyHandleImpl(nullptr, nullptr, nullptr),
|
|
id_(id),
|
|
comparator_(comparator) {}
|
|
uint32_t GetID() const override { return id_; }
|
|
const Comparator* GetComparator() const override { return comparator_; }
|
|
|
|
private:
|
|
uint32_t id_;
|
|
const Comparator* comparator_;
|
|
};
|
|
|
|
struct Entry {
|
|
std::string key;
|
|
std::string value;
|
|
WriteType type;
|
|
};
|
|
|
|
struct TestHandler : public WriteBatch::Handler {
|
|
std::map<uint32_t, std::vector<Entry>> seen;
|
|
Status PutCF(uint32_t column_family_id, const Slice& key,
|
|
const Slice& value) override {
|
|
Entry e;
|
|
e.key = key.ToString();
|
|
e.value = value.ToString();
|
|
e.type = kPutRecord;
|
|
seen[column_family_id].push_back(e);
|
|
return Status::OK();
|
|
}
|
|
Status MergeCF(uint32_t column_family_id, const Slice& key,
|
|
const Slice& value) override {
|
|
Entry e;
|
|
e.key = key.ToString();
|
|
e.value = value.ToString();
|
|
e.type = kMergeRecord;
|
|
seen[column_family_id].push_back(e);
|
|
return Status::OK();
|
|
}
|
|
void LogData(const Slice& /*blob*/) override {}
|
|
Status DeleteCF(uint32_t column_family_id, const Slice& key) override {
|
|
Entry e;
|
|
e.key = key.ToString();
|
|
e.value = "";
|
|
e.type = kDeleteRecord;
|
|
seen[column_family_id].push_back(e);
|
|
return Status::OK();
|
|
}
|
|
};
|
|
|
|
using KVMap = std::map<std::string, std::string>;
|
|
|
|
class KVIter : public Iterator {
|
|
public:
|
|
explicit KVIter(const KVMap* map) : map_(map), iter_(map_->end()) {}
|
|
bool Valid() const override { return iter_ != map_->end(); }
|
|
void SeekToFirst() override { iter_ = map_->begin(); }
|
|
void SeekToLast() override {
|
|
if (map_->empty()) {
|
|
iter_ = map_->end();
|
|
} else {
|
|
iter_ = map_->find(map_->rbegin()->first);
|
|
}
|
|
}
|
|
void Seek(const Slice& k) override {
|
|
iter_ = map_->lower_bound(k.ToString());
|
|
}
|
|
void SeekForPrev(const Slice& k) override {
|
|
iter_ = map_->upper_bound(k.ToString());
|
|
Prev();
|
|
}
|
|
void Next() override { ++iter_; }
|
|
void Prev() override {
|
|
if (iter_ == map_->begin()) {
|
|
iter_ = map_->end();
|
|
return;
|
|
}
|
|
--iter_;
|
|
}
|
|
Slice key() const override { return iter_->first; }
|
|
Slice value() const override { return iter_->second; }
|
|
Status status() const override { return Status::OK(); }
|
|
|
|
private:
|
|
const KVMap* const map_;
|
|
KVMap::const_iterator iter_;
|
|
};
|
|
|
|
static std::string PrintContents(WriteBatchWithIndex* batch,
|
|
ColumnFamilyHandle* column_family) {
|
|
std::string result;
|
|
|
|
WBWIIterator* iter;
|
|
if (column_family == nullptr) {
|
|
iter = batch->NewIterator();
|
|
} else {
|
|
iter = batch->NewIterator(column_family);
|
|
}
|
|
|
|
iter->SeekToFirst();
|
|
while (iter->Valid()) {
|
|
WriteEntry e = iter->Entry();
|
|
|
|
if (e.type == kPutRecord) {
|
|
result.append("PUT(");
|
|
result.append(e.key.ToString());
|
|
result.append("):");
|
|
result.append(e.value.ToString());
|
|
} else if (e.type == kMergeRecord) {
|
|
result.append("MERGE(");
|
|
result.append(e.key.ToString());
|
|
result.append("):");
|
|
result.append(e.value.ToString());
|
|
} else if (e.type == kSingleDeleteRecord) {
|
|
result.append("SINGLE-DEL(");
|
|
result.append(e.key.ToString());
|
|
result.append(")");
|
|
} else {
|
|
assert(e.type == kDeleteRecord);
|
|
result.append("DEL(");
|
|
result.append(e.key.ToString());
|
|
result.append(")");
|
|
}
|
|
|
|
result.append(",");
|
|
iter->Next();
|
|
}
|
|
|
|
delete iter;
|
|
return result;
|
|
}
|
|
|
|
static std::string PrintContents(WriteBatchWithIndex* batch, KVMap* base_map,
|
|
ColumnFamilyHandle* column_family) {
|
|
std::string result;
|
|
|
|
Iterator* iter;
|
|
if (column_family == nullptr) {
|
|
iter = batch->NewIteratorWithBase(new KVIter(base_map));
|
|
} else {
|
|
iter = batch->NewIteratorWithBase(column_family, new KVIter(base_map));
|
|
}
|
|
|
|
iter->SeekToFirst();
|
|
while (iter->Valid()) {
|
|
assert(iter->status().ok());
|
|
|
|
Slice key = iter->key();
|
|
Slice value = iter->value();
|
|
|
|
result.append(key.ToString());
|
|
result.append(":");
|
|
result.append(value.ToString());
|
|
result.append(",");
|
|
|
|
iter->Next();
|
|
}
|
|
|
|
delete iter;
|
|
return result;
|
|
}
|
|
|
|
void AssertIter(Iterator* iter, const std::string& key,
|
|
const std::string& value) {
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ(key, iter->key().ToString());
|
|
ASSERT_EQ(value, iter->value().ToString());
|
|
}
|
|
|
|
void AssertItersMatch(Iterator* iter1, Iterator* iter2) {
|
|
ASSERT_EQ(iter1->Valid(), iter2->Valid());
|
|
if (iter1->Valid()) {
|
|
ASSERT_EQ(iter1->key().ToString(), iter2->key().ToString());
|
|
ASSERT_EQ(iter1->value().ToString(), iter2->value().ToString());
|
|
}
|
|
}
|
|
|
|
void AssertItersEqual(Iterator* iter1, Iterator* iter2) {
|
|
iter1->SeekToFirst();
|
|
iter2->SeekToFirst();
|
|
while (iter1->Valid()) {
|
|
ASSERT_EQ(iter1->Valid(), iter2->Valid());
|
|
ASSERT_EQ(iter1->key().ToString(), iter2->key().ToString());
|
|
ASSERT_EQ(iter1->value().ToString(), iter2->value().ToString());
|
|
iter1->Next();
|
|
iter2->Next();
|
|
}
|
|
ASSERT_EQ(iter1->Valid(), iter2->Valid());
|
|
}
|
|
|
|
void AssertIterEqual(WBWIIteratorImpl* wbwii,
|
|
const std::vector<std::string>& keys) {
|
|
wbwii->SeekToFirst();
|
|
for (auto k : keys) {
|
|
ASSERT_TRUE(wbwii->Valid());
|
|
ASSERT_EQ(wbwii->Entry().key, k);
|
|
wbwii->NextKey();
|
|
}
|
|
ASSERT_FALSE(wbwii->Valid());
|
|
wbwii->SeekToLast();
|
|
for (auto kit = keys.rbegin(); kit != keys.rend(); ++kit) {
|
|
ASSERT_TRUE(wbwii->Valid());
|
|
ASSERT_EQ(wbwii->Entry().key, *kit);
|
|
wbwii->PrevKey();
|
|
}
|
|
ASSERT_FALSE(wbwii->Valid());
|
|
}
|
|
} // namespace anonymous
|
|
|
|
class WBWIBaseTest : public testing::Test {
|
|
public:
|
|
explicit WBWIBaseTest(bool overwrite) : db_(nullptr) {
|
|
options_.merge_operator =
|
|
MergeOperators::CreateFromStringId("stringappend");
|
|
options_.create_if_missing = true;
|
|
dbname_ = test::PerThreadDBPath("write_batch_with_index_test");
|
|
DestroyDB(dbname_, options_);
|
|
batch_.reset(new WriteBatchWithIndex(BytewiseComparator(), 20, overwrite));
|
|
}
|
|
|
|
virtual ~WBWIBaseTest() {
|
|
if (db_ != nullptr) {
|
|
ReleaseSnapshot();
|
|
delete db_;
|
|
DestroyDB(dbname_, options_);
|
|
}
|
|
}
|
|
|
|
std::string AddToBatch(ColumnFamilyHandle* cf, const std::string& key) {
|
|
std::string result;
|
|
for (size_t i = 0; i < key.size(); i++) {
|
|
if (key[i] == 'd') {
|
|
batch_->Delete(cf, key);
|
|
result = "";
|
|
} else if (key[i] == 'p') {
|
|
result = key + ToString(i);
|
|
batch_->Put(cf, key, result);
|
|
} else if (key[i] == 'm') {
|
|
std::string value = key + ToString(i);
|
|
batch_->Merge(cf, key, value);
|
|
if (result.empty()) {
|
|
result = value;
|
|
} else {
|
|
result = result + "," + value;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
virtual Status OpenDB() { return DB::Open(options_, dbname_, &db_); }
|
|
|
|
void ReleaseSnapshot() {
|
|
if (read_opts_.snapshot != nullptr) {
|
|
EXPECT_NE(db_, nullptr);
|
|
db_->ReleaseSnapshot(read_opts_.snapshot);
|
|
read_opts_.snapshot = nullptr;
|
|
}
|
|
}
|
|
|
|
public:
|
|
DB* db_;
|
|
std::string dbname_;
|
|
Options options_;
|
|
WriteOptions write_opts_;
|
|
ReadOptions read_opts_;
|
|
std::unique_ptr<WriteBatchWithIndex> batch_;
|
|
};
|
|
|
|
class WBWIKeepTest : public WBWIBaseTest {
|
|
public:
|
|
WBWIKeepTest() : WBWIBaseTest(false) {}
|
|
};
|
|
|
|
class WBWIOverwriteTest : public WBWIBaseTest {
|
|
public:
|
|
WBWIOverwriteTest() : WBWIBaseTest(true) {}
|
|
};
|
|
class WriteBatchWithIndexTest : public WBWIBaseTest,
|
|
public testing::WithParamInterface<bool> {
|
|
public:
|
|
WriteBatchWithIndexTest() : WBWIBaseTest(GetParam()) {}
|
|
};
|
|
|
|
void TestValueAsSecondaryIndexHelper(std::vector<Entry> entries,
|
|
WriteBatchWithIndex* batch) {
|
|
// In this test, we insert <key, value> to column family `data`, and
|
|
// <value, key> to column family `index`. Then iterator them in order
|
|
// and seek them by key.
|
|
|
|
// Sort entries by key
|
|
std::map<std::string, std::vector<Entry*>> data_map;
|
|
// Sort entries by value
|
|
std::map<std::string, std::vector<Entry*>> index_map;
|
|
for (auto& e : entries) {
|
|
data_map[e.key].push_back(&e);
|
|
index_map[e.value].push_back(&e);
|
|
}
|
|
|
|
ColumnFamilyHandleImplDummy data(6, BytewiseComparator());
|
|
ColumnFamilyHandleImplDummy index(8, BytewiseComparator());
|
|
for (auto& e : entries) {
|
|
if (e.type == kPutRecord) {
|
|
ASSERT_OK(batch->Put(&data, e.key, e.value));
|
|
ASSERT_OK(batch->Put(&index, e.value, e.key));
|
|
} else if (e.type == kMergeRecord) {
|
|
ASSERT_OK(batch->Merge(&data, e.key, e.value));
|
|
ASSERT_OK(batch->Put(&index, e.value, e.key));
|
|
} else {
|
|
assert(e.type == kDeleteRecord);
|
|
std::unique_ptr<WBWIIterator> iter(batch->NewIterator(&data));
|
|
iter->Seek(e.key);
|
|
ASSERT_OK(iter->status());
|
|
auto write_entry = iter->Entry();
|
|
ASSERT_EQ(e.key, write_entry.key.ToString());
|
|
ASSERT_EQ(e.value, write_entry.value.ToString());
|
|
ASSERT_OK(batch->Delete(&data, e.key));
|
|
ASSERT_OK(batch->Put(&index, e.value, ""));
|
|
}
|
|
}
|
|
|
|
// Iterator all keys
|
|
{
|
|
std::unique_ptr<WBWIIterator> iter(batch->NewIterator(&data));
|
|
for (int seek_to_first : {0, 1}) {
|
|
if (seek_to_first) {
|
|
iter->SeekToFirst();
|
|
} else {
|
|
iter->Seek("");
|
|
}
|
|
for (auto pair : data_map) {
|
|
for (auto v : pair.second) {
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(iter->Valid());
|
|
auto write_entry = iter->Entry();
|
|
ASSERT_EQ(pair.first, write_entry.key.ToString());
|
|
ASSERT_EQ(v->type, write_entry.type);
|
|
if (write_entry.type != kDeleteRecord) {
|
|
ASSERT_EQ(v->value, write_entry.value.ToString());
|
|
}
|
|
iter->Next();
|
|
}
|
|
}
|
|
ASSERT_TRUE(!iter->Valid());
|
|
}
|
|
iter->SeekToLast();
|
|
for (auto pair = data_map.rbegin(); pair != data_map.rend(); ++pair) {
|
|
for (auto v = pair->second.rbegin(); v != pair->second.rend(); v++) {
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(iter->Valid());
|
|
auto write_entry = iter->Entry();
|
|
ASSERT_EQ(pair->first, write_entry.key.ToString());
|
|
ASSERT_EQ((*v)->type, write_entry.type);
|
|
if (write_entry.type != kDeleteRecord) {
|
|
ASSERT_EQ((*v)->value, write_entry.value.ToString());
|
|
}
|
|
iter->Prev();
|
|
}
|
|
}
|
|
ASSERT_TRUE(!iter->Valid());
|
|
}
|
|
|
|
// Iterator all indexes
|
|
{
|
|
std::unique_ptr<WBWIIterator> iter(batch->NewIterator(&index));
|
|
for (int seek_to_first : {0, 1}) {
|
|
if (seek_to_first) {
|
|
iter->SeekToFirst();
|
|
} else {
|
|
iter->Seek("");
|
|
}
|
|
for (auto pair : index_map) {
|
|
for (auto v : pair.second) {
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(iter->Valid());
|
|
auto write_entry = iter->Entry();
|
|
ASSERT_EQ(pair.first, write_entry.key.ToString());
|
|
if (v->type != kDeleteRecord) {
|
|
ASSERT_EQ(v->key, write_entry.value.ToString());
|
|
ASSERT_EQ(v->value, write_entry.key.ToString());
|
|
}
|
|
iter->Next();
|
|
}
|
|
}
|
|
ASSERT_TRUE(!iter->Valid());
|
|
}
|
|
|
|
iter->SeekToLast();
|
|
for (auto pair = index_map.rbegin(); pair != index_map.rend(); ++pair) {
|
|
for (auto v = pair->second.rbegin(); v != pair->second.rend(); v++) {
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(iter->Valid());
|
|
auto write_entry = iter->Entry();
|
|
ASSERT_EQ(pair->first, write_entry.key.ToString());
|
|
if ((*v)->type != kDeleteRecord) {
|
|
ASSERT_EQ((*v)->key, write_entry.value.ToString());
|
|
ASSERT_EQ((*v)->value, write_entry.key.ToString());
|
|
}
|
|
iter->Prev();
|
|
}
|
|
}
|
|
ASSERT_TRUE(!iter->Valid());
|
|
}
|
|
|
|
// Seek to every key
|
|
{
|
|
std::unique_ptr<WBWIIterator> iter(batch->NewIterator(&data));
|
|
|
|
// Seek the keys one by one in reverse order
|
|
for (auto pair = data_map.rbegin(); pair != data_map.rend(); ++pair) {
|
|
iter->Seek(pair->first);
|
|
ASSERT_OK(iter->status());
|
|
for (auto v : pair->second) {
|
|
ASSERT_TRUE(iter->Valid());
|
|
auto write_entry = iter->Entry();
|
|
ASSERT_EQ(pair->first, write_entry.key.ToString());
|
|
ASSERT_EQ(v->type, write_entry.type);
|
|
if (write_entry.type != kDeleteRecord) {
|
|
ASSERT_EQ(v->value, write_entry.value.ToString());
|
|
}
|
|
iter->Next();
|
|
ASSERT_OK(iter->status());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Seek to every index
|
|
{
|
|
std::unique_ptr<WBWIIterator> iter(batch->NewIterator(&index));
|
|
|
|
// Seek the keys one by one in reverse order
|
|
for (auto pair = index_map.rbegin(); pair != index_map.rend(); ++pair) {
|
|
iter->Seek(pair->first);
|
|
ASSERT_OK(iter->status());
|
|
for (auto v : pair->second) {
|
|
ASSERT_TRUE(iter->Valid());
|
|
auto write_entry = iter->Entry();
|
|
ASSERT_EQ(pair->first, write_entry.key.ToString());
|
|
ASSERT_EQ(v->value, write_entry.key.ToString());
|
|
if (v->type != kDeleteRecord) {
|
|
ASSERT_EQ(v->key, write_entry.value.ToString());
|
|
}
|
|
iter->Next();
|
|
ASSERT_OK(iter->status());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify WriteBatch can be iterated
|
|
TestHandler handler;
|
|
ASSERT_OK(batch->GetWriteBatch()->Iterate(&handler));
|
|
|
|
// Verify data column family
|
|
{
|
|
ASSERT_EQ(entries.size(), handler.seen[data.GetID()].size());
|
|
size_t i = 0;
|
|
for (auto e : handler.seen[data.GetID()]) {
|
|
auto write_entry = entries[i++];
|
|
ASSERT_EQ(e.type, write_entry.type);
|
|
ASSERT_EQ(e.key, write_entry.key);
|
|
if (e.type != kDeleteRecord) {
|
|
ASSERT_EQ(e.value, write_entry.value);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify index column family
|
|
{
|
|
ASSERT_EQ(entries.size(), handler.seen[index.GetID()].size());
|
|
size_t i = 0;
|
|
for (auto e : handler.seen[index.GetID()]) {
|
|
auto write_entry = entries[i++];
|
|
ASSERT_EQ(e.key, write_entry.value);
|
|
if (write_entry.type != kDeleteRecord) {
|
|
ASSERT_EQ(e.value, write_entry.key);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(WBWIKeepTest, TestValueAsSecondaryIndex) {
|
|
Entry entries[] = {
|
|
{"aaa", "0005", kPutRecord},
|
|
{"b", "0002", kPutRecord},
|
|
{"cdd", "0002", kMergeRecord},
|
|
{"aab", "00001", kPutRecord},
|
|
{"cc", "00005", kPutRecord},
|
|
{"cdd", "0002", kPutRecord},
|
|
{"aab", "0003", kPutRecord},
|
|
{"cc", "00005", kDeleteRecord},
|
|
};
|
|
std::vector<Entry> entries_list(entries, entries + 8);
|
|
|
|
batch_.reset(new WriteBatchWithIndex(nullptr, 20, false));
|
|
|
|
TestValueAsSecondaryIndexHelper(entries_list, batch_.get());
|
|
|
|
// Clear batch and re-run test with new values
|
|
batch_->Clear();
|
|
|
|
Entry new_entries[] = {
|
|
{"aaa", "0005", kPutRecord},
|
|
{"e", "0002", kPutRecord},
|
|
{"add", "0002", kMergeRecord},
|
|
{"aab", "00001", kPutRecord},
|
|
{"zz", "00005", kPutRecord},
|
|
{"add", "0002", kPutRecord},
|
|
{"aab", "0003", kPutRecord},
|
|
{"zz", "00005", kDeleteRecord},
|
|
};
|
|
|
|
entries_list = std::vector<Entry>(new_entries, new_entries + 8);
|
|
|
|
TestValueAsSecondaryIndexHelper(entries_list, batch_.get());
|
|
}
|
|
|
|
TEST_P(WriteBatchWithIndexTest, TestComparatorForCF) {
|
|
ColumnFamilyHandleImplDummy cf1(6, nullptr);
|
|
ColumnFamilyHandleImplDummy reverse_cf(66, ReverseBytewiseComparator());
|
|
ColumnFamilyHandleImplDummy cf2(88, BytewiseComparator());
|
|
|
|
ASSERT_OK(batch_->Put(&cf1, "ddd", ""));
|
|
ASSERT_OK(batch_->Put(&cf2, "aaa", ""));
|
|
ASSERT_OK(batch_->Put(&cf2, "eee", ""));
|
|
ASSERT_OK(batch_->Put(&cf1, "ccc", ""));
|
|
ASSERT_OK(batch_->Put(&reverse_cf, "a11", ""));
|
|
ASSERT_OK(batch_->Put(&cf1, "bbb", ""));
|
|
|
|
Slice key_slices[] = {"a", "3", "3"};
|
|
Slice value_slice = "";
|
|
ASSERT_OK(batch_->Put(&reverse_cf, SliceParts(key_slices, 3),
|
|
SliceParts(&value_slice, 1)));
|
|
ASSERT_OK(batch_->Put(&reverse_cf, "a22", ""));
|
|
|
|
{
|
|
std::unique_ptr<WBWIIterator> iter(batch_->NewIterator(&cf1));
|
|
iter->Seek("");
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ("bbb", iter->Entry().key.ToString());
|
|
iter->Next();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ("ccc", iter->Entry().key.ToString());
|
|
iter->Next();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ("ddd", iter->Entry().key.ToString());
|
|
iter->Next();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(!iter->Valid());
|
|
}
|
|
|
|
{
|
|
std::unique_ptr<WBWIIterator> iter(batch_->NewIterator(&cf2));
|
|
iter->Seek("");
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ("aaa", iter->Entry().key.ToString());
|
|
iter->Next();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ("eee", iter->Entry().key.ToString());
|
|
iter->Next();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(!iter->Valid());
|
|
}
|
|
|
|
{
|
|
std::unique_ptr<WBWIIterator> iter(batch_->NewIterator(&reverse_cf));
|
|
iter->Seek("");
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(!iter->Valid());
|
|
|
|
iter->Seek("z");
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ("a33", iter->Entry().key.ToString());
|
|
iter->Next();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ("a22", iter->Entry().key.ToString());
|
|
iter->Next();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ("a11", iter->Entry().key.ToString());
|
|
iter->Next();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(!iter->Valid());
|
|
|
|
iter->Seek("a22");
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ("a22", iter->Entry().key.ToString());
|
|
|
|
iter->Seek("a13");
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ("a11", iter->Entry().key.ToString());
|
|
}
|
|
}
|
|
|
|
TEST_F(WBWIOverwriteTest, TestOverwriteKey) {
|
|
ColumnFamilyHandleImplDummy cf1(6, nullptr);
|
|
ColumnFamilyHandleImplDummy reverse_cf(66, ReverseBytewiseComparator());
|
|
ColumnFamilyHandleImplDummy cf2(88, BytewiseComparator());
|
|
|
|
ASSERT_OK(batch_->Merge(&cf1, "ddd", ""));
|
|
ASSERT_OK(batch_->Put(&cf1, "ddd", ""));
|
|
ASSERT_OK(batch_->Delete(&cf1, "ddd"));
|
|
ASSERT_OK(batch_->Put(&cf2, "aaa", ""));
|
|
ASSERT_OK(batch_->Delete(&cf2, "aaa"));
|
|
ASSERT_OK(batch_->Put(&cf2, "aaa", "aaa"));
|
|
ASSERT_OK(batch_->Put(&cf2, "eee", "eee"));
|
|
ASSERT_OK(batch_->Put(&cf1, "ccc", ""));
|
|
ASSERT_OK(batch_->Put(&reverse_cf, "a11", ""));
|
|
ASSERT_OK(batch_->Delete(&cf1, "ccc"));
|
|
ASSERT_OK(batch_->Put(&reverse_cf, "a33", "a33"));
|
|
ASSERT_OK(batch_->Put(&reverse_cf, "a11", "a11"));
|
|
Slice slices[] = {"a", "3", "3"};
|
|
ASSERT_OK(batch_->Delete(&reverse_cf, SliceParts(slices, 3)));
|
|
|
|
{
|
|
std::unique_ptr<WBWIIterator> iter(batch_->NewIterator(&cf1));
|
|
iter->Seek("");
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ("ccc", iter->Entry().key.ToString());
|
|
ASSERT_TRUE(iter->Entry().type == WriteType::kDeleteRecord);
|
|
iter->Next();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ("ddd", iter->Entry().key.ToString());
|
|
ASSERT_TRUE(iter->Entry().type == WriteType::kDeleteRecord);
|
|
iter->Next();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(!iter->Valid());
|
|
}
|
|
|
|
{
|
|
std::unique_ptr<WBWIIterator> iter(batch_->NewIterator(&cf2));
|
|
iter->SeekToLast();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ("eee", iter->Entry().key.ToString());
|
|
ASSERT_EQ("eee", iter->Entry().value.ToString());
|
|
iter->Prev();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ("aaa", iter->Entry().key.ToString());
|
|
ASSERT_EQ("aaa", iter->Entry().value.ToString());
|
|
iter->Prev();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(!iter->Valid());
|
|
|
|
iter->SeekToFirst();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ("aaa", iter->Entry().key.ToString());
|
|
ASSERT_EQ("aaa", iter->Entry().value.ToString());
|
|
iter->Next();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ("eee", iter->Entry().key.ToString());
|
|
ASSERT_EQ("eee", iter->Entry().value.ToString());
|
|
iter->Next();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(!iter->Valid());
|
|
}
|
|
|
|
{
|
|
std::unique_ptr<WBWIIterator> iter(batch_->NewIterator(&reverse_cf));
|
|
iter->Seek("");
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(!iter->Valid());
|
|
|
|
iter->Seek("z");
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ("a33", iter->Entry().key.ToString());
|
|
ASSERT_TRUE(iter->Entry().type == WriteType::kDeleteRecord);
|
|
iter->Next();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ("a11", iter->Entry().key.ToString());
|
|
ASSERT_EQ("a11", iter->Entry().value.ToString());
|
|
iter->Next();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(!iter->Valid());
|
|
|
|
iter->SeekToLast();
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ("a11", iter->Entry().key.ToString());
|
|
ASSERT_EQ("a11", iter->Entry().value.ToString());
|
|
iter->Prev();
|
|
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ("a33", iter->Entry().key.ToString());
|
|
ASSERT_TRUE(iter->Entry().type == WriteType::kDeleteRecord);
|
|
iter->Prev();
|
|
ASSERT_TRUE(!iter->Valid());
|
|
}
|
|
}
|
|
|
|
TEST_P(WriteBatchWithIndexTest, TestWBWIIterator) {
|
|
ColumnFamilyHandleImplDummy cf1(1, BytewiseComparator());
|
|
ColumnFamilyHandleImplDummy cf2(2, BytewiseComparator());
|
|
ASSERT_OK(batch_->Put(&cf1, "a", "a1"));
|
|
ASSERT_OK(batch_->Put(&cf1, "c", "c1"));
|
|
ASSERT_OK(batch_->Put(&cf1, "c", "c2"));
|
|
ASSERT_OK(batch_->Put(&cf1, "e", "e1"));
|
|
ASSERT_OK(batch_->Put(&cf1, "e", "e2"));
|
|
ASSERT_OK(batch_->Put(&cf1, "e", "e3"));
|
|
std::unique_ptr<WBWIIteratorImpl> iter1(
|
|
static_cast<WBWIIteratorImpl*>(batch_->NewIterator(&cf1)));
|
|
std::unique_ptr<WBWIIteratorImpl> iter2(
|
|
static_cast<WBWIIteratorImpl*>(batch_->NewIterator(&cf2)));
|
|
AssertIterEqual(iter1.get(), {"a", "c", "e"});
|
|
AssertIterEqual(iter2.get(), {});
|
|
ASSERT_OK(batch_->Put(&cf2, "a", "a2"));
|
|
ASSERT_OK(batch_->Merge(&cf2, "b", "b1"));
|
|
ASSERT_OK(batch_->Merge(&cf2, "b", "b2"));
|
|
ASSERT_OK(batch_->Delete(&cf2, "d"));
|
|
ASSERT_OK(batch_->Merge(&cf2, "d", "d2"));
|
|
ASSERT_OK(batch_->Merge(&cf2, "d", "d3"));
|
|
ASSERT_OK(batch_->Delete(&cf2, "f"));
|
|
AssertIterEqual(iter1.get(), {"a", "c", "e"});
|
|
AssertIterEqual(iter2.get(), {"a", "b", "d", "f"});
|
|
}
|
|
|
|
TEST_P(WriteBatchWithIndexTest, TestRandomIteraratorWithBase) {
|
|
std::vector<std::string> source_strings = {"a", "b", "c", "d", "e",
|
|
"f", "g", "h", "i", "j"};
|
|
for (int rand_seed = 301; rand_seed < 366; rand_seed++) {
|
|
Random rnd(rand_seed);
|
|
|
|
ColumnFamilyHandleImplDummy cf1(6, BytewiseComparator());
|
|
ColumnFamilyHandleImplDummy cf2(2, BytewiseComparator());
|
|
ColumnFamilyHandleImplDummy cf3(8, BytewiseComparator());
|
|
batch_->Clear();
|
|
|
|
if (rand_seed % 2 == 0) {
|
|
ASSERT_OK(batch_->Put(&cf2, "zoo", "bar"));
|
|
}
|
|
if (rand_seed % 4 == 1) {
|
|
ASSERT_OK(batch_->Put(&cf3, "zoo", "bar"));
|
|
}
|
|
|
|
KVMap map;
|
|
KVMap merged_map;
|
|
for (auto key : source_strings) {
|
|
std::string value = key + key;
|
|
int type = rnd.Uniform(6);
|
|
switch (type) {
|
|
case 0:
|
|
// only base has it
|
|
map[key] = value;
|
|
merged_map[key] = value;
|
|
break;
|
|
case 1:
|
|
// only delta has it
|
|
ASSERT_OK(batch_->Put(&cf1, key, value));
|
|
map[key] = value;
|
|
merged_map[key] = value;
|
|
break;
|
|
case 2:
|
|
// both has it. Delta should win
|
|
ASSERT_OK(batch_->Put(&cf1, key, value));
|
|
map[key] = "wrong_value";
|
|
merged_map[key] = value;
|
|
break;
|
|
case 3:
|
|
// both has it. Delta is delete
|
|
ASSERT_OK(batch_->Delete(&cf1, key));
|
|
map[key] = "wrong_value";
|
|
break;
|
|
case 4:
|
|
// only delta has it. Delta is delete
|
|
ASSERT_OK(batch_->Delete(&cf1, key));
|
|
map[key] = "wrong_value";
|
|
break;
|
|
default:
|
|
// Neither iterator has it.
|
|
break;
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<Iterator> iter(
|
|
batch_->NewIteratorWithBase(&cf1, new KVIter(&map)));
|
|
std::unique_ptr<Iterator> result_iter(new KVIter(&merged_map));
|
|
|
|
bool is_valid = false;
|
|
for (int i = 0; i < 128; i++) {
|
|
// Random walk and make sure iter and result_iter returns the
|
|
// same key and value
|
|
int type = rnd.Uniform(6);
|
|
ASSERT_OK(iter->status());
|
|
switch (type) {
|
|
case 0:
|
|
// Seek to First
|
|
iter->SeekToFirst();
|
|
result_iter->SeekToFirst();
|
|
break;
|
|
case 1:
|
|
// Seek to last
|
|
iter->SeekToLast();
|
|
result_iter->SeekToLast();
|
|
break;
|
|
case 2: {
|
|
// Seek to random key
|
|
auto key_idx = rnd.Uniform(static_cast<int>(source_strings.size()));
|
|
auto key = source_strings[key_idx];
|
|
iter->Seek(key);
|
|
result_iter->Seek(key);
|
|
break;
|
|
}
|
|
case 3: {
|
|
// SeekForPrev to random key
|
|
auto key_idx = rnd.Uniform(static_cast<int>(source_strings.size()));
|
|
auto key = source_strings[key_idx];
|
|
iter->SeekForPrev(key);
|
|
result_iter->SeekForPrev(key);
|
|
break;
|
|
}
|
|
case 4:
|
|
// Next
|
|
if (is_valid) {
|
|
iter->Next();
|
|
result_iter->Next();
|
|
} else {
|
|
continue;
|
|
}
|
|
break;
|
|
default:
|
|
assert(type == 5);
|
|
// Prev
|
|
if (is_valid) {
|
|
iter->Prev();
|
|
result_iter->Prev();
|
|
} else {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
AssertItersMatch(iter.get(), result_iter.get());
|
|
is_valid = iter->Valid();
|
|
}
|
|
|
|
ASSERT_OK(iter->status());
|
|
}
|
|
}
|
|
|
|
TEST_P(WriteBatchWithIndexTest, TestIteraratorWithBase) {
|
|
ColumnFamilyHandleImplDummy cf1(6, BytewiseComparator());
|
|
ColumnFamilyHandleImplDummy cf2(2, BytewiseComparator());
|
|
{
|
|
KVMap map;
|
|
map["a"] = "aa";
|
|
map["c"] = "cc";
|
|
map["e"] = "ee";
|
|
std::unique_ptr<Iterator> iter(
|
|
batch_->NewIteratorWithBase(&cf1, new KVIter(&map)));
|
|
|
|
iter->SeekToFirst();
|
|
AssertIter(iter.get(), "a", "aa");
|
|
iter->Next();
|
|
AssertIter(iter.get(), "c", "cc");
|
|
iter->Next();
|
|
AssertIter(iter.get(), "e", "ee");
|
|
iter->Next();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(!iter->Valid());
|
|
|
|
iter->SeekToLast();
|
|
AssertIter(iter.get(), "e", "ee");
|
|
iter->Prev();
|
|
AssertIter(iter.get(), "c", "cc");
|
|
iter->Prev();
|
|
AssertIter(iter.get(), "a", "aa");
|
|
iter->Prev();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(!iter->Valid());
|
|
|
|
iter->Seek("b");
|
|
AssertIter(iter.get(), "c", "cc");
|
|
|
|
iter->Prev();
|
|
AssertIter(iter.get(), "a", "aa");
|
|
|
|
iter->Seek("a");
|
|
AssertIter(iter.get(), "a", "aa");
|
|
}
|
|
|
|
// Test the case that there is one element in the write batch
|
|
ASSERT_OK(batch_->Put(&cf2, "zoo", "bar"));
|
|
ASSERT_OK(batch_->Put(&cf1, "a", "aa"));
|
|
{
|
|
KVMap empty_map;
|
|
std::unique_ptr<Iterator> iter(
|
|
batch_->NewIteratorWithBase(&cf1, new KVIter(&empty_map)));
|
|
|
|
iter->SeekToFirst();
|
|
AssertIter(iter.get(), "a", "aa");
|
|
iter->Next();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(!iter->Valid());
|
|
}
|
|
|
|
ASSERT_OK(batch_->Delete(&cf1, "b"));
|
|
ASSERT_OK(batch_->Put(&cf1, "c", "cc"));
|
|
ASSERT_OK(batch_->Put(&cf1, "d", "dd"));
|
|
ASSERT_OK(batch_->Delete(&cf1, "e"));
|
|
|
|
{
|
|
KVMap map;
|
|
map["b"] = "";
|
|
map["cc"] = "cccc";
|
|
map["f"] = "ff";
|
|
std::unique_ptr<Iterator> iter(
|
|
batch_->NewIteratorWithBase(&cf1, new KVIter(&map)));
|
|
|
|
iter->SeekToFirst();
|
|
AssertIter(iter.get(), "a", "aa");
|
|
iter->Next();
|
|
AssertIter(iter.get(), "c", "cc");
|
|
iter->Next();
|
|
AssertIter(iter.get(), "cc", "cccc");
|
|
iter->Next();
|
|
AssertIter(iter.get(), "d", "dd");
|
|
iter->Next();
|
|
AssertIter(iter.get(), "f", "ff");
|
|
iter->Next();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(!iter->Valid());
|
|
|
|
iter->SeekToLast();
|
|
AssertIter(iter.get(), "f", "ff");
|
|
iter->Prev();
|
|
AssertIter(iter.get(), "d", "dd");
|
|
iter->Prev();
|
|
AssertIter(iter.get(), "cc", "cccc");
|
|
iter->Prev();
|
|
AssertIter(iter.get(), "c", "cc");
|
|
iter->Next();
|
|
AssertIter(iter.get(), "cc", "cccc");
|
|
iter->Prev();
|
|
AssertIter(iter.get(), "c", "cc");
|
|
iter->Prev();
|
|
AssertIter(iter.get(), "a", "aa");
|
|
iter->Prev();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(!iter->Valid());
|
|
|
|
iter->Seek("c");
|
|
AssertIter(iter.get(), "c", "cc");
|
|
|
|
iter->Seek("cb");
|
|
AssertIter(iter.get(), "cc", "cccc");
|
|
|
|
iter->Seek("cc");
|
|
AssertIter(iter.get(), "cc", "cccc");
|
|
iter->Next();
|
|
AssertIter(iter.get(), "d", "dd");
|
|
|
|
iter->Seek("e");
|
|
AssertIter(iter.get(), "f", "ff");
|
|
|
|
iter->Prev();
|
|
AssertIter(iter.get(), "d", "dd");
|
|
|
|
iter->Next();
|
|
AssertIter(iter.get(), "f", "ff");
|
|
}
|
|
|
|
{
|
|
KVMap empty_map;
|
|
std::unique_ptr<Iterator> iter(
|
|
batch_->NewIteratorWithBase(&cf1, new KVIter(&empty_map)));
|
|
|
|
iter->SeekToFirst();
|
|
AssertIter(iter.get(), "a", "aa");
|
|
iter->Next();
|
|
AssertIter(iter.get(), "c", "cc");
|
|
iter->Next();
|
|
AssertIter(iter.get(), "d", "dd");
|
|
iter->Next();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(!iter->Valid());
|
|
|
|
iter->SeekToLast();
|
|
AssertIter(iter.get(), "d", "dd");
|
|
iter->Prev();
|
|
AssertIter(iter.get(), "c", "cc");
|
|
iter->Prev();
|
|
AssertIter(iter.get(), "a", "aa");
|
|
|
|
iter->Prev();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(!iter->Valid());
|
|
|
|
iter->Seek("aa");
|
|
AssertIter(iter.get(), "c", "cc");
|
|
iter->Next();
|
|
AssertIter(iter.get(), "d", "dd");
|
|
|
|
iter->Seek("ca");
|
|
AssertIter(iter.get(), "d", "dd");
|
|
|
|
iter->Prev();
|
|
AssertIter(iter.get(), "c", "cc");
|
|
}
|
|
}
|
|
|
|
TEST_P(WriteBatchWithIndexTest, TestIteraratorWithBaseReverseCmp) {
|
|
ColumnFamilyHandleImplDummy cf1(6, ReverseBytewiseComparator());
|
|
ColumnFamilyHandleImplDummy cf2(2, ReverseBytewiseComparator());
|
|
|
|
// Test the case that there is one element in the write batch
|
|
ASSERT_OK(batch_->Put(&cf2, "zoo", "bar"));
|
|
ASSERT_OK(batch_->Put(&cf1, "a", "aa"));
|
|
{
|
|
KVMap empty_map;
|
|
std::unique_ptr<Iterator> iter(
|
|
batch_->NewIteratorWithBase(&cf1, new KVIter(&empty_map)));
|
|
|
|
iter->SeekToFirst();
|
|
AssertIter(iter.get(), "a", "aa");
|
|
iter->Next();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(!iter->Valid());
|
|
}
|
|
|
|
ASSERT_OK(batch_->Put(&cf1, "c", "cc"));
|
|
{
|
|
KVMap map;
|
|
std::unique_ptr<Iterator> iter(
|
|
batch_->NewIteratorWithBase(&cf1, new KVIter(&map)));
|
|
|
|
iter->SeekToFirst();
|
|
AssertIter(iter.get(), "c", "cc");
|
|
iter->Next();
|
|
AssertIter(iter.get(), "a", "aa");
|
|
iter->Next();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(!iter->Valid());
|
|
|
|
iter->SeekToLast();
|
|
AssertIter(iter.get(), "a", "aa");
|
|
iter->Prev();
|
|
AssertIter(iter.get(), "c", "cc");
|
|
iter->Prev();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(!iter->Valid());
|
|
|
|
iter->Seek("b");
|
|
AssertIter(iter.get(), "a", "aa");
|
|
|
|
iter->Prev();
|
|
AssertIter(iter.get(), "c", "cc");
|
|
|
|
iter->Seek("a");
|
|
AssertIter(iter.get(), "a", "aa");
|
|
}
|
|
|
|
// default column family
|
|
ASSERT_OK(batch_->Put("a", "b"));
|
|
{
|
|
KVMap map;
|
|
map["b"] = "";
|
|
std::unique_ptr<Iterator> iter(
|
|
batch_->NewIteratorWithBase(new KVIter(&map)));
|
|
|
|
iter->SeekToFirst();
|
|
AssertIter(iter.get(), "a", "b");
|
|
iter->Next();
|
|
AssertIter(iter.get(), "b", "");
|
|
iter->Next();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(!iter->Valid());
|
|
|
|
iter->SeekToLast();
|
|
AssertIter(iter.get(), "b", "");
|
|
iter->Prev();
|
|
AssertIter(iter.get(), "a", "b");
|
|
iter->Prev();
|
|
ASSERT_OK(iter->status());
|
|
ASSERT_TRUE(!iter->Valid());
|
|
|
|
iter->Seek("b");
|
|
AssertIter(iter.get(), "b", "");
|
|
|
|
iter->Prev();
|
|
AssertIter(iter.get(), "a", "b");
|
|
|
|
iter->Seek("0");
|
|
AssertIter(iter.get(), "a", "b");
|
|
}
|
|
}
|
|
|
|
TEST_P(WriteBatchWithIndexTest, TestGetFromBatch) {
|
|
Options options;
|
|
Status s;
|
|
std::string value;
|
|
|
|
s = batch_->GetFromBatch(options_, "b", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
|
|
ASSERT_OK(batch_->Put("a", "a"));
|
|
ASSERT_OK(batch_->Put("b", "b"));
|
|
ASSERT_OK(batch_->Put("c", "c"));
|
|
ASSERT_OK(batch_->Put("a", "z"));
|
|
ASSERT_OK(batch_->Delete("c"));
|
|
ASSERT_OK(batch_->Delete("d"));
|
|
ASSERT_OK(batch_->Delete("e"));
|
|
ASSERT_OK(batch_->Put("e", "e"));
|
|
|
|
s = batch_->GetFromBatch(options_, "b", &value);
|
|
ASSERT_OK(s);
|
|
ASSERT_EQ("b", value);
|
|
|
|
s = batch_->GetFromBatch(options_, "a", &value);
|
|
ASSERT_OK(s);
|
|
ASSERT_EQ("z", value);
|
|
|
|
s = batch_->GetFromBatch(options_, "c", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
|
|
s = batch_->GetFromBatch(options_, "d", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
|
|
s = batch_->GetFromBatch(options_, "x", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
|
|
s = batch_->GetFromBatch(options_, "e", &value);
|
|
ASSERT_OK(s);
|
|
ASSERT_EQ("e", value);
|
|
|
|
ASSERT_OK(batch_->Merge("z", "z"));
|
|
|
|
s = batch_->GetFromBatch(options_, "z", &value);
|
|
ASSERT_NOK(s); // No merge operator specified.
|
|
|
|
s = batch_->GetFromBatch(options_, "b", &value);
|
|
ASSERT_OK(s);
|
|
ASSERT_EQ("b", value);
|
|
}
|
|
|
|
TEST_P(WriteBatchWithIndexTest, TestGetFromBatchMerge) {
|
|
Status s = OpenDB();
|
|
ASSERT_OK(s);
|
|
|
|
ColumnFamilyHandle* column_family = db_->DefaultColumnFamily();
|
|
std::string value;
|
|
|
|
s = batch_->GetFromBatch(options_, "x", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
|
|
ASSERT_OK(batch_->Put("x", "X"));
|
|
std::string expected = "X";
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
ASSERT_OK(batch_->Merge("x", ToString(i)));
|
|
expected = expected + "," + ToString(i);
|
|
|
|
if (i % 2 == 0) {
|
|
ASSERT_OK(batch_->Put("y", ToString(i / 2)));
|
|
}
|
|
|
|
ASSERT_OK(batch_->Merge("z", "z"));
|
|
|
|
s = batch_->GetFromBatch(column_family, options_, "x", &value);
|
|
ASSERT_OK(s);
|
|
ASSERT_EQ(expected, value);
|
|
|
|
s = batch_->GetFromBatch(column_family, options_, "y", &value);
|
|
ASSERT_OK(s);
|
|
ASSERT_EQ(ToString(i / 2), value);
|
|
|
|
s = batch_->GetFromBatch(column_family, options_, "z", &value);
|
|
ASSERT_TRUE(s.IsMergeInProgress());
|
|
}
|
|
}
|
|
|
|
TEST_F(WBWIOverwriteTest, TestGetFromBatchMerge2) {
|
|
Status s = OpenDB();
|
|
ASSERT_OK(s);
|
|
|
|
ColumnFamilyHandle* column_family = db_->DefaultColumnFamily();
|
|
std::string value;
|
|
|
|
s = batch_->GetFromBatch(column_family, options_, "X", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
|
|
ASSERT_OK(batch_->Put(column_family, "X", "x"));
|
|
ASSERT_OK(batch_->GetFromBatch(column_family, options_, "X", &value));
|
|
ASSERT_EQ("x", value);
|
|
|
|
ASSERT_OK(batch_->Put(column_family, "X", "x2"));
|
|
ASSERT_OK(batch_->GetFromBatch(column_family, options_, "X", &value));
|
|
ASSERT_EQ("x2", value);
|
|
|
|
ASSERT_OK(batch_->Merge(column_family, "X", "aaa"));
|
|
ASSERT_OK(batch_->GetFromBatch(column_family, options_, "X", &value));
|
|
ASSERT_EQ("x2,aaa", value);
|
|
|
|
ASSERT_OK(batch_->Merge(column_family, "X", "bbb"));
|
|
ASSERT_OK(batch_->GetFromBatch(column_family, options_, "X", &value));
|
|
ASSERT_EQ("x2,aaa,bbb", value);
|
|
|
|
ASSERT_OK(batch_->Put(column_family, "X", "x3"));
|
|
ASSERT_OK(batch_->GetFromBatch(column_family, options_, "X", &value));
|
|
ASSERT_EQ("x3", value);
|
|
|
|
ASSERT_OK(batch_->Merge(column_family, "X", "ccc"));
|
|
ASSERT_OK(batch_->GetFromBatch(column_family, options_, "X", &value));
|
|
ASSERT_EQ("x3,ccc", value);
|
|
|
|
ASSERT_OK(batch_->Delete(column_family, "X"));
|
|
s = batch_->GetFromBatch(column_family, options_, "X", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
|
|
batch_->Merge(column_family, "X", "ddd");
|
|
ASSERT_OK(batch_->GetFromBatch(column_family, options_, "X", &value));
|
|
ASSERT_EQ("ddd", value);
|
|
}
|
|
|
|
TEST_P(WriteBatchWithIndexTest, TestGetFromBatchAndDB) {
|
|
ASSERT_OK(OpenDB());
|
|
|
|
std::string value;
|
|
|
|
ASSERT_OK(db_->Put(write_opts_, "a", "a"));
|
|
ASSERT_OK(db_->Put(write_opts_, "b", "b"));
|
|
ASSERT_OK(db_->Put(write_opts_, "c", "c"));
|
|
|
|
ASSERT_OK(batch_->Put("a", "batch_->a"));
|
|
ASSERT_OK(batch_->Delete("b"));
|
|
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "a", &value));
|
|
ASSERT_EQ("batch_->a", value);
|
|
|
|
Status s = batch_->GetFromBatchAndDB(db_, read_opts_, "b", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "c", &value));
|
|
ASSERT_EQ("c", value);
|
|
|
|
s = batch_->GetFromBatchAndDB(db_, read_opts_, "x", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
|
|
ASSERT_OK(db_->Delete(write_opts_, "x"));
|
|
|
|
s = batch_->GetFromBatchAndDB(db_, read_opts_, "x", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
}
|
|
|
|
TEST_P(WriteBatchWithIndexTest, TestGetFromBatchAndDBMerge) {
|
|
Status s = OpenDB();
|
|
ASSERT_OK(s);
|
|
|
|
std::string value;
|
|
|
|
ASSERT_OK(db_->Put(write_opts_, "a", "a0"));
|
|
ASSERT_OK(db_->Put(write_opts_, "b", "b0"));
|
|
ASSERT_OK(db_->Merge(write_opts_, "b", "b1"));
|
|
ASSERT_OK(db_->Merge(write_opts_, "c", "c0"));
|
|
ASSERT_OK(db_->Merge(write_opts_, "d", "d0"));
|
|
|
|
ASSERT_OK(batch_->Merge("a", "a1"));
|
|
ASSERT_OK(batch_->Merge("a", "a2"));
|
|
ASSERT_OK(batch_->Merge("b", "b2"));
|
|
ASSERT_OK(batch_->Merge("d", "d1"));
|
|
ASSERT_OK(batch_->Merge("e", "e0"));
|
|
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "a", &value));
|
|
ASSERT_EQ("a0,a1,a2", value);
|
|
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "b", &value));
|
|
ASSERT_EQ("b0,b1,b2", value);
|
|
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "c", &value));
|
|
ASSERT_EQ("c0", value);
|
|
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "d", &value));
|
|
ASSERT_EQ("d0,d1", value);
|
|
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "e", &value));
|
|
ASSERT_EQ("e0", value);
|
|
|
|
ASSERT_OK(db_->Delete(write_opts_, "x"));
|
|
|
|
s = batch_->GetFromBatchAndDB(db_, read_opts_, "x", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
|
|
const Snapshot* snapshot = db_->GetSnapshot();
|
|
ReadOptions snapshot_read_options;
|
|
snapshot_read_options.snapshot = snapshot;
|
|
|
|
ASSERT_OK(db_->Delete(write_opts_, "a"));
|
|
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "a", &value));
|
|
ASSERT_EQ("a1,a2", value);
|
|
|
|
ASSERT_OK(
|
|
s = batch_->GetFromBatchAndDB(db_, snapshot_read_options, "a", &value));
|
|
ASSERT_EQ("a0,a1,a2", value);
|
|
|
|
ASSERT_OK(batch_->Delete("a"));
|
|
|
|
s = batch_->GetFromBatchAndDB(db_, read_opts_, "a", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
|
|
s = batch_->GetFromBatchAndDB(db_, snapshot_read_options, "a", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
|
|
ASSERT_OK(s = db_->Merge(write_opts_, "c", "c1"));
|
|
|
|
ASSERT_OK(s = batch_->GetFromBatchAndDB(db_, read_opts_, "c", &value));
|
|
ASSERT_EQ("c0,c1", value);
|
|
|
|
ASSERT_OK(
|
|
s = batch_->GetFromBatchAndDB(db_, snapshot_read_options, "c", &value));
|
|
ASSERT_EQ("c0", value);
|
|
|
|
ASSERT_OK(db_->Put(write_opts_, "e", "e1"));
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "e", &value));
|
|
ASSERT_EQ("e1,e0", value);
|
|
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, snapshot_read_options, "e", &value));
|
|
ASSERT_EQ("e0", value);
|
|
|
|
ASSERT_OK(s = db_->Delete(write_opts_, "e"));
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "e", &value));
|
|
ASSERT_EQ("e0", value);
|
|
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, snapshot_read_options, "e", &value));
|
|
ASSERT_EQ("e0", value);
|
|
|
|
db_->ReleaseSnapshot(snapshot);
|
|
}
|
|
|
|
TEST_F(WBWIOverwriteTest, TestGetFromBatchAndDBMerge2) {
|
|
Status s = OpenDB();
|
|
ASSERT_OK(s);
|
|
|
|
std::string value;
|
|
|
|
s = batch_->GetFromBatchAndDB(db_, read_opts_, "A", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
|
|
ASSERT_OK(batch_->Merge("A", "xxx"));
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "A", &value));
|
|
ASSERT_EQ(value, "xxx");
|
|
|
|
ASSERT_OK(batch_->Merge("A", "yyy"));
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "A", &value));
|
|
ASSERT_EQ(value, "xxx,yyy");
|
|
|
|
ASSERT_OK(db_->Put(write_opts_, "A", "a0"));
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "A", &value));
|
|
ASSERT_EQ(value, "a0,xxx,yyy");
|
|
|
|
ASSERT_OK(batch_->Delete("A"));
|
|
|
|
s = batch_->GetFromBatchAndDB(db_, read_opts_, "A", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
}
|
|
|
|
TEST_P(WriteBatchWithIndexTest, TestGetFromBatchAndDBMerge3) {
|
|
Status s = OpenDB();
|
|
ASSERT_OK(s);
|
|
|
|
FlushOptions flush_options;
|
|
std::string value;
|
|
|
|
ASSERT_OK(db_->Put(write_opts_, "A", "1"));
|
|
ASSERT_OK(db_->Flush(flush_options, db_->DefaultColumnFamily()));
|
|
ASSERT_OK(batch_->Merge("A", "2"));
|
|
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "A", &value));
|
|
ASSERT_EQ(value, "1,2");
|
|
}
|
|
|
|
void AssertKey(std::string key, WBWIIterator* iter) {
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ(key, iter->Entry().key.ToString());
|
|
}
|
|
|
|
void AssertValue(std::string value, WBWIIterator* iter) {
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ(value, iter->Entry().value.ToString());
|
|
}
|
|
|
|
// Tests that we can write to the WBWI while we iterate (from a single thread).
|
|
// iteration should see the newest writes
|
|
TEST_F(WBWIOverwriteTest, MutateWhileIteratingCorrectnessTest) {
|
|
for (char c = 'a'; c <= 'z'; ++c) {
|
|
ASSERT_OK(batch_->Put(std::string(1, c), std::string(1, c)));
|
|
}
|
|
|
|
std::unique_ptr<WBWIIterator> iter(batch_->NewIterator());
|
|
iter->Seek("k");
|
|
AssertKey("k", iter.get());
|
|
iter->Next();
|
|
AssertKey("l", iter.get());
|
|
ASSERT_OK(batch_->Put("ab", "cc"));
|
|
iter->Next();
|
|
AssertKey("m", iter.get());
|
|
ASSERT_OK(batch_->Put("mm", "kk"));
|
|
iter->Next();
|
|
AssertKey("mm", iter.get());
|
|
AssertValue("kk", iter.get());
|
|
ASSERT_OK(batch_->Delete("mm"));
|
|
|
|
iter->Next();
|
|
AssertKey("n", iter.get());
|
|
iter->Prev();
|
|
AssertKey("mm", iter.get());
|
|
ASSERT_EQ(kDeleteRecord, iter->Entry().type);
|
|
|
|
iter->Seek("ab");
|
|
AssertKey("ab", iter.get());
|
|
ASSERT_OK(batch_->Delete("x"));
|
|
iter->Seek("x");
|
|
AssertKey("x", iter.get());
|
|
ASSERT_EQ(kDeleteRecord, iter->Entry().type);
|
|
iter->Prev();
|
|
AssertKey("w", iter.get());
|
|
}
|
|
|
|
void AssertIterKey(std::string key, Iterator* iter) {
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ(key, iter->key().ToString());
|
|
}
|
|
|
|
void AssertIterValue(std::string value, Iterator* iter) {
|
|
ASSERT_TRUE(iter->Valid());
|
|
ASSERT_EQ(value, iter->value().ToString());
|
|
}
|
|
|
|
// same thing as above, but testing IteratorWithBase
|
|
TEST_F(WBWIOverwriteTest, MutateWhileIteratingBaseCorrectnessTest) {
|
|
WriteBatchWithIndex batch(BytewiseComparator(), 0, true);
|
|
for (char c = 'a'; c <= 'z'; ++c) {
|
|
ASSERT_OK(batch_->Put(std::string(1, c), std::string(1, c)));
|
|
}
|
|
|
|
KVMap map;
|
|
map["aa"] = "aa";
|
|
map["cc"] = "cc";
|
|
map["ee"] = "ee";
|
|
map["em"] = "me";
|
|
|
|
std::unique_ptr<Iterator> iter(batch_->NewIteratorWithBase(new KVIter(&map)));
|
|
iter->Seek("k");
|
|
AssertIterKey("k", iter.get());
|
|
iter->Next();
|
|
AssertIterKey("l", iter.get());
|
|
ASSERT_OK(batch_->Put("ab", "cc"));
|
|
iter->Next();
|
|
AssertIterKey("m", iter.get());
|
|
ASSERT_OK(batch_->Put("mm", "kk"));
|
|
iter->Next();
|
|
AssertIterKey("mm", iter.get());
|
|
AssertIterValue("kk", iter.get());
|
|
ASSERT_OK(batch_->Delete("mm"));
|
|
iter->Next();
|
|
AssertIterKey("n", iter.get());
|
|
iter->Prev();
|
|
// "mm" is deleted, so we're back at "m"
|
|
AssertIterKey("m", iter.get());
|
|
|
|
iter->Seek("ab");
|
|
AssertIterKey("ab", iter.get());
|
|
iter->Prev();
|
|
AssertIterKey("aa", iter.get());
|
|
iter->Prev();
|
|
AssertIterKey("a", iter.get());
|
|
ASSERT_OK(batch_->Delete("aa"));
|
|
iter->Next();
|
|
AssertIterKey("ab", iter.get());
|
|
iter->Prev();
|
|
AssertIterKey("a", iter.get());
|
|
|
|
ASSERT_OK(batch_->Delete("x"));
|
|
iter->Seek("x");
|
|
AssertIterKey("y", iter.get());
|
|
iter->Next();
|
|
AssertIterKey("z", iter.get());
|
|
iter->Prev();
|
|
iter->Prev();
|
|
AssertIterKey("w", iter.get());
|
|
|
|
ASSERT_OK(batch_->Delete("e"));
|
|
iter->Seek("e");
|
|
AssertIterKey("ee", iter.get());
|
|
AssertIterValue("ee", iter.get());
|
|
ASSERT_OK(batch_->Put("ee", "xx"));
|
|
// still the same value
|
|
AssertIterValue("ee", iter.get());
|
|
iter->Next();
|
|
AssertIterKey("em", iter.get());
|
|
iter->Prev();
|
|
// new value
|
|
AssertIterValue("xx", iter.get());
|
|
|
|
ASSERT_OK(iter->status());
|
|
}
|
|
|
|
// stress testing mutations with IteratorWithBase
|
|
TEST_F(WBWIOverwriteTest, MutateWhileIteratingBaseStressTest) {
|
|
for (char c = 'a'; c <= 'z'; ++c) {
|
|
ASSERT_OK(batch_->Put(std::string(1, c), std::string(1, c)));
|
|
}
|
|
|
|
KVMap map;
|
|
for (char c = 'a'; c <= 'z'; ++c) {
|
|
map[std::string(2, c)] = std::string(2, c);
|
|
}
|
|
|
|
std::unique_ptr<Iterator> iter(batch_->NewIteratorWithBase(new KVIter(&map)));
|
|
|
|
Random rnd(301);
|
|
for (int i = 0; i < 1000000; ++i) {
|
|
int random = rnd.Uniform(8);
|
|
char c = static_cast<char>(rnd.Uniform(26) + 'a');
|
|
switch (random) {
|
|
case 0:
|
|
ASSERT_OK(batch_->Put(std::string(1, c), "xxx"));
|
|
break;
|
|
case 1:
|
|
ASSERT_OK(batch_->Put(std::string(2, c), "xxx"));
|
|
break;
|
|
case 2:
|
|
ASSERT_OK(batch_->Delete(std::string(1, c)));
|
|
break;
|
|
case 3:
|
|
ASSERT_OK(batch_->Delete(std::string(2, c)));
|
|
break;
|
|
case 4:
|
|
iter->Seek(std::string(1, c));
|
|
break;
|
|
case 5:
|
|
iter->Seek(std::string(2, c));
|
|
break;
|
|
case 6:
|
|
if (iter->Valid()) {
|
|
iter->Next();
|
|
}
|
|
break;
|
|
case 7:
|
|
if (iter->Valid()) {
|
|
iter->Prev();
|
|
}
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
}
|
|
ASSERT_OK(iter->status());
|
|
}
|
|
|
|
TEST_P(WriteBatchWithIndexTest, SavePointTest) {
|
|
ColumnFamilyHandleImplDummy cf1(1, BytewiseComparator());
|
|
KVMap empty_map;
|
|
std::unique_ptr<Iterator> cf0_iter(
|
|
batch_->NewIteratorWithBase(new KVIter(&empty_map)));
|
|
std::unique_ptr<Iterator> cf1_iter(
|
|
batch_->NewIteratorWithBase(&cf1, new KVIter(&empty_map)));
|
|
Status s;
|
|
KVMap kvm_cf0_0 = {{"A", "aa"}, {"B", "b"}};
|
|
KVMap kvm_cf1_0 = {{"A", "a1"}, {"C", "c1"}, {"E", "e1"}};
|
|
KVIter kvi_cf0_0(&kvm_cf0_0);
|
|
KVIter kvi_cf1_0(&kvm_cf1_0);
|
|
|
|
ASSERT_OK(batch_->Put("A", "a"));
|
|
ASSERT_OK(batch_->Put("B", "b"));
|
|
ASSERT_OK(batch_->Put("A", "aa"));
|
|
ASSERT_OK(batch_->Put(&cf1, "A", "a1"));
|
|
ASSERT_OK(batch_->Delete(&cf1, "B"));
|
|
ASSERT_OK(batch_->Put(&cf1, "C", "c1"));
|
|
ASSERT_OK(batch_->Put(&cf1, "E", "e1"));
|
|
|
|
AssertItersEqual(cf0_iter.get(), &kvi_cf0_0);
|
|
AssertItersEqual(cf1_iter.get(), &kvi_cf1_0);
|
|
batch_->SetSavePoint(); // 1
|
|
|
|
KVMap kvm_cf0_1 = {{"B", "bb"}, {"C", "cc"}};
|
|
KVMap kvm_cf1_1 = {{"B", "b1"}, {"C", "c1"}};
|
|
KVIter kvi_cf0_1(&kvm_cf0_1);
|
|
KVIter kvi_cf1_1(&kvm_cf1_1);
|
|
|
|
ASSERT_OK(batch_->Put("C", "cc"));
|
|
ASSERT_OK(batch_->Put("B", "bb"));
|
|
ASSERT_OK(batch_->Delete("A"));
|
|
ASSERT_OK(batch_->Put(&cf1, "B", "b1"));
|
|
ASSERT_OK(batch_->Delete(&cf1, "A"));
|
|
ASSERT_OK(batch_->SingleDelete(&cf1, "E"));
|
|
batch_->SetSavePoint(); // 2
|
|
AssertItersEqual(cf0_iter.get(), &kvi_cf0_1);
|
|
AssertItersEqual(cf1_iter.get(), &kvi_cf1_1);
|
|
|
|
KVMap kvm_cf0_2 = {{"A", "xxx"}, {"C", "cc"}};
|
|
KVMap kvm_cf1_2 = {{"B", "b2"}};
|
|
KVIter kvi_cf0_2(&kvm_cf0_2);
|
|
KVIter kvi_cf1_2(&kvm_cf1_2);
|
|
|
|
ASSERT_OK(batch_->Put("A", "aaa"));
|
|
ASSERT_OK(batch_->Put("A", "xxx"));
|
|
ASSERT_OK(batch_->Delete("B"));
|
|
ASSERT_OK(batch_->Put(&cf1, "B", "b2"));
|
|
ASSERT_OK(batch_->Delete(&cf1, "C"));
|
|
batch_->SetSavePoint(); // 3
|
|
batch_->SetSavePoint(); // 4
|
|
AssertItersEqual(cf0_iter.get(), &kvi_cf0_2);
|
|
AssertItersEqual(cf1_iter.get(), &kvi_cf1_2);
|
|
|
|
KVMap kvm_cf0_4 = {{"A", "xxx"}, {"C", "cc"}};
|
|
KVMap kvm_cf1_4 = {{"B", "b2"}};
|
|
KVIter kvi_cf0_4(&kvm_cf0_4);
|
|
KVIter kvi_cf1_4(&kvm_cf1_4);
|
|
ASSERT_OK(batch_->SingleDelete("D"));
|
|
ASSERT_OK(batch_->Delete(&cf1, "D"));
|
|
ASSERT_OK(batch_->Delete(&cf1, "E"));
|
|
AssertItersEqual(cf0_iter.get(), &kvi_cf0_4);
|
|
AssertItersEqual(cf1_iter.get(), &kvi_cf1_4);
|
|
|
|
ASSERT_OK(batch_->RollbackToSavePoint()); // rollback to 4
|
|
AssertItersEqual(cf0_iter.get(), &kvi_cf0_2);
|
|
AssertItersEqual(cf1_iter.get(), &kvi_cf1_2);
|
|
|
|
ASSERT_OK(batch_->RollbackToSavePoint()); // rollback to 3
|
|
AssertItersEqual(cf0_iter.get(), &kvi_cf0_2);
|
|
AssertItersEqual(cf1_iter.get(), &kvi_cf1_2);
|
|
|
|
ASSERT_OK(batch_->RollbackToSavePoint()); // rollback to 2
|
|
AssertItersEqual(cf0_iter.get(), &kvi_cf0_1);
|
|
AssertItersEqual(cf1_iter.get(), &kvi_cf1_1);
|
|
|
|
batch_->SetSavePoint(); // 5
|
|
ASSERT_OK(batch_->Put("X", "x"));
|
|
|
|
KVMap kvm_cf0_5 = {{"B", "bb"}, {"C", "cc"}, {"X", "x"}};
|
|
KVIter kvi_cf0_5(&kvm_cf0_5);
|
|
KVIter kvi_cf1_5(&kvm_cf1_1);
|
|
AssertItersEqual(cf0_iter.get(), &kvi_cf0_5);
|
|
AssertItersEqual(cf1_iter.get(), &kvi_cf1_5);
|
|
|
|
ASSERT_OK(batch_->RollbackToSavePoint()); // rollback to 5
|
|
AssertItersEqual(cf0_iter.get(), &kvi_cf0_1);
|
|
AssertItersEqual(cf1_iter.get(), &kvi_cf1_1);
|
|
|
|
ASSERT_OK(batch_->RollbackToSavePoint()); // rollback to 1
|
|
AssertItersEqual(cf0_iter.get(), &kvi_cf0_0);
|
|
AssertItersEqual(cf1_iter.get(), &kvi_cf1_0);
|
|
|
|
s = batch_->RollbackToSavePoint(); // no savepoint found
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
AssertItersEqual(cf0_iter.get(), &kvi_cf0_0);
|
|
AssertItersEqual(cf1_iter.get(), &kvi_cf1_0);
|
|
|
|
batch_->SetSavePoint(); // 6
|
|
|
|
batch_->Clear();
|
|
ASSERT_EQ("", PrintContents(batch_.get(), nullptr));
|
|
ASSERT_EQ("", PrintContents(batch_.get(), &cf1));
|
|
|
|
s = batch_->RollbackToSavePoint(); // rollback to 6
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
}
|
|
|
|
TEST_P(WriteBatchWithIndexTest, SingleDeleteTest) {
|
|
Status s;
|
|
std::string value;
|
|
|
|
ASSERT_OK(batch_->SingleDelete("A"));
|
|
|
|
s = batch_->GetFromBatch(options_, "A", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
s = batch_->GetFromBatch(options_, "B", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
|
|
batch_->Clear();
|
|
ASSERT_OK(batch_->Put("A", "a"));
|
|
ASSERT_OK(batch_->Put("A", "a2"));
|
|
ASSERT_OK(batch_->Put("B", "b"));
|
|
ASSERT_OK(batch_->SingleDelete("A"));
|
|
|
|
s = batch_->GetFromBatch(options_, "A", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
s = batch_->GetFromBatch(options_, "B", &value);
|
|
ASSERT_OK(s);
|
|
ASSERT_EQ("b", value);
|
|
|
|
ASSERT_OK(batch_->Put("C", "c"));
|
|
ASSERT_OK(batch_->Put("A", "a3"));
|
|
ASSERT_OK(batch_->Delete("B"));
|
|
ASSERT_OK(batch_->SingleDelete("B"));
|
|
ASSERT_OK(batch_->SingleDelete("C"));
|
|
|
|
s = batch_->GetFromBatch(options_, "A", &value);
|
|
ASSERT_OK(s);
|
|
ASSERT_EQ("a3", value);
|
|
s = batch_->GetFromBatch(options_, "B", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
s = batch_->GetFromBatch(options_, "C", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
s = batch_->GetFromBatch(options_, "D", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
|
|
ASSERT_OK(batch_->Put("B", "b4"));
|
|
ASSERT_OK(batch_->Put("C", "c4"));
|
|
ASSERT_OK(batch_->Put("D", "d4"));
|
|
ASSERT_OK(batch_->SingleDelete("D"));
|
|
ASSERT_OK(batch_->SingleDelete("D"));
|
|
ASSERT_OK(batch_->Delete("A"));
|
|
|
|
s = batch_->GetFromBatch(options_, "A", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
s = batch_->GetFromBatch(options_, "B", &value);
|
|
ASSERT_OK(s);
|
|
ASSERT_EQ("b4", value);
|
|
s = batch_->GetFromBatch(options_, "C", &value);
|
|
ASSERT_OK(s);
|
|
ASSERT_EQ("c4", value);
|
|
s = batch_->GetFromBatch(options_, "D", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
}
|
|
|
|
TEST_P(WriteBatchWithIndexTest, SingleDeleteDeltaIterTest) {
|
|
std::string value;
|
|
ASSERT_OK(batch_->Put("A", "a"));
|
|
ASSERT_OK(batch_->Put("A", "a2"));
|
|
ASSERT_OK(batch_->Put("B", "b"));
|
|
ASSERT_OK(batch_->SingleDelete("A"));
|
|
ASSERT_OK(batch_->Delete("B"));
|
|
|
|
KVMap map;
|
|
value = PrintContents(batch_.get(), &map, nullptr);
|
|
ASSERT_EQ("", value);
|
|
|
|
map["A"] = "aa";
|
|
map["C"] = "cc";
|
|
map["D"] = "dd";
|
|
|
|
ASSERT_OK(batch_->SingleDelete("B"));
|
|
ASSERT_OK(batch_->SingleDelete("C"));
|
|
ASSERT_OK(batch_->SingleDelete("Z"));
|
|
|
|
value = PrintContents(batch_.get(), &map, nullptr);
|
|
ASSERT_EQ("D:dd,", value);
|
|
|
|
ASSERT_OK(batch_->Put("A", "a3"));
|
|
ASSERT_OK(batch_->Put("B", "b3"));
|
|
ASSERT_OK(batch_->SingleDelete("A"));
|
|
ASSERT_OK(batch_->SingleDelete("A"));
|
|
ASSERT_OK(batch_->SingleDelete("D"));
|
|
ASSERT_OK(batch_->SingleDelete("D"));
|
|
ASSERT_OK(batch_->Delete("D"));
|
|
|
|
map["E"] = "ee";
|
|
|
|
value = PrintContents(batch_.get(), &map, nullptr);
|
|
ASSERT_EQ("B:b3,E:ee,", value);
|
|
}
|
|
|
|
TEST_P(WriteBatchWithIndexTest, MultiGetTest) {
|
|
// MultiGet a lot of keys in order to force std::vector reallocations
|
|
std::vector<std::string> keys;
|
|
for (int i = 0; i < 100; ++i) {
|
|
keys.emplace_back(std::to_string(i));
|
|
}
|
|
|
|
ASSERT_OK(OpenDB());
|
|
ColumnFamilyHandle* cf0 = db_->DefaultColumnFamily();
|
|
|
|
// Write some data to the db for the even numbered keys
|
|
{
|
|
WriteBatch wb;
|
|
for (size_t i = 1; i < keys.size(); ++i) {
|
|
std::string val = "val" + std::to_string(i);
|
|
ASSERT_OK(wb.Put(cf0, keys[i], val));
|
|
}
|
|
ASSERT_OK(db_->Write(write_opts_, &wb));
|
|
for (size_t i = 1; i < keys.size(); ++i) {
|
|
std::string value;
|
|
ASSERT_OK(db_->Get(read_opts_, cf0, keys[i], &value));
|
|
}
|
|
}
|
|
|
|
// Write some data to the batch
|
|
for (size_t i = 0; i < keys.size(); ++i) {
|
|
if ((i % 5) == 0) {
|
|
ASSERT_OK(batch_->Delete(cf0, keys[i]));
|
|
} else if ((i % 7) == 0) {
|
|
std::string val = "new" + std::to_string(i);
|
|
ASSERT_OK(batch_->Put(cf0, keys[i], val));
|
|
}
|
|
if (i > 0 && (i % 3) == 0) {
|
|
ASSERT_OK(batch_->Merge(cf0, keys[i], "merge"));
|
|
}
|
|
}
|
|
|
|
std::vector<Slice> key_slices;
|
|
for (size_t i = 0; i < keys.size(); ++i) {
|
|
key_slices.emplace_back(keys[i]);
|
|
}
|
|
std::vector<PinnableSlice> values(keys.size());
|
|
std::vector<Status> statuses(keys.size());
|
|
|
|
batch_->MultiGetFromBatchAndDB(db_, read_opts_, cf0, key_slices.size(),
|
|
key_slices.data(), values.data(),
|
|
statuses.data(), false);
|
|
for (size_t i = 0; i < keys.size(); ++i) {
|
|
if (i == 0) {
|
|
ASSERT_TRUE(statuses[i].IsNotFound());
|
|
} else if ((i % 3) == 0) {
|
|
ASSERT_OK(statuses[i]);
|
|
if ((i % 5) == 0) { // Merge after Delete
|
|
ASSERT_EQ(values[i], "merge");
|
|
} else if ((i % 7) == 0) { // Merge after Put
|
|
std::string val = "new" + std::to_string(i);
|
|
ASSERT_EQ(values[i], val + ",merge");
|
|
} else {
|
|
std::string val = "val" + std::to_string(i);
|
|
ASSERT_EQ(values[i], val + ",merge");
|
|
}
|
|
} else if ((i % 5) == 0) {
|
|
ASSERT_TRUE(statuses[i].IsNotFound());
|
|
} else if ((i % 7) == 0) {
|
|
ASSERT_OK(statuses[i]);
|
|
ASSERT_EQ(values[i], "new" + std::to_string(i));
|
|
} else {
|
|
ASSERT_OK(statuses[i]);
|
|
ASSERT_EQ(values[i], "val" + std::to_string(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
// This test has merges, but the merge does not play into the final result
|
|
TEST_P(WriteBatchWithIndexTest, FakeMergeWithIteratorTest) {
|
|
ASSERT_OK(OpenDB());
|
|
ColumnFamilyHandle* cf0 = db_->DefaultColumnFamily();
|
|
|
|
// The map we are starting with
|
|
KVMap input = {
|
|
{"odm", "odm0"},
|
|
{"omd", "omd0"},
|
|
{"omp", "omp0"},
|
|
};
|
|
KVMap result = {
|
|
{"odm", "odm2"}, // Orig, Delete, Merge
|
|
{"mp", "mp1"}, // Merge, Put
|
|
{"omp", "omp2"}, // Origi, Merge, Put
|
|
{"mmp", "mmp2"} // Merge, Merge, Put
|
|
};
|
|
|
|
for (auto& iter : result) {
|
|
EXPECT_EQ(AddToBatch(cf0, iter.first), iter.second);
|
|
}
|
|
AddToBatch(cf0, "md"); // Merge, Delete
|
|
AddToBatch(cf0, "mmd"); // Merge, Merge, Delete
|
|
AddToBatch(cf0, "omd"); // Orig, Merge, Delete
|
|
|
|
KVIter kvi(&result);
|
|
// First try just the batch
|
|
std::unique_ptr<Iterator> iter(
|
|
batch_->NewIteratorWithBase(cf0, new KVIter(&input)));
|
|
AssertItersEqual(iter.get(), &kvi);
|
|
}
|
|
|
|
TEST_P(WriteBatchWithIndexTest, IteratorMergeTest) {
|
|
ASSERT_OK(OpenDB());
|
|
ColumnFamilyHandle* cf0 = db_->DefaultColumnFamily();
|
|
|
|
KVMap result = {
|
|
{"m", "m0"}, // Merge
|
|
{"mm", "mm0,mm1"}, // Merge, Merge
|
|
{"dm", "dm1"}, // Delete, Merge
|
|
{"dmm", "dmm1,dmm2"}, // Delete, Merge, Merge
|
|
{"mdm", "mdm2"}, // Merge, Delete, Merge
|
|
{"mpm", "mpm1,mpm2"}, // Merge, Put, Merge
|
|
{"pm", "pm0,pm1"}, // Put, Merge
|
|
{"pmm", "pmm0,pmm1,pmm2"}, // Put, Merge, Merge
|
|
};
|
|
|
|
for (auto& iter : result) {
|
|
EXPECT_EQ(AddToBatch(cf0, iter.first), iter.second);
|
|
}
|
|
|
|
KVIter kvi(&result);
|
|
// First try just the batch
|
|
KVMap empty_map;
|
|
std::unique_ptr<Iterator> iter(
|
|
batch_->NewIteratorWithBase(cf0, new KVIter(&empty_map)));
|
|
AssertItersEqual(iter.get(), &kvi);
|
|
}
|
|
|
|
TEST_P(WriteBatchWithIndexTest, IteratorMergeTestWithOrig) {
|
|
ASSERT_OK(OpenDB());
|
|
ColumnFamilyHandle* cf0 = db_->DefaultColumnFamily();
|
|
KVMap original;
|
|
KVMap results = {
|
|
{"m", "om,m0"}, // Merge
|
|
{"mm", "omm,mm0,mm1"}, // Merge, Merge
|
|
{"dm", "dm1"}, // Delete, Merge
|
|
{"dmm", "dmm1,dmm2"}, // Delete, Merge, Merge
|
|
{"mdm", "mdm2"}, // Merge, Delete, Merge
|
|
{"mpm", "mpm1,mpm2"}, // Merge, Put, Merge
|
|
{"pm", "pm0,pm1"}, // Put, Merge
|
|
{"pmm", "pmm0,pmm1,pmm2"}, // Put, Merge, Merge
|
|
};
|
|
|
|
for (auto& iter : results) {
|
|
AddToBatch(cf0, iter.first);
|
|
original[iter.first] = "o" + iter.first;
|
|
}
|
|
|
|
KVIter kvi(&results);
|
|
// First try just the batch
|
|
std::unique_ptr<Iterator> iter(
|
|
batch_->NewIteratorWithBase(cf0, new KVIter(&original)));
|
|
AssertItersEqual(iter.get(), &kvi);
|
|
}
|
|
|
|
TEST_P(WriteBatchWithIndexTest, GetFromBatchAfterMerge) {
|
|
std::string value;
|
|
Status s;
|
|
|
|
ASSERT_OK(OpenDB());
|
|
ASSERT_OK(db_->Put(write_opts_, "o", "aa"));
|
|
batch_->Merge("o", "bb"); // Merging bb under key "o"
|
|
batch_->Merge("m", "cc"); // Merging bc under key "m"
|
|
s = batch_->GetFromBatch(options_, "m", &value);
|
|
ASSERT_EQ(s.code(), Status::Code::kMergeInProgress);
|
|
s = batch_->GetFromBatch(options_, "o", &value);
|
|
ASSERT_EQ(s.code(), Status::Code::kMergeInProgress);
|
|
|
|
ASSERT_OK(db_->Write(write_opts_, batch_->GetWriteBatch()));
|
|
ASSERT_OK(db_->Get(read_opts_, "o", &value));
|
|
ASSERT_EQ(value, "aa,bb");
|
|
ASSERT_OK(db_->Get(read_opts_, "m", &value));
|
|
ASSERT_EQ(value, "cc");
|
|
}
|
|
|
|
TEST_P(WriteBatchWithIndexTest, GetFromBatchAndDBAfterMerge) {
|
|
std::string value;
|
|
|
|
ASSERT_OK(OpenDB());
|
|
ASSERT_OK(db_->Put(write_opts_, "o", "aa"));
|
|
ASSERT_OK(batch_->Merge("o", "bb")); // Merging bb under key "o"
|
|
ASSERT_OK(batch_->Merge("m", "cc")); // Merging bc under key "m"
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "o", &value));
|
|
ASSERT_EQ(value, "aa,bb");
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "m", &value));
|
|
ASSERT_EQ(value, "cc");
|
|
}
|
|
|
|
TEST_F(WBWIKeepTest, GetAfterPut) {
|
|
std::string value;
|
|
ASSERT_OK(OpenDB());
|
|
ColumnFamilyHandle* cf0 = db_->DefaultColumnFamily();
|
|
|
|
ASSERT_OK(db_->Put(write_opts_, "key", "orig"));
|
|
|
|
ASSERT_OK(batch_->Put("key", "aa")); // Writing aa under key
|
|
ASSERT_OK(batch_->GetFromBatch(cf0, options_, "key", &value));
|
|
ASSERT_EQ(value, "aa");
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "key", &value));
|
|
ASSERT_EQ(value, "aa");
|
|
|
|
ASSERT_OK(batch_->Merge("key", "bb")); // Merging bb under key
|
|
ASSERT_OK(batch_->GetFromBatch(cf0, options_, "key", &value));
|
|
ASSERT_EQ(value, "aa,bb");
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "key", &value));
|
|
ASSERT_EQ(value, "aa,bb");
|
|
|
|
ASSERT_OK(batch_->Merge("key", "cc")); // Merging cc under key
|
|
ASSERT_OK(batch_->GetFromBatch(cf0, options_, "key", &value));
|
|
ASSERT_EQ(value, "aa,bb,cc");
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "key", &value));
|
|
ASSERT_EQ(value, "aa,bb,cc");
|
|
}
|
|
|
|
TEST_P(WriteBatchWithIndexTest, GetAfterMergePut) {
|
|
std::string value;
|
|
ASSERT_OK(OpenDB());
|
|
ColumnFamilyHandle* cf0 = db_->DefaultColumnFamily();
|
|
ASSERT_OK(db_->Put(write_opts_, "key", "orig"));
|
|
|
|
ASSERT_OK(batch_->Merge("key", "aa")); // Merging aa under key
|
|
Status s = batch_->GetFromBatch(cf0, options_, "key", &value);
|
|
ASSERT_EQ(s.code(), Status::Code::kMergeInProgress);
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "key", &value));
|
|
ASSERT_EQ(value, "orig,aa");
|
|
|
|
ASSERT_OK(batch_->Merge("key", "bb")); // Merging bb under key
|
|
s = batch_->GetFromBatch(cf0, options_, "key", &value);
|
|
ASSERT_EQ(s.code(), Status::Code::kMergeInProgress);
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "key", &value));
|
|
ASSERT_EQ(value, "orig,aa,bb");
|
|
|
|
ASSERT_OK(batch_->Put("key", "cc")); // Writing cc under key
|
|
ASSERT_OK(batch_->GetFromBatch(cf0, options_, "key", &value));
|
|
ASSERT_EQ(value, "cc");
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "key", &value));
|
|
ASSERT_EQ(value, "cc");
|
|
|
|
ASSERT_OK(batch_->Merge("key", "dd")); // Merging dd under key
|
|
ASSERT_OK(batch_->GetFromBatch(cf0, options_, "key", &value));
|
|
ASSERT_EQ(value, "cc,dd");
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "key", &value));
|
|
ASSERT_EQ(value, "cc,dd");
|
|
}
|
|
|
|
TEST_P(WriteBatchWithIndexTest, GetAfterMergeDelete) {
|
|
std::string value;
|
|
ASSERT_OK(OpenDB());
|
|
ColumnFamilyHandle* cf0 = db_->DefaultColumnFamily();
|
|
|
|
ASSERT_OK(batch_->Merge("key", "aa")); // Merging aa under key
|
|
Status s = batch_->GetFromBatch(cf0, options_, "key", &value);
|
|
ASSERT_EQ(s.code(), Status::Code::kMergeInProgress);
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "key", &value));
|
|
ASSERT_EQ(value, "aa");
|
|
|
|
ASSERT_OK(batch_->Merge("key", "bb")); // Merging bb under key
|
|
s = batch_->GetFromBatch(cf0, options_, "key", &value);
|
|
ASSERT_EQ(s.code(), Status::Code::kMergeInProgress);
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "key", &value));
|
|
ASSERT_EQ(value, "aa,bb");
|
|
|
|
ASSERT_OK(batch_->Delete("key")); // Delete key from batch
|
|
s = batch_->GetFromBatch(cf0, options_, "key", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
s = batch_->GetFromBatchAndDB(db_, read_opts_, "key", &value);
|
|
ASSERT_TRUE(s.IsNotFound());
|
|
|
|
ASSERT_OK(batch_->Merge("key", "cc")); // Merging cc under key
|
|
ASSERT_OK(batch_->GetFromBatch(cf0, options_, "key", &value));
|
|
ASSERT_EQ(value, "cc");
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "key", &value));
|
|
ASSERT_EQ(value, "cc");
|
|
ASSERT_OK(batch_->Merge("key", "dd")); // Merging dd under key
|
|
ASSERT_OK(batch_->GetFromBatch(cf0, options_, "key", &value));
|
|
ASSERT_EQ(value, "cc,dd");
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "key", &value));
|
|
ASSERT_EQ(value, "cc,dd");
|
|
}
|
|
|
|
TEST_F(WBWIOverwriteTest, TestBadMergeOperator) {
|
|
class FailingMergeOperator : public MergeOperator {
|
|
public:
|
|
FailingMergeOperator() {}
|
|
|
|
bool FullMergeV2(const MergeOperationInput& /*merge_in*/,
|
|
MergeOperationOutput* /*merge_out*/) const override {
|
|
return false;
|
|
}
|
|
|
|
const char* Name() const override { return "Failing"; }
|
|
};
|
|
options_.merge_operator.reset(new FailingMergeOperator());
|
|
ASSERT_OK(OpenDB());
|
|
|
|
ColumnFamilyHandle* column_family = db_->DefaultColumnFamily();
|
|
std::string value;
|
|
|
|
ASSERT_OK(db_->Put(write_opts_, "a", "a0"));
|
|
ASSERT_OK(batch_->Put("b", "b0"));
|
|
|
|
ASSERT_OK(batch_->Merge("a", "a1"));
|
|
ASSERT_NOK(batch_->GetFromBatchAndDB(db_, read_opts_, "a", &value));
|
|
ASSERT_NOK(batch_->GetFromBatch(column_family, options_, "a", &value));
|
|
ASSERT_OK(batch_->GetFromBatchAndDB(db_, read_opts_, "b", &value));
|
|
ASSERT_OK(batch_->GetFromBatch(column_family, options_, "b", &value));
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(WBWI, WriteBatchWithIndexTest, testing::Bool());
|
|
} // namespace ROCKSDB_NAMESPACE
|
|
|
|
int main(int argc, char** argv) {
|
|
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
return RUN_ALL_TESTS();
|
|
}
|
|
|
|
#else
|
|
#include <stdio.h>
|
|
|
|
int main() {
|
|
fprintf(stderr, "SKIPPED\n");
|
|
return 0;
|
|
}
|
|
|
|
#endif // !ROCKSDB_LITE
|