| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888 | //  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.#include "rocksdb/db.h"#include <memory>#include "db/column_family.h"#include "db/memtable.h"#include "db/write_batch_internal.h"#include "rocksdb/env.h"#include "rocksdb/memtablerep.h"#include "rocksdb/utilities/write_batch_with_index.h"#include "rocksdb/write_buffer_manager.h"#include "table/scoped_arena_iterator.h"#include "test_util/testharness.h"#include "util/string_util.h"namespace ROCKSDB_NAMESPACE {static std::string PrintContents(WriteBatch* b) {  InternalKeyComparator cmp(BytewiseComparator());  auto factory = std::make_shared<SkipListFactory>();  Options options;  options.memtable_factory = factory;  ImmutableCFOptions ioptions(options);  WriteBufferManager wb(options.db_write_buffer_size);  MemTable* mem = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb,                               kMaxSequenceNumber, 0 /* column_family_id */);  mem->Ref();  std::string state;  ColumnFamilyMemTablesDefault cf_mems_default(mem);  Status s =      WriteBatchInternal::InsertInto(b, &cf_mems_default, nullptr, nullptr);  uint32_t count = 0;  int put_count = 0;  int delete_count = 0;  int single_delete_count = 0;  int delete_range_count = 0;  int merge_count = 0;  for (int i = 0; i < 2; ++i) {    Arena arena;    ScopedArenaIterator arena_iter_guard;    std::unique_ptr<InternalIterator> iter_guard;    InternalIterator* iter;    if (i == 0) {      iter = mem->NewIterator(ReadOptions(), &arena);      arena_iter_guard.set(iter);    } else {      iter = mem->NewRangeTombstoneIterator(ReadOptions(),                                            kMaxSequenceNumber /* read_seq */);      iter_guard.reset(iter);    }    if (iter == nullptr) {      continue;    }    for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {      ParsedInternalKey ikey;      ikey.clear();      EXPECT_TRUE(ParseInternalKey(iter->key(), &ikey));      switch (ikey.type) {        case kTypeValue:          state.append("Put(");          state.append(ikey.user_key.ToString());          state.append(", ");          state.append(iter->value().ToString());          state.append(")");          count++;          put_count++;          break;        case kTypeDeletion:          state.append("Delete(");          state.append(ikey.user_key.ToString());          state.append(")");          count++;          delete_count++;          break;        case kTypeSingleDeletion:          state.append("SingleDelete(");          state.append(ikey.user_key.ToString());          state.append(")");          count++;          single_delete_count++;          break;        case kTypeRangeDeletion:          state.append("DeleteRange(");          state.append(ikey.user_key.ToString());          state.append(", ");          state.append(iter->value().ToString());          state.append(")");          count++;          delete_range_count++;          break;        case kTypeMerge:          state.append("Merge(");          state.append(ikey.user_key.ToString());          state.append(", ");          state.append(iter->value().ToString());          state.append(")");          count++;          merge_count++;          break;        default:          assert(false);          break;      }      state.append("@");      state.append(NumberToString(ikey.sequence));    }  }  EXPECT_EQ(b->HasPut(), put_count > 0);  EXPECT_EQ(b->HasDelete(), delete_count > 0);  EXPECT_EQ(b->HasSingleDelete(), single_delete_count > 0);  EXPECT_EQ(b->HasDeleteRange(), delete_range_count > 0);  EXPECT_EQ(b->HasMerge(), merge_count > 0);  if (!s.ok()) {    state.append(s.ToString());  } else if (count != WriteBatchInternal::Count(b)) {    state.append("CountMismatch()");  }  delete mem->Unref();  return state;}class WriteBatchTest : public testing::Test {};TEST_F(WriteBatchTest, Empty) {  WriteBatch batch;  ASSERT_EQ("", PrintContents(&batch));  ASSERT_EQ(0u, WriteBatchInternal::Count(&batch));  ASSERT_EQ(0u, batch.Count());}TEST_F(WriteBatchTest, Multiple) {  WriteBatch batch;  batch.Put(Slice("foo"), Slice("bar"));  batch.Delete(Slice("box"));  batch.DeleteRange(Slice("bar"), Slice("foo"));  batch.Put(Slice("baz"), Slice("boo"));  WriteBatchInternal::SetSequence(&batch, 100);  ASSERT_EQ(100U, WriteBatchInternal::Sequence(&batch));  ASSERT_EQ(4u, WriteBatchInternal::Count(&batch));  ASSERT_EQ(      "Put(baz, boo)@103"      "Delete(box)@101"      "Put(foo, bar)@100"      "DeleteRange(bar, foo)@102",      PrintContents(&batch));  ASSERT_EQ(4u, batch.Count());}TEST_F(WriteBatchTest, Corruption) {  WriteBatch batch;  batch.Put(Slice("foo"), Slice("bar"));  batch.Delete(Slice("box"));  WriteBatchInternal::SetSequence(&batch, 200);  Slice contents = WriteBatchInternal::Contents(&batch);  WriteBatchInternal::SetContents(&batch,                                  Slice(contents.data(),contents.size()-1));  ASSERT_EQ("Put(foo, bar)@200"            "Corruption: bad WriteBatch Delete",            PrintContents(&batch));}TEST_F(WriteBatchTest, Append) {  WriteBatch b1, b2;  WriteBatchInternal::SetSequence(&b1, 200);  WriteBatchInternal::SetSequence(&b2, 300);  WriteBatchInternal::Append(&b1, &b2);  ASSERT_EQ("",            PrintContents(&b1));  ASSERT_EQ(0u, b1.Count());  b2.Put("a", "va");  WriteBatchInternal::Append(&b1, &b2);  ASSERT_EQ("Put(a, va)@200",            PrintContents(&b1));  ASSERT_EQ(1u, b1.Count());  b2.Clear();  b2.Put("b", "vb");  WriteBatchInternal::Append(&b1, &b2);  ASSERT_EQ("Put(a, va)@200"            "Put(b, vb)@201",            PrintContents(&b1));  ASSERT_EQ(2u, b1.Count());  b2.Delete("foo");  WriteBatchInternal::Append(&b1, &b2);  ASSERT_EQ("Put(a, va)@200"            "Put(b, vb)@202"            "Put(b, vb)@201"            "Delete(foo)@203",            PrintContents(&b1));  ASSERT_EQ(4u, b1.Count());  b2.Clear();  b2.Put("c", "cc");  b2.Put("d", "dd");  b2.MarkWalTerminationPoint();  b2.Put("e", "ee");  WriteBatchInternal::Append(&b1, &b2, /*wal only*/ true);  ASSERT_EQ(      "Put(a, va)@200"      "Put(b, vb)@202"      "Put(b, vb)@201"      "Put(c, cc)@204"      "Put(d, dd)@205"      "Delete(foo)@203",      PrintContents(&b1));  ASSERT_EQ(6u, b1.Count());  ASSERT_EQ(      "Put(c, cc)@0"      "Put(d, dd)@1"      "Put(e, ee)@2",      PrintContents(&b2));  ASSERT_EQ(3u, b2.Count());}TEST_F(WriteBatchTest, SingleDeletion) {  WriteBatch batch;  WriteBatchInternal::SetSequence(&batch, 100);  ASSERT_EQ("", PrintContents(&batch));  ASSERT_EQ(0u, batch.Count());  batch.Put("a", "va");  ASSERT_EQ("Put(a, va)@100", PrintContents(&batch));  ASSERT_EQ(1u, batch.Count());  batch.SingleDelete("a");  ASSERT_EQ(      "SingleDelete(a)@101"      "Put(a, va)@100",      PrintContents(&batch));  ASSERT_EQ(2u, batch.Count());}namespace {  struct TestHandler : public WriteBatch::Handler {    std::string seen;    Status PutCF(uint32_t column_family_id, const Slice& key,                 const Slice& value) override {      if (column_family_id == 0) {        seen += "Put(" + key.ToString() + ", " + value.ToString() + ")";      } else {        seen += "PutCF(" + ToString(column_family_id) + ", " +                key.ToString() + ", " + value.ToString() + ")";      }      return Status::OK();    }    Status DeleteCF(uint32_t column_family_id, const Slice& key) override {      if (column_family_id == 0) {        seen += "Delete(" + key.ToString() + ")";      } else {        seen += "DeleteCF(" + ToString(column_family_id) + ", " +                key.ToString() + ")";      }      return Status::OK();    }    Status SingleDeleteCF(uint32_t column_family_id,                          const Slice& key) override {      if (column_family_id == 0) {        seen += "SingleDelete(" + key.ToString() + ")";      } else {        seen += "SingleDeleteCF(" + ToString(column_family_id) + ", " +                key.ToString() + ")";      }      return Status::OK();    }    Status DeleteRangeCF(uint32_t column_family_id, const Slice& begin_key,                         const Slice& end_key) override {      if (column_family_id == 0) {        seen += "DeleteRange(" + begin_key.ToString() + ", " +                end_key.ToString() + ")";      } else {        seen += "DeleteRangeCF(" + ToString(column_family_id) + ", " +                begin_key.ToString() + ", " + end_key.ToString() + ")";      }      return Status::OK();    }    Status MergeCF(uint32_t column_family_id, const Slice& key,                   const Slice& value) override {      if (column_family_id == 0) {        seen += "Merge(" + key.ToString() + ", " + value.ToString() + ")";      } else {        seen += "MergeCF(" + ToString(column_family_id) + ", " +                key.ToString() + ", " + value.ToString() + ")";      }      return Status::OK();    }    void LogData(const Slice& blob) override {      seen += "LogData(" + blob.ToString() + ")";    }    Status MarkBeginPrepare(bool unprepare) override {      seen +=          "MarkBeginPrepare(" + std::string(unprepare ? "true" : "false") + ")";      return Status::OK();    }    Status MarkEndPrepare(const Slice& xid) override {      seen += "MarkEndPrepare(" + xid.ToString() + ")";      return Status::OK();    }    Status MarkNoop(bool empty_batch) override {      seen += "MarkNoop(" + std::string(empty_batch ? "true" : "false") + ")";      return Status::OK();    }    Status MarkCommit(const Slice& xid) override {      seen += "MarkCommit(" + xid.ToString() + ")";      return Status::OK();    }    Status MarkRollback(const Slice& xid) override {      seen += "MarkRollback(" + xid.ToString() + ")";      return Status::OK();    }  };}TEST_F(WriteBatchTest, PutNotImplemented) {  WriteBatch batch;  batch.Put(Slice("k1"), Slice("v1"));  ASSERT_EQ(1u, batch.Count());  ASSERT_EQ("Put(k1, v1)@0", PrintContents(&batch));  WriteBatch::Handler handler;  ASSERT_OK(batch.Iterate(&handler));}TEST_F(WriteBatchTest, DeleteNotImplemented) {  WriteBatch batch;  batch.Delete(Slice("k2"));  ASSERT_EQ(1u, batch.Count());  ASSERT_EQ("Delete(k2)@0", PrintContents(&batch));  WriteBatch::Handler handler;  ASSERT_OK(batch.Iterate(&handler));}TEST_F(WriteBatchTest, SingleDeleteNotImplemented) {  WriteBatch batch;  batch.SingleDelete(Slice("k2"));  ASSERT_EQ(1u, batch.Count());  ASSERT_EQ("SingleDelete(k2)@0", PrintContents(&batch));  WriteBatch::Handler handler;  ASSERT_OK(batch.Iterate(&handler));}TEST_F(WriteBatchTest, MergeNotImplemented) {  WriteBatch batch;  batch.Merge(Slice("foo"), Slice("bar"));  ASSERT_EQ(1u, batch.Count());  ASSERT_EQ("Merge(foo, bar)@0", PrintContents(&batch));  WriteBatch::Handler handler;  ASSERT_OK(batch.Iterate(&handler));}TEST_F(WriteBatchTest, Blob) {  WriteBatch batch;  batch.Put(Slice("k1"), Slice("v1"));  batch.Put(Slice("k2"), Slice("v2"));  batch.Put(Slice("k3"), Slice("v3"));  batch.PutLogData(Slice("blob1"));  batch.Delete(Slice("k2"));  batch.SingleDelete(Slice("k3"));  batch.PutLogData(Slice("blob2"));  batch.Merge(Slice("foo"), Slice("bar"));  ASSERT_EQ(6u, batch.Count());  ASSERT_EQ(      "Merge(foo, bar)@5"      "Put(k1, v1)@0"      "Delete(k2)@3"      "Put(k2, v2)@1"      "SingleDelete(k3)@4"      "Put(k3, v3)@2",      PrintContents(&batch));  TestHandler handler;  batch.Iterate(&handler);  ASSERT_EQ(      "Put(k1, v1)"      "Put(k2, v2)"      "Put(k3, v3)"      "LogData(blob1)"      "Delete(k2)"      "SingleDelete(k3)"      "LogData(blob2)"      "Merge(foo, bar)",      handler.seen);}TEST_F(WriteBatchTest, PrepareCommit) {  WriteBatch batch;  WriteBatchInternal::InsertNoop(&batch);  batch.Put(Slice("k1"), Slice("v1"));  batch.Put(Slice("k2"), Slice("v2"));  batch.SetSavePoint();  WriteBatchInternal::MarkEndPrepare(&batch, Slice("xid1"));  Status s = batch.RollbackToSavePoint();  ASSERT_EQ(s, Status::NotFound());  WriteBatchInternal::MarkCommit(&batch, Slice("xid1"));  WriteBatchInternal::MarkRollback(&batch, Slice("xid1"));  ASSERT_EQ(2u, batch.Count());  TestHandler handler;  batch.Iterate(&handler);  ASSERT_EQ(      "MarkBeginPrepare(false)"      "Put(k1, v1)"      "Put(k2, v2)"      "MarkEndPrepare(xid1)"      "MarkCommit(xid1)"      "MarkRollback(xid1)",      handler.seen);}// It requires more than 30GB of memory to run the test. With single memory// allocation of more than 30GB.// Not all platform can run it. Also it runs a long time. So disable it.TEST_F(WriteBatchTest, DISABLED_ManyUpdates) {  // Insert key and value of 3GB and push total batch size to 12GB.  static const size_t kKeyValueSize = 4u;  static const uint32_t kNumUpdates = uint32_t(3 << 30);  std::string raw(kKeyValueSize, 'A');  WriteBatch batch(kNumUpdates * (4 + kKeyValueSize * 2) + 1024u);  char c = 'A';  for (uint32_t i = 0; i < kNumUpdates; i++) {    if (c > 'Z') {      c = 'A';    }    raw[0] = c;    raw[raw.length() - 1] = c;    c++;    batch.Put(raw, raw);  }  ASSERT_EQ(kNumUpdates, batch.Count());  struct NoopHandler : public WriteBatch::Handler {    uint32_t num_seen = 0;    char expected_char = 'A';    Status PutCF(uint32_t /*column_family_id*/, const Slice& key,                 const Slice& value) override {      EXPECT_EQ(kKeyValueSize, key.size());      EXPECT_EQ(kKeyValueSize, value.size());      EXPECT_EQ(expected_char, key[0]);      EXPECT_EQ(expected_char, value[0]);      EXPECT_EQ(expected_char, key[kKeyValueSize - 1]);      EXPECT_EQ(expected_char, value[kKeyValueSize - 1]);      expected_char++;      if (expected_char > 'Z') {        expected_char = 'A';      }      ++num_seen;      return Status::OK();    }    Status DeleteCF(uint32_t /*column_family_id*/,                    const Slice& /*key*/) override {      ADD_FAILURE();      return Status::OK();    }    Status SingleDeleteCF(uint32_t /*column_family_id*/,                          const Slice& /*key*/) override {      ADD_FAILURE();      return Status::OK();    }    Status MergeCF(uint32_t /*column_family_id*/, const Slice& /*key*/,                   const Slice& /*value*/) override {      ADD_FAILURE();      return Status::OK();    }    void LogData(const Slice& /*blob*/) override { ADD_FAILURE(); }    bool Continue() override { return num_seen < kNumUpdates; }  } handler;  batch.Iterate(&handler);  ASSERT_EQ(kNumUpdates, handler.num_seen);}// The test requires more than 18GB memory to run it, with single memory// allocation of more than 12GB. Not all the platform can run it. So disable it.TEST_F(WriteBatchTest, DISABLED_LargeKeyValue) {  // Insert key and value of 3GB and push total batch size to 12GB.  static const size_t kKeyValueSize = 3221225472u;  std::string raw(kKeyValueSize, 'A');  WriteBatch batch(size_t(12884901888ull + 1024u));  for (char i = 0; i < 2; i++) {    raw[0] = 'A' + i;    raw[raw.length() - 1] = 'A' - i;    batch.Put(raw, raw);  }  ASSERT_EQ(2u, batch.Count());  struct NoopHandler : public WriteBatch::Handler {    int num_seen = 0;    Status PutCF(uint32_t /*column_family_id*/, const Slice& key,                 const Slice& value) override {      EXPECT_EQ(kKeyValueSize, key.size());      EXPECT_EQ(kKeyValueSize, value.size());      EXPECT_EQ('A' + num_seen, key[0]);      EXPECT_EQ('A' + num_seen, value[0]);      EXPECT_EQ('A' - num_seen, key[kKeyValueSize - 1]);      EXPECT_EQ('A' - num_seen, value[kKeyValueSize - 1]);      ++num_seen;      return Status::OK();    }    Status DeleteCF(uint32_t /*column_family_id*/,                    const Slice& /*key*/) override {      ADD_FAILURE();      return Status::OK();    }    Status SingleDeleteCF(uint32_t /*column_family_id*/,                          const Slice& /*key*/) override {      ADD_FAILURE();      return Status::OK();    }    Status MergeCF(uint32_t /*column_family_id*/, const Slice& /*key*/,                   const Slice& /*value*/) override {      ADD_FAILURE();      return Status::OK();    }    void LogData(const Slice& /*blob*/) override { ADD_FAILURE(); }    bool Continue() override { return num_seen < 2; }  } handler;  batch.Iterate(&handler);  ASSERT_EQ(2, handler.num_seen);}TEST_F(WriteBatchTest, Continue) {  WriteBatch batch;  struct Handler : public TestHandler {    int num_seen = 0;    Status PutCF(uint32_t column_family_id, const Slice& key,                 const Slice& value) override {      ++num_seen;      return TestHandler::PutCF(column_family_id, key, value);    }    Status DeleteCF(uint32_t column_family_id, const Slice& key) override {      ++num_seen;      return TestHandler::DeleteCF(column_family_id, key);    }    Status SingleDeleteCF(uint32_t column_family_id,                          const Slice& key) override {      ++num_seen;      return TestHandler::SingleDeleteCF(column_family_id, key);    }    Status MergeCF(uint32_t column_family_id, const Slice& key,                   const Slice& value) override {      ++num_seen;      return TestHandler::MergeCF(column_family_id, key, value);    }    void LogData(const Slice& blob) override {      ++num_seen;      TestHandler::LogData(blob);    }    bool Continue() override { return num_seen < 5; }  } handler;  batch.Put(Slice("k1"), Slice("v1"));  batch.Put(Slice("k2"), Slice("v2"));  batch.PutLogData(Slice("blob1"));  batch.Delete(Slice("k1"));  batch.SingleDelete(Slice("k2"));  batch.PutLogData(Slice("blob2"));  batch.Merge(Slice("foo"), Slice("bar"));  batch.Iterate(&handler);  ASSERT_EQ(      "Put(k1, v1)"      "Put(k2, v2)"      "LogData(blob1)"      "Delete(k1)"      "SingleDelete(k2)",      handler.seen);}TEST_F(WriteBatchTest, PutGatherSlices) {  WriteBatch batch;  batch.Put(Slice("foo"), Slice("bar"));  {    // Try a write where the key is one slice but the value is two    Slice key_slice("baz");    Slice value_slices[2] = { Slice("header"), Slice("payload") };    batch.Put(SliceParts(&key_slice, 1),              SliceParts(value_slices, 2));  }  {    // One where the key is composite but the value is a single slice    Slice key_slices[3] = { Slice("key"), Slice("part2"), Slice("part3") };    Slice value_slice("value");    batch.Put(SliceParts(key_slices, 3),              SliceParts(&value_slice, 1));  }  WriteBatchInternal::SetSequence(&batch, 100);  ASSERT_EQ("Put(baz, headerpayload)@101"            "Put(foo, bar)@100"            "Put(keypart2part3, value)@102",            PrintContents(&batch));  ASSERT_EQ(3u, batch.Count());}namespace {class ColumnFamilyHandleImplDummy : public ColumnFamilyHandleImpl { public:  explicit ColumnFamilyHandleImplDummy(int id)      : ColumnFamilyHandleImpl(nullptr, nullptr, nullptr), id_(id) {}  uint32_t GetID() const override { return id_; }  const Comparator* GetComparator() const override {    return BytewiseComparator();  } private:  uint32_t id_;};}  // namespace anonymousTEST_F(WriteBatchTest, ColumnFamiliesBatchTest) {  WriteBatch batch;  ColumnFamilyHandleImplDummy zero(0), two(2), three(3), eight(8);  batch.Put(&zero, Slice("foo"), Slice("bar"));  batch.Put(&two, Slice("twofoo"), Slice("bar2"));  batch.Put(&eight, Slice("eightfoo"), Slice("bar8"));  batch.Delete(&eight, Slice("eightfoo"));  batch.SingleDelete(&two, Slice("twofoo"));  batch.DeleteRange(&two, Slice("3foo"), Slice("4foo"));  batch.Merge(&three, Slice("threethree"), Slice("3three"));  batch.Put(&zero, Slice("foo"), Slice("bar"));  batch.Merge(Slice("omom"), Slice("nom"));  TestHandler handler;  batch.Iterate(&handler);  ASSERT_EQ(      "Put(foo, bar)"      "PutCF(2, twofoo, bar2)"      "PutCF(8, eightfoo, bar8)"      "DeleteCF(8, eightfoo)"      "SingleDeleteCF(2, twofoo)"      "DeleteRangeCF(2, 3foo, 4foo)"      "MergeCF(3, threethree, 3three)"      "Put(foo, bar)"      "Merge(omom, nom)",      handler.seen);}#ifndef ROCKSDB_LITETEST_F(WriteBatchTest, ColumnFamiliesBatchWithIndexTest) {  WriteBatchWithIndex batch;  ColumnFamilyHandleImplDummy zero(0), two(2), three(3), eight(8);  batch.Put(&zero, Slice("foo"), Slice("bar"));  batch.Put(&two, Slice("twofoo"), Slice("bar2"));  batch.Put(&eight, Slice("eightfoo"), Slice("bar8"));  batch.Delete(&eight, Slice("eightfoo"));  batch.SingleDelete(&two, Slice("twofoo"));  batch.Merge(&three, Slice("threethree"), Slice("3three"));  batch.Put(&zero, Slice("foo"), Slice("bar"));  batch.Merge(Slice("omom"), Slice("nom"));  std::unique_ptr<WBWIIterator> iter;  iter.reset(batch.NewIterator(&eight));  iter->Seek("eightfoo");  ASSERT_OK(iter->status());  ASSERT_TRUE(iter->Valid());  ASSERT_EQ(WriteType::kPutRecord, iter->Entry().type);  ASSERT_EQ("eightfoo", iter->Entry().key.ToString());  ASSERT_EQ("bar8", iter->Entry().value.ToString());  iter->Next();  ASSERT_OK(iter->status());  ASSERT_TRUE(iter->Valid());  ASSERT_EQ(WriteType::kDeleteRecord, iter->Entry().type);  ASSERT_EQ("eightfoo", iter->Entry().key.ToString());  iter->Next();  ASSERT_OK(iter->status());  ASSERT_TRUE(!iter->Valid());  iter.reset(batch.NewIterator(&two));  iter->Seek("twofoo");  ASSERT_OK(iter->status());  ASSERT_TRUE(iter->Valid());  ASSERT_EQ(WriteType::kPutRecord, iter->Entry().type);  ASSERT_EQ("twofoo", iter->Entry().key.ToString());  ASSERT_EQ("bar2", iter->Entry().value.ToString());  iter->Next();  ASSERT_OK(iter->status());  ASSERT_TRUE(iter->Valid());  ASSERT_EQ(WriteType::kSingleDeleteRecord, iter->Entry().type);  ASSERT_EQ("twofoo", iter->Entry().key.ToString());  iter->Next();  ASSERT_OK(iter->status());  ASSERT_TRUE(!iter->Valid());  iter.reset(batch.NewIterator());  iter->Seek("gggg");  ASSERT_OK(iter->status());  ASSERT_TRUE(iter->Valid());  ASSERT_EQ(WriteType::kMergeRecord, iter->Entry().type);  ASSERT_EQ("omom", iter->Entry().key.ToString());  ASSERT_EQ("nom", iter->Entry().value.ToString());  iter->Next();  ASSERT_OK(iter->status());  ASSERT_TRUE(!iter->Valid());  iter.reset(batch.NewIterator(&zero));  iter->Seek("foo");  ASSERT_OK(iter->status());  ASSERT_TRUE(iter->Valid());  ASSERT_EQ(WriteType::kPutRecord, iter->Entry().type);  ASSERT_EQ("foo", iter->Entry().key.ToString());  ASSERT_EQ("bar", iter->Entry().value.ToString());  iter->Next();  ASSERT_OK(iter->status());  ASSERT_TRUE(iter->Valid());  ASSERT_EQ(WriteType::kPutRecord, iter->Entry().type);  ASSERT_EQ("foo", iter->Entry().key.ToString());  ASSERT_EQ("bar", iter->Entry().value.ToString());  iter->Next();  ASSERT_OK(iter->status());  ASSERT_TRUE(iter->Valid());  ASSERT_EQ(WriteType::kMergeRecord, iter->Entry().type);  ASSERT_EQ("omom", iter->Entry().key.ToString());  ASSERT_EQ("nom", iter->Entry().value.ToString());  iter->Next();  ASSERT_OK(iter->status());  ASSERT_TRUE(!iter->Valid());  TestHandler handler;  batch.GetWriteBatch()->Iterate(&handler);  ASSERT_EQ(      "Put(foo, bar)"      "PutCF(2, twofoo, bar2)"      "PutCF(8, eightfoo, bar8)"      "DeleteCF(8, eightfoo)"      "SingleDeleteCF(2, twofoo)"      "MergeCF(3, threethree, 3three)"      "Put(foo, bar)"      "Merge(omom, nom)",      handler.seen);}#endif  // !ROCKSDB_LITETEST_F(WriteBatchTest, SavePointTest) {  Status s;  WriteBatch batch;  batch.SetSavePoint();  batch.Put("A", "a");  batch.Put("B", "b");  batch.SetSavePoint();  batch.Put("C", "c");  batch.Delete("A");  batch.SetSavePoint();  batch.SetSavePoint();  ASSERT_OK(batch.RollbackToSavePoint());  ASSERT_EQ(      "Delete(A)@3"      "Put(A, a)@0"      "Put(B, b)@1"      "Put(C, c)@2",      PrintContents(&batch));  ASSERT_OK(batch.RollbackToSavePoint());  ASSERT_OK(batch.RollbackToSavePoint());  ASSERT_EQ(      "Put(A, a)@0"      "Put(B, b)@1",      PrintContents(&batch));  batch.Delete("A");  batch.Put("B", "bb");  ASSERT_OK(batch.RollbackToSavePoint());  ASSERT_EQ("", PrintContents(&batch));  s = batch.RollbackToSavePoint();  ASSERT_TRUE(s.IsNotFound());  ASSERT_EQ("", PrintContents(&batch));  batch.Put("D", "d");  batch.Delete("A");  batch.SetSavePoint();  batch.Put("A", "aaa");  ASSERT_OK(batch.RollbackToSavePoint());  ASSERT_EQ(      "Delete(A)@1"      "Put(D, d)@0",      PrintContents(&batch));  batch.SetSavePoint();  batch.Put("D", "d");  batch.Delete("A");  ASSERT_OK(batch.RollbackToSavePoint());  ASSERT_EQ(      "Delete(A)@1"      "Put(D, d)@0",      PrintContents(&batch));  s = batch.RollbackToSavePoint();  ASSERT_TRUE(s.IsNotFound());  ASSERT_EQ(      "Delete(A)@1"      "Put(D, d)@0",      PrintContents(&batch));  WriteBatch batch2;  s = batch2.RollbackToSavePoint();  ASSERT_TRUE(s.IsNotFound());  ASSERT_EQ("", PrintContents(&batch2));  batch2.Delete("A");  batch2.SetSavePoint();  s = batch2.RollbackToSavePoint();  ASSERT_OK(s);  ASSERT_EQ("Delete(A)@0", PrintContents(&batch2));  batch2.Clear();  ASSERT_EQ("", PrintContents(&batch2));  batch2.SetSavePoint();  batch2.Delete("B");  ASSERT_EQ("Delete(B)@0", PrintContents(&batch2));  batch2.SetSavePoint();  s = batch2.RollbackToSavePoint();  ASSERT_OK(s);  ASSERT_EQ("Delete(B)@0", PrintContents(&batch2));  s = batch2.RollbackToSavePoint();  ASSERT_OK(s);  ASSERT_EQ("", PrintContents(&batch2));  s = batch2.RollbackToSavePoint();  ASSERT_TRUE(s.IsNotFound());  ASSERT_EQ("", PrintContents(&batch2));  WriteBatch batch3;  s = batch3.PopSavePoint();  ASSERT_TRUE(s.IsNotFound());  ASSERT_EQ("", PrintContents(&batch3));  batch3.SetSavePoint();  batch3.Delete("A");  s = batch3.PopSavePoint();  ASSERT_OK(s);  ASSERT_EQ("Delete(A)@0", PrintContents(&batch3));}TEST_F(WriteBatchTest, MemoryLimitTest) {  Status s;  // The header size is 12 bytes. The two Puts take 8 bytes which gives total  // of 12 + 8 * 2 = 28 bytes.  WriteBatch batch(0, 28);  ASSERT_OK(batch.Put("a", "...."));  ASSERT_OK(batch.Put("b", "...."));  s = batch.Put("c", "....");  ASSERT_TRUE(s.IsMemoryLimit());}}  // namespace ROCKSDB_NAMESPACEint main(int argc, char** argv) {  ::testing::InitGoogleTest(&argc, argv);  return RUN_ALL_TESTS();}
 |