rocksdb/utilities/write_batch_with_index/write_batch_with_index_test.cc
mrambacher ff463742b5 Add Merge Operator support to WriteBatchWithIndex (#8135)
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
2021-05-10 12:50:25 -07:00

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