summaryrefslogtreecommitdiff
path: root/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_sorted_impl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_sorted_impl.cpp')
-rw-r--r--src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_sorted_impl.cpp1533
1 files changed, 1533 insertions, 0 deletions
diff --git a/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_sorted_impl.cpp b/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_sorted_impl.cpp
new file mode 100644
index 00000000000..a4737f23e03
--- /dev/null
+++ b/src/mongo/db/storage/ephemeral_for_test/ephemeral_for_test_sorted_impl.cpp
@@ -0,0 +1,1533 @@
+/**
+ * Copyright (C) 2018-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kStorage
+
+#include "mongo/platform/basic.h"
+
+#include <boost/iterator/iterator_facade.hpp>
+#include <cstring>
+#include <memory>
+#include <string>
+
+#include "mongo/bson/bsonobj.h"
+#include "mongo/bson/bsonobjbuilder.h"
+#include "mongo/db/catalog/collection.h"
+#include "mongo/db/catalog/index_catalog_entry.h"
+#include "mongo/db/index/index_descriptor.h"
+#include "mongo/db/storage/ephemeral_for_test/ephemeral_for_test_radix_store.h"
+#include "mongo/db/storage/ephemeral_for_test/ephemeral_for_test_recovery_unit.h"
+#include "mongo/db/storage/ephemeral_for_test/ephemeral_for_test_sorted_impl.h"
+#include "mongo/db/storage/index_entry_comparison.h"
+#include "mongo/db/storage/key_string.h"
+#include "mongo/util/bufreader.h"
+#include "mongo/util/hex.h"
+#include "mongo/util/shared_buffer.h"
+#include "mongo/util/str.h"
+
+namespace mongo {
+namespace ephemeral_for_test {
+namespace {
+
+// Helper to interpret index data buffer
+class IndexDataEntry {
+public:
+ IndexDataEntry() : _buffer(nullptr) {}
+ IndexDataEntry(const uint8_t* buffer);
+ IndexDataEntry(const std::string& indexDataEntry);
+
+ static std::string create(RecordId loc, const KeyString::TypeBits& typeBits);
+
+ const uint8_t* buffer() const;
+ size_t size() const; // returns buffer size
+ RecordId loc() const;
+ KeyString::TypeBits typeBits() const;
+
+private:
+ const uint8_t* _buffer;
+};
+
+// Forward iterator for IndexDataEntry with contigous memory layout
+class IndexDataEntryIterator : public boost::iterator_facade<IndexDataEntryIterator,
+ IndexDataEntry const,
+ boost::forward_traversal_tag> {
+public:
+ IndexDataEntryIterator() = default;
+ IndexDataEntryIterator(const uint8_t* entryData);
+
+private:
+ friend class boost::iterator_core_access;
+
+ void increment();
+ bool equal(IndexDataEntryIterator const& other) const;
+ const IndexDataEntry& dereference() const;
+
+ IndexDataEntry _entry;
+};
+
+// Helper to interpret index data buffer for unique index
+class UniqueIndexData {
+public:
+ UniqueIndexData() : _size(0), _begin(nullptr), _end(nullptr) {}
+ UniqueIndexData(const std::string& indexData) {
+ std::memcpy(&_size, indexData.data(), sizeof(uint64_t));
+ _begin = reinterpret_cast<const uint8_t*>(indexData.data() + sizeof(uint64_t));
+ _end = reinterpret_cast<const uint8_t*>(indexData.data() + indexData.size());
+ }
+
+ using const_iterator = IndexDataEntryIterator;
+
+ size_t size() const {
+ return _size;
+ }
+ bool empty() const {
+ return _size == 0;
+ }
+ const_iterator begin() const {
+ return IndexDataEntryIterator(_begin);
+ }
+ const_iterator end() const {
+ return IndexDataEntryIterator(_end);
+ }
+ const_iterator lower_bound(RecordId loc) const;
+ const_iterator upper_bound(RecordId loc) const;
+
+ // Creates a new UniqueIndexData buffer containing an additional item. Returns boost::none if
+ // entry already exists.
+ boost::optional<std::string> add(RecordId loc, const KeyString::TypeBits& typeBits);
+
+ // Creates a new UniqueIndexData buffer with item with RecordId removed. Returns boost::none if
+ // entry did not exist.
+ boost::optional<std::string> remove(RecordId loc);
+
+private:
+ size_t _memoryUsage() const;
+
+ size_t _size;
+ const uint8_t* _begin;
+ const uint8_t* _end;
+};
+
+IndexDataEntry::IndexDataEntry(const uint8_t* buffer) : _buffer(buffer) {}
+IndexDataEntry::IndexDataEntry(const std::string& indexDataEntry)
+ : _buffer(reinterpret_cast<const uint8_t*>(indexDataEntry.data())) {}
+
+std::string IndexDataEntry::create(RecordId loc, const KeyString::TypeBits& typeBits) {
+ uint64_t repr = loc.repr();
+ uint64_t typebitsSize = typeBits.getSize();
+ std::string output(sizeof(loc) + sizeof(typebitsSize) + typebitsSize, '\0');
+
+ // RecordId
+ std::memcpy(output.data(), &repr, sizeof(repr));
+
+ // TypeBits size
+ std::memcpy(output.data() + sizeof(repr), &typebitsSize, sizeof(typebitsSize));
+
+ // TypeBits data
+ std::memcpy(
+ output.data() + sizeof(repr) + sizeof(typebitsSize), typeBits.getBuffer(), typebitsSize);
+
+ return output;
+}
+
+const uint8_t* IndexDataEntry::buffer() const {
+ return _buffer;
+}
+
+size_t IndexDataEntry::size() const {
+ uint64_t typeBitsSize;
+ std::memcpy(&typeBitsSize, _buffer + sizeof(uint64_t), sizeof(uint64_t));
+
+ // RecordId + TypeBits size + TypeBits buffer
+ return sizeof(uint64_t) * 2 + typeBitsSize;
+}
+
+RecordId IndexDataEntry::loc() const {
+ uint64_t repr;
+ std::memcpy(&repr, _buffer, sizeof(uint64_t));
+ return RecordId(repr);
+}
+
+KeyString::TypeBits IndexDataEntry::typeBits() const {
+ uint64_t size;
+ std::memcpy(&size, _buffer + sizeof(uint64_t), sizeof(uint64_t));
+
+ BufReader reader(_buffer + sizeof(uint64_t) * 2, size);
+ return KeyString::TypeBits::fromBuffer(KeyString::Version::kLatestVersion, &reader);
+}
+
+IndexDataEntryIterator::IndexDataEntryIterator(const uint8_t* entryData)
+ : _entry(IndexDataEntry(entryData)) {}
+void IndexDataEntryIterator::increment() {
+ _entry = IndexDataEntry(_entry.buffer() + _entry.size());
+}
+bool IndexDataEntryIterator::equal(IndexDataEntryIterator const& other) const {
+ return _entry.buffer() == other._entry.buffer();
+}
+
+const IndexDataEntry& IndexDataEntryIterator::dereference() const {
+ return _entry;
+}
+
+UniqueIndexData::const_iterator UniqueIndexData::lower_bound(RecordId loc) const {
+ // Linear search to the first item not less than loc
+ return std::find_if_not(
+ begin(), end(), [loc](const IndexDataEntry& entry) { return entry.loc() < loc; });
+}
+UniqueIndexData::const_iterator UniqueIndexData::upper_bound(RecordId loc) const {
+ // Linear search to the first item larger than loc
+ auto lb = lower_bound(loc);
+ return std::find_if(
+ lb, end(), [loc](const IndexDataEntry& entry) { return loc < entry.loc(); });
+}
+
+size_t UniqueIndexData::_memoryUsage() const {
+ return sizeof(_size) + _end - _begin;
+}
+
+boost::optional<std::string> UniqueIndexData::add(RecordId loc,
+ const KeyString::TypeBits& typeBits) {
+ // If entry already exists then nothing to do
+ auto it = lower_bound(loc);
+ if (it != end() && it->loc() == loc)
+ return boost::none;
+
+ auto itBuffer = it->buffer();
+ std::string entry = IndexDataEntry::create(loc, typeBits);
+
+ // Allocate string that fit the new entry
+ std::string output(_memoryUsage() + entry.size(), '\0');
+ auto pos = output.data();
+
+ // Write number of entries
+ uint64_t num = size() + 1;
+ std::memcpy(pos, &num, sizeof(num));
+ pos += sizeof(num);
+
+ // Write old entries smaller than the new one
+ if (auto bytes = itBuffer - _begin) {
+ std::memcpy(pos, _begin, bytes);
+ pos += bytes;
+ }
+
+ // Write new entry
+ std::memcpy(pos, entry.data(), entry.size());
+ pos += entry.size();
+
+ // Write old entries larger than the new one
+ if (auto bytes = _end - itBuffer) {
+ std::memcpy(pos, itBuffer, bytes);
+ }
+
+ return output;
+}
+boost::optional<std::string> UniqueIndexData::remove(RecordId loc) {
+ // If entry doesn't exist then nothing to do
+ auto it = lower_bound(loc);
+ if (it == end() || it->loc() != loc)
+ return boost::none;
+
+ // Allocate string with approrpriate amount of space
+ std::string output(_memoryUsage() - it->size(), '\0');
+ auto pos = output.data();
+
+ // Write number of entries
+ uint64_t num = size() - 1;
+ std::memcpy(pos, &num, sizeof(num));
+ pos += sizeof(num);
+
+ // Write entries before entry to remove
+ std::memcpy(pos, _begin, it->buffer() - _begin);
+ pos += it->buffer() - _begin;
+
+ // Skip entry to remove and write remaining entries
+ ++it;
+ std::memcpy(pos, it->buffer(), _end - it->buffer());
+ return output;
+}
+
+const Ordering allAscending = Ordering::make(BSONObj());
+
+void prefixKeyStringWithoutLoc(KeyString::Builder* keyString, const std::string& prefixToUse) {
+ BSONObjBuilder b;
+ b.append("", prefixToUse); // prefix
+ b.append("", StringData(keyString->getBuffer(), keyString->getSize())); // key
+
+ keyString->resetToKey(b.obj(), allAscending);
+}
+
+void prefixKeyStringWithLoc(KeyString::Builder* keyString,
+ RecordId loc,
+ const std::string& prefixToUse) {
+ BSONObjBuilder b;
+ b.append("", prefixToUse); // prefix
+ b.append("", StringData(keyString->getBuffer(), keyString->getSize())); // key
+
+ keyString->resetToKey(b.obj(), allAscending, loc);
+}
+
+std::string createRadixKeyWithoutLocFromObj(const BSONObj& key,
+ const std::string& prefixToUse,
+ Ordering order) {
+ KeyString::Version version = KeyString::Version::kLatestVersion;
+ KeyString::Builder ks(version, BSONObj::stripFieldNames(key), order);
+
+ prefixKeyStringWithoutLoc(&ks, prefixToUse);
+ return std::string(ks.getBuffer(), ks.getSize());
+}
+
+std::string createRadixKeyWithoutLocFromKS(const KeyString::Value& keyString,
+ const std::string& prefixToUse) {
+ KeyString::Builder ks(KeyString::Version::kLatestVersion);
+ ks.resetFromBuffer(
+ keyString.getBuffer(),
+ KeyString::sizeWithoutRecordIdAtEnd(keyString.getBuffer(), keyString.getSize()));
+ prefixKeyStringWithoutLoc(&ks, prefixToUse);
+ return std::string(ks.getBuffer(), ks.getSize());
+}
+
+std::string createRadixKeyWithoutLocFromKSWithoutRecordId(const KeyString::Value& keyString,
+ const std::string& prefixToUse) {
+ KeyString::Builder ks(KeyString::Version::kLatestVersion);
+ ks.resetFromBuffer(keyString.getBuffer(), keyString.getSize());
+ prefixKeyStringWithoutLoc(&ks, prefixToUse);
+ return std::string(ks.getBuffer(), ks.getSize());
+}
+
+std::string createRadixKeyWithLocFromObj(const BSONObj& key,
+ RecordId loc,
+ const std::string& prefixToUse,
+ Ordering order) {
+ KeyString::Version version = KeyString::Version::kLatestVersion;
+ KeyString::Builder ks(version, BSONObj::stripFieldNames(key), order);
+
+ prefixKeyStringWithLoc(&ks, loc, prefixToUse);
+ return std::string(ks.getBuffer(), ks.getSize());
+}
+
+std::string createRadixKeyWithLocFromKS(const KeyString::Value& keyString,
+ RecordId loc,
+ const std::string& prefixToUse) {
+ KeyString::Builder ks(KeyString::Version::kLatestVersion);
+ ks.resetFromBuffer(
+ keyString.getBuffer(),
+ KeyString::sizeWithoutRecordIdAtEnd(keyString.getBuffer(), keyString.getSize()));
+ prefixKeyStringWithLoc(&ks, loc, prefixToUse);
+ return std::string(ks.getBuffer(), ks.getSize());
+}
+
+std::string createRadixKeyWithLocFromKSWithoutRecordId(const KeyString::Value& keyString,
+ RecordId loc,
+ const std::string& prefixToUse) {
+ KeyString::Builder ks(KeyString::Version::kLatestVersion);
+ ks.resetFromBuffer(keyString.getBuffer(), keyString.getSize());
+ prefixKeyStringWithLoc(&ks, loc, prefixToUse);
+ return std::string(ks.getBuffer(), ks.getSize());
+}
+
+BSONObj createObjFromRadixKey(const std::string& radixKey,
+ const KeyString::TypeBits& typeBits,
+ const Ordering& order) {
+ KeyString::Version version = KeyString::Version::kLatestVersion;
+ KeyString::TypeBits tbOuter = KeyString::TypeBits(version);
+ BSONObj bsonObj =
+ KeyString::toBsonSafe(radixKey.data(), radixKey.size(), allAscending, tbOuter);
+
+ SharedBuffer sb;
+ auto it = BSONObjIterator(bsonObj);
+ ++it; // We want the second part
+ KeyString::Builder ks(version);
+ ks.resetFromBuffer((*it).valuestr(), (*it).valuestrsize());
+
+ return KeyString::toBsonSafe(ks.getBuffer(), ks.getSize(), order, typeBits);
+}
+
+IndexKeyEntry createIndexKeyEntryFromRadixKey(const std::string& radixKey,
+ RecordId loc,
+ const KeyString::TypeBits& typeBits,
+ const Ordering order) {
+ return IndexKeyEntry(createObjFromRadixKey(radixKey, typeBits, order), loc);
+}
+
+IndexKeyEntry createIndexKeyEntryFromRadixKey(const std::string& radixKey,
+ const std::string& indexDataEntry,
+ const Ordering order) {
+ IndexDataEntry data(indexDataEntry);
+ return IndexKeyEntry(createObjFromRadixKey(radixKey, data.typeBits(), order), data.loc());
+}
+
+boost::optional<KeyStringEntry> createKeyStringEntryFromRadixKey(
+ const std::string& radixKey,
+ RecordId loc,
+ const KeyString::TypeBits& typeBits,
+ const Ordering& order) {
+ auto key = createObjFromRadixKey(radixKey, typeBits, order);
+ KeyString::Builder ksFinal(KeyString::Version::kLatestVersion, key, order);
+ ksFinal.appendRecordId(loc);
+ return KeyStringEntry(ksFinal.getValueCopy(), loc);
+}
+
+boost::optional<KeyStringEntry> createKeyStringEntryFromRadixKey(const std::string& radixKey,
+ const std::string& indexDataEntry,
+ const Ordering& order) {
+ IndexDataEntry data(indexDataEntry);
+ RecordId loc = data.loc();
+ auto key = createObjFromRadixKey(radixKey, data.typeBits(), order);
+ KeyString::Builder ksFinal(KeyString::Version::kLatestVersion, key, order);
+ ksFinal.appendRecordId(loc);
+ return KeyStringEntry(ksFinal.getValueCopy(), loc);
+}
+
+/*
+ * This is the base cursor class required by the sorted data interface.
+ * Using CRTP (static inheritance) to reuse shared implementation for cursors over unique and
+ * standard indexes
+ */
+template <class CursorImpl>
+class CursorBase : public ::mongo::SortedDataInterface::Cursor {
+public:
+ // All the following public functions just implement the interface.
+ CursorBase(OperationContext* opCtx,
+ bool isForward,
+ // This is the ident.
+ std::string _prefix,
+ // This is a string immediately after the ident and before other idents.
+ std::string _identEnd,
+ StringStore* workingCopy,
+ Ordering order,
+ std::string prefixBSON,
+ std::string KSForIdentEnd);
+ virtual void setEndPosition(const BSONObj& key, bool inclusive) override;
+ virtual boost::optional<IndexKeyEntry> seek(const KeyString::Value& keyString,
+ RequestedInfo parts = kKeyAndLoc) override;
+ virtual boost::optional<KeyStringEntry> seekForKeyString(
+ const KeyString::Value& keyStringValue) override;
+ virtual boost::optional<KeyStringEntry> seekExactForKeyString(
+ const KeyString::Value& keyStringValue) override;
+ virtual boost::optional<IndexKeyEntry> seekExact(const KeyString::Value& keyStringValue,
+ RequestedInfo) override;
+ virtual void save() override;
+ virtual void restore() override;
+ virtual void detachFromOperationContext() override;
+ virtual void reattachToOperationContext(OperationContext* opCtx) override;
+
+private:
+ // CRTP Interface
+ std::string createRadixKeyFromObj(const BSONObj& key,
+ RecordId loc,
+ const std::string& prefixToUse,
+ Ordering order) {
+ return static_cast<CursorImpl*>(this)->createRadixKeyFromObj(key, loc, prefixToUse, order);
+ }
+ std::string createRadixKeyFromKSWithoutRecordId(const KeyString::Value& keyString,
+ RecordId loc,
+ const std::string& prefixToUse) {
+ return static_cast<CursorImpl*>(this)->createRadixKeyFromKSWithoutRecordId(
+ keyString, loc, prefixToUse);
+ }
+ boost::optional<KeyStringEntry> finishSeekAfterProcessing() {
+ return static_cast<CursorImpl*>(this)->finishSeekAfterProcessing();
+ }
+ bool advanceNextInternal() {
+ return static_cast<CursorImpl*>(this)->advanceNextInternal();
+ }
+ void finishAdvanceNext() {
+ static_cast<CursorImpl*>(this)->finishAdvanceNext();
+ }
+ bool checkCursorValid() {
+ return static_cast<CursorImpl*>(this)->checkCursorValid();
+ }
+ void saveForward() {
+ return static_cast<CursorImpl*>(this)->saveForward();
+ }
+ void saveReverse() {
+ return static_cast<CursorImpl*>(this)->saveReverse();
+ }
+ void restoreForward() {
+ return static_cast<CursorImpl*>(this)->restoreForward();
+ }
+ void restoreReverse() {
+ return static_cast<CursorImpl*>(this)->restoreReverse();
+ }
+
+protected:
+ bool advanceNext();
+ // This is a helper function to check if the cursor was explicitly set by the user or not.
+ bool endPosSet();
+ // This is a helper function for seek.
+ boost::optional<IndexKeyEntry> seekAfterProcessing(BSONObj finalKey);
+ boost::optional<KeyStringEntry> seekAfterProcessing(const KeyString::Value& keyString);
+ OperationContext* _opCtx;
+ // This is the "working copy" of the master "branch" in the git analogy.
+ StringStore* _workingCopy;
+ // These store the end positions.
+ boost::optional<StringStore::const_iterator> _endPos;
+ boost::optional<StringStore::const_reverse_iterator> _endPosReverse;
+ // This means if the cursor is a forward or reverse cursor.
+ bool _forward;
+ // This means whether the cursor has reached the last EOF (with regard to this index).
+ bool _atEOF;
+ // This means whether or not the last move was restore.
+ bool _lastMoveWasRestore;
+ // This is the keystring for the saved location.
+ std::string _saveKey;
+ RecordId _saveLoc;
+ // These are the same as before.
+ std::string _prefix;
+ std::string _identEnd;
+ // These two store the const_iterator, which is the data structure for cursors. The one we
+ // use depends on _forward.
+ StringStore::const_iterator _forwardIt;
+ StringStore::const_reverse_iterator _reverseIt;
+ // This is the ordering for the key's values for multi-field keys.
+ Ordering _order;
+ // This stores whether or not the end position is inclusive for restore.
+ bool _endPosIncl;
+ // This stores the key for the end position.
+ boost::optional<BSONObj> _endPosKey;
+ // The next two are the same as above.
+ std::string _KSForIdentStart;
+ std::string _KSForIdentEnd;
+};
+
+// Cursor
+template <class CursorImpl>
+CursorBase<CursorImpl>::CursorBase(OperationContext* opCtx,
+ bool isForward,
+ std::string _prefix,
+ std::string _identEnd,
+ StringStore* workingCopy,
+ Ordering order,
+ std::string _KSForIdentStart,
+ std::string identEndBSON)
+ : _opCtx(opCtx),
+ _workingCopy(workingCopy),
+ _endPos(boost::none),
+ _endPosReverse(boost::none),
+ _forward(isForward),
+ _atEOF(false),
+ _lastMoveWasRestore(false),
+ _prefix(_prefix),
+ _identEnd(_identEnd),
+ _forwardIt(workingCopy->begin()),
+ _reverseIt(workingCopy->rbegin()),
+ _order(order),
+ _endPosIncl(false),
+ _KSForIdentStart(_KSForIdentStart),
+ _KSForIdentEnd(identEndBSON) {}
+
+template <class CursorImpl>
+bool CursorBase<CursorImpl>::advanceNext() {
+ if (!_atEOF) {
+ // If the last move was restore, then we don't need to advance the cursor, since the user
+ // never got the value the cursor was pointing to in the first place. However,
+ // _lastMoveWasRestore will go through extra logic on a unique index, since unique indexes
+ // are not allowed to return the same key twice.
+ if (_lastMoveWasRestore) {
+ _lastMoveWasRestore = false;
+ } else {
+ if (advanceNextInternal())
+ return true;
+
+ // We basically just check to make sure the cursor is in the ident.
+ if (_forward && checkCursorValid()) {
+ ++_forwardIt;
+ } else if (!_forward && checkCursorValid()) {
+ ++_reverseIt;
+ }
+ // We check here to make sure that we are on the correct side of the end position, and
+ // that the cursor is still in the ident after advancing.
+ if (!checkCursorValid()) {
+ _atEOF = true;
+ return false;
+ }
+ }
+ } else {
+ _lastMoveWasRestore = false;
+ return false;
+ }
+
+ finishAdvanceNext();
+
+ return true;
+}
+
+// This function checks whether or not the cursor end position was set by the user or not.
+template <class CursorImpl>
+bool CursorBase<CursorImpl>::endPosSet() {
+ return (_forward && _endPos != boost::none) || (!_forward && _endPosReverse != boost::none);
+}
+
+template <class CursorImpl>
+void CursorBase<CursorImpl>::setEndPosition(const BSONObj& key, bool inclusive) {
+ StringStore* workingCopy(RecoveryUnit::get(_opCtx)->getHead());
+ if (key.isEmpty()) {
+ _endPos = boost::none;
+ _endPosReverse = boost::none;
+ return;
+ }
+ _endPosIncl = inclusive;
+ _endPosKey = key;
+ StringStore::const_iterator it;
+ // If forward and inclusive or reverse and not inclusive, then we use the last element in this
+ // ident. Otherwise, we use the first as our bound.
+ if (_forward == inclusive)
+ it = workingCopy->upper_bound(createRadixKeyFromObj(key, RecordId::max(), _prefix, _order));
+ else
+ it = workingCopy->lower_bound(createRadixKeyFromObj(key, RecordId::min(), _prefix, _order));
+ if (_forward)
+ _endPos = it;
+ else
+ _endPosReverse = StringStore::const_reverse_iterator(it);
+}
+
+template <class CursorImpl>
+boost::optional<IndexKeyEntry> CursorBase<CursorImpl>::seekAfterProcessing(BSONObj finalKey) {
+ std::string workingCopyBound;
+
+ KeyString::Builder ks(KeyString::Version::kLatestVersion, finalKey, _order);
+ auto ksEntry = seekAfterProcessing(ks.getValueCopy());
+
+ const BSONObj bson = KeyString::toBson(ksEntry->keyString.getBuffer(),
+ ksEntry->keyString.getSize(),
+ _order,
+ ksEntry->keyString.getTypeBits());
+ return IndexKeyEntry(bson, ksEntry->loc);
+}
+
+template <class CursorImpl>
+boost::optional<KeyStringEntry> CursorBase<CursorImpl>::seekAfterProcessing(
+ const KeyString::Value& keyStringVal) {
+
+ KeyString::Discriminator discriminator = KeyString::decodeDiscriminator(
+ keyStringVal.getBuffer(), keyStringVal.getSize(), _order, keyStringVal.getTypeBits());
+
+ bool inclusive;
+ switch (discriminator) {
+ case KeyString::Discriminator::kInclusive:
+ inclusive = true;
+ break;
+ case KeyString::Discriminator::kExclusiveBefore:
+ inclusive = _forward;
+ break;
+ case KeyString::Discriminator::kExclusiveAfter:
+ inclusive = !_forward;
+ break;
+ }
+
+ // If the key is empty and it's not inclusive, then no elements satisfy this seek.
+ if (keyStringVal.isEmpty() && !inclusive) {
+ _atEOF = true;
+ return boost::none;
+ }
+
+ StringStore::const_iterator it;
+ // Forward inclusive seek uses lower_bound and exclusive upper_bound. For reverse iterators this
+ // is also reversed.
+ if (_forward == inclusive)
+ it = _workingCopy->lower_bound(
+ createRadixKeyFromKSWithoutRecordId(keyStringVal, RecordId::min(), _prefix));
+ else
+ it = _workingCopy->upper_bound(
+ createRadixKeyFromKSWithoutRecordId(keyStringVal, RecordId::max(), _prefix));
+ if (_forward)
+ _forwardIt = it;
+ else
+ _reverseIt = StringStore::const_reverse_iterator(it);
+
+ // Here, we check to make sure the iterator doesn't fall off the data structure and is
+ // in the ident. We also check to make sure it is on the correct side of the end
+ // position, if it was set.
+ if (!checkCursorValid()) {
+ _atEOF = true;
+ return boost::none;
+ }
+
+ return finishSeekAfterProcessing();
+}
+
+template <class CursorImpl>
+boost::optional<IndexKeyEntry> CursorBase<CursorImpl>::seek(const KeyString::Value& keyString,
+ RequestedInfo parts) {
+ boost::optional<KeyStringEntry> ksValue = seekForKeyString(keyString);
+ if (ksValue) {
+ BSONObj bson = KeyString::toBson(ksValue->keyString.getBuffer(),
+ ksValue->keyString.getSize(),
+ _order,
+ ksValue->keyString.getTypeBits());
+ return IndexKeyEntry(bson, ksValue->loc);
+ }
+ return boost::none;
+}
+
+template <class CursorImpl>
+boost::optional<KeyStringEntry> CursorBase<CursorImpl>::seekForKeyString(
+ const KeyString::Value& keyStringValue) {
+ _lastMoveWasRestore = false;
+ _atEOF = false;
+ return seekAfterProcessing(keyStringValue);
+}
+
+template <class CursorImpl>
+boost::optional<KeyStringEntry> CursorBase<CursorImpl>::seekExactForKeyString(
+ const KeyString::Value& keyStringValue) {
+ dassert(KeyString::decodeDiscriminator(keyStringValue.getBuffer(),
+ keyStringValue.getSize(),
+ _order,
+ keyStringValue.getTypeBits()) ==
+ KeyString::Discriminator::kInclusive);
+ auto ksEntry = seekForKeyString(keyStringValue);
+ if (!ksEntry) {
+ return {};
+ }
+ if (KeyString::compare(ksEntry->keyString.getBuffer(),
+ keyStringValue.getBuffer(),
+ KeyString::sizeWithoutRecordIdAtEnd(ksEntry->keyString.getBuffer(),
+ ksEntry->keyString.getSize()),
+ keyStringValue.getSize()) == 0) {
+ return KeyStringEntry(ksEntry->keyString, ksEntry->loc);
+ }
+ return {};
+}
+
+template <class CursorImpl>
+boost::optional<IndexKeyEntry> CursorBase<CursorImpl>::seekExact(
+ const KeyString::Value& keyStringValue, RequestedInfo parts) {
+ auto ksEntry = seekExactForKeyString(keyStringValue);
+ if (!ksEntry) {
+ return {};
+ }
+
+ BSONObj bson;
+ if (parts & SortedDataInterface::Cursor::kWantKey) {
+ bson = KeyString::toBson(ksEntry->keyString.getBuffer(),
+ ksEntry->keyString.getSize(),
+ _order,
+ ksEntry->keyString.getTypeBits());
+ }
+ return IndexKeyEntry(std::move(bson), ksEntry->loc);
+}
+
+template <class CursorImpl>
+void CursorBase<CursorImpl>::save() {
+ _atEOF = false;
+ if (_lastMoveWasRestore) {
+ return;
+ } else if (_forward && checkCursorValid()) {
+ _saveKey = _forwardIt->first;
+ saveForward();
+ } else if (!_forward && checkCursorValid()) { // reverse
+ _saveKey = _reverseIt->first;
+ saveReverse();
+ } else {
+ _saveKey = "";
+ _saveLoc = RecordId();
+ }
+}
+
+template <class CursorImpl>
+void CursorBase<CursorImpl>::restore() {
+ StringStore* workingCopy(RecoveryUnit::get(_opCtx)->getHead());
+
+ this->_workingCopy = workingCopy;
+
+ // Here, we have to reset the end position if one was set earlier.
+ if (endPosSet()) {
+ setEndPosition(*_endPosKey, _endPosIncl);
+ }
+
+ // We reset the cursor, and make sure it's within the end position bounds. It doesn't matter if
+ // the cursor is not in the ident right now, since that will be taken care of upon the call to
+ // next().
+ if (_forward) {
+ if (_saveKey.length() == 0) {
+ _forwardIt = workingCopy->end();
+ } else {
+ _forwardIt = workingCopy->lower_bound(_saveKey);
+ }
+ restoreForward();
+ } else {
+ // Now we are dealing with reverse cursors, and use similar logic.
+ if (_saveKey.length() == 0) {
+ _reverseIt = workingCopy->rend();
+ } else {
+ _reverseIt = StringStore::const_reverse_iterator(workingCopy->upper_bound(_saveKey));
+ }
+ restoreReverse();
+ }
+}
+
+template <class CursorImpl>
+void CursorBase<CursorImpl>::detachFromOperationContext() {
+ _opCtx = nullptr;
+}
+
+template <class CursorImpl>
+void CursorBase<CursorImpl>::reattachToOperationContext(OperationContext* opCtx) {
+ this->_opCtx = opCtx;
+}
+
+/*
+ * This is the cursor class required by the sorted data interface for unique indexes.
+ */
+class CursorUnique final : public CursorBase<CursorUnique> {
+public:
+ using CursorBase::CursorBase;
+
+ virtual boost::optional<IndexKeyEntry> next(RequestedInfo parts = kKeyAndLoc) override;
+ virtual boost::optional<KeyStringEntry> nextKeyString() override;
+
+private:
+ // Implementations of CursorBase interface
+ friend class CursorBase;
+
+ bool advanceNextInternal();
+ void finishAdvanceNext();
+ std::string createRadixKeyFromObj(const BSONObj& key,
+ RecordId loc,
+ const std::string& prefixToUse,
+ Ordering order) {
+ return createRadixKeyWithoutLocFromObj(key, prefixToUse, order);
+ }
+ std::string createRadixKeyFromKSWithoutRecordId(const KeyString::Value& keyString,
+ RecordId loc,
+ const std::string& prefixToUse) {
+ return createRadixKeyWithoutLocFromKSWithoutRecordId(keyString, prefixToUse);
+ }
+ boost::optional<KeyStringEntry> finishSeekAfterProcessing();
+
+ void saveForward();
+ void saveReverse();
+ void restoreForward();
+ void restoreReverse();
+
+ // This is a helper function to check if the cursor is valid or not.
+ bool checkCursorValid();
+ // Helper function to set index data iterators to reverse position, we cannot use reverse
+ // iterators because we only have forward iterator support over this data
+ void initReverseDataIterators();
+ // Unpacked data from current position in the radix tree. Needed to iterate over indexes
+ // containing duplicates
+ UniqueIndexData _indexData;
+ UniqueIndexData::const_iterator _indexDataIt;
+ UniqueIndexData::const_iterator _indexDataEnd;
+ size_t _reversePos;
+};
+
+bool CursorUnique::advanceNextInternal() {
+ // Iterate over duplicates before moving to the next item in the radix tree
+ if (!_indexData.empty()) {
+ if (_forward) {
+ if (++_indexDataIt != _indexDataEnd)
+ return true;
+ } else {
+ if (++_reversePos < _indexData.size()) {
+ initReverseDataIterators();
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void CursorUnique::finishAdvanceNext() {
+ // We have moved to a new position in the tree, initialize index data for iterating over
+ // duplicates
+ if (_forward) {
+ _indexData = UniqueIndexData(_forwardIt->second);
+ _indexDataIt = _indexData.begin();
+ _indexDataEnd = _indexData.end();
+ } else {
+ _indexData = UniqueIndexData(_reverseIt->second);
+ _reversePos = 0;
+ initReverseDataIterators();
+ }
+}
+
+// This function checks whether or not a cursor is valid. In particular, it checks 1) whether the
+// cursor is at end() or rend(), 2) whether the cursor is on the wrong side of the end position
+// if it was set, and 3) whether the cursor is still in the ident.
+bool CursorUnique::checkCursorValid() {
+ if (_forward) {
+ if (_forwardIt == _workingCopy->end()) {
+ return false;
+ }
+ if (endPosSet()) {
+ // The endPos must be in the ident, at most one past the ident, or end. Therefore, the
+ // endPos includes the check for being inside the ident
+ if (_endPosIncl) {
+ if (*_endPos == _workingCopy->end())
+ return true;
+
+ // For unique indexes, we need to check if the cursor moved up a position when it
+ // was restored. This isn't required for non-unique indexes because we store the
+ // RecordId in the KeyString and use a "<" comparison instead of "<=" since we know
+ // that no RecordId will ever reach RecordId::max() so we don't need to check the
+ // equal side of things. This assumption doesn't hold for unique index KeyStrings.
+ std::string endPosKeyString =
+ createRadixKeyFromObj(*_endPosKey, RecordId::min(), _prefix, _order);
+
+ if (_forwardIt->first.compare(endPosKeyString) <= 0)
+ return true;
+ return false;
+ }
+
+ return *_endPos == _workingCopy->end() ||
+ _forwardIt->first.compare((*_endPos)->first) < 0;
+ }
+ return _forwardIt->first.compare(_KSForIdentEnd) <= 0;
+ } else {
+ // This is a reverse cursor
+ if (_reverseIt == _workingCopy->rend()) {
+ return false;
+ }
+ if (endPosSet()) {
+ if (_endPosIncl) {
+ if (*_endPosReverse == _workingCopy->rend())
+ return true;
+
+ std::string endPosKeyString =
+ createRadixKeyFromObj(*_endPosKey, RecordId::min(), _prefix, _order);
+
+ if (_reverseIt->first.compare(endPosKeyString) >= 0)
+ return true;
+ return false;
+ }
+
+ return *_endPosReverse == _workingCopy->rend() ||
+ _reverseIt->first.compare((*_endPosReverse)->first) > 0;
+ }
+ return _reverseIt->first.compare(_KSForIdentStart) >= 0;
+ }
+}
+
+void CursorUnique::initReverseDataIterators() {
+ _indexDataIt = _indexData.begin();
+ _indexDataEnd = _indexData.end();
+ for (size_t i = 1; i < (_indexData.size() - _reversePos); ++i)
+ ++_indexDataIt;
+}
+
+boost::optional<IndexKeyEntry> CursorUnique::next(RequestedInfo parts) {
+ if (!advanceNext()) {
+ return {};
+ }
+
+ if (_forward) {
+ return createIndexKeyEntryFromRadixKey(
+ _forwardIt->first, _indexDataIt->loc(), _indexDataIt->typeBits(), _order);
+ }
+ return createIndexKeyEntryFromRadixKey(
+ _reverseIt->first, _indexDataIt->loc(), _indexDataIt->typeBits(), _order);
+}
+
+boost::optional<KeyStringEntry> CursorUnique::nextKeyString() {
+ if (!advanceNext()) {
+ return {};
+ }
+
+ if (_forward) {
+ return createKeyStringEntryFromRadixKey(
+ _forwardIt->first, _indexDataIt->loc(), _indexDataIt->typeBits(), _order);
+ }
+ return createKeyStringEntryFromRadixKey(
+ _reverseIt->first, _indexDataIt->loc(), _indexDataIt->typeBits(), _order);
+}
+
+boost::optional<KeyStringEntry> CursorUnique::finishSeekAfterProcessing() {
+ // We have seeked to an entry in the tree. Now unpack the data and initialize iterators to point
+ // to the first entry if this index contains duplicates
+ if (_forward) {
+ _indexData = UniqueIndexData(_forwardIt->second);
+ _indexDataIt = _indexData.begin();
+ _indexDataEnd = _indexData.end();
+ return createKeyStringEntryFromRadixKey(
+ _forwardIt->first, _indexDataIt->loc(), _indexDataIt->typeBits(), _order);
+ } else {
+ _indexData = UniqueIndexData(_reverseIt->second);
+ _reversePos = 0;
+ initReverseDataIterators();
+ return createKeyStringEntryFromRadixKey(
+ _reverseIt->first, _indexDataIt->loc(), _indexDataIt->typeBits(), _order);
+ }
+}
+
+void CursorUnique::saveForward() {
+ if (!_indexData.empty()) {
+ _saveLoc = _indexDataIt->loc();
+ }
+}
+
+void CursorUnique::saveReverse() {
+ if (!_indexData.empty()) {
+ _saveLoc = _indexDataIt->loc();
+ }
+}
+
+void CursorUnique::restoreForward() {
+ _lastMoveWasRestore = true;
+ if (_saveLoc != RecordId() && _forwardIt != _workingCopy->end() &&
+ _forwardIt->first == _saveKey) {
+ _indexData = UniqueIndexData(_forwardIt->second);
+ _indexDataIt = _indexData.lower_bound(_saveLoc);
+ _indexDataEnd = _indexData.end();
+ if (_indexDataIt == _indexDataEnd) {
+ // We reached the end of the index data, so we need to go to the next item in the
+ // radix tree to be positioned on a valid item
+ ++_forwardIt;
+ if (checkCursorValid()) {
+ _indexData = UniqueIndexData(_forwardIt->second);
+ _indexDataIt = _indexData.begin();
+ _indexDataEnd = _indexData.end();
+ }
+ } else {
+ // Unique indexes disregard difference in location and forces the cursor to advance
+ // to guarantee that we never return the same key twice
+ _lastMoveWasRestore = false;
+ }
+ }
+ if (!checkCursorValid()) {
+ _atEOF = true;
+ }
+}
+
+void CursorUnique::restoreReverse() {
+ _lastMoveWasRestore = true;
+ if (_saveLoc != RecordId() && _reverseIt != _workingCopy->rend() &&
+ _reverseIt->first == _saveKey) {
+ _indexData = UniqueIndexData(_reverseIt->second);
+ _indexDataIt = _indexData.upper_bound(_saveLoc);
+ _indexDataEnd = _indexData.end();
+ if (_indexDataIt == _indexDataEnd) {
+ ++_reverseIt;
+ if (checkCursorValid()) {
+ _indexData = UniqueIndexData(_reverseIt->second);
+ _reversePos = 0;
+ initReverseDataIterators();
+ }
+ } else {
+ _reversePos = _indexData.size() - std::distance(_indexData.begin(), _indexDataIt) - 1;
+ _lastMoveWasRestore = false;
+ }
+ }
+ if (!checkCursorValid()) {
+ _atEOF = true;
+ }
+}
+
+/*
+ * This is the cursor class required by the sorted data interface for standard (non-unique) indexes.
+ */
+class CursorStandard final : public CursorBase<CursorStandard> {
+public:
+ using CursorBase::CursorBase;
+
+ virtual boost::optional<IndexKeyEntry> next(RequestedInfo parts = kKeyAndLoc) override;
+ virtual boost::optional<KeyStringEntry> nextKeyString() override;
+
+protected:
+ // Implementations of CursorBase interface
+ friend class CursorBase;
+
+ bool advanceNextInternal() {
+ return false;
+ }
+ void finishAdvanceNext() {}
+ std::string createRadixKeyFromObj(const BSONObj& key,
+ RecordId loc,
+ const std::string& prefixToUse,
+ Ordering order) {
+ return createRadixKeyWithLocFromObj(key, loc, prefixToUse, order);
+ }
+ std::string createRadixKeyFromKSWithoutRecordId(const KeyString::Value& keyString,
+ RecordId loc,
+ const std::string& prefixToUse) {
+ return createRadixKeyWithLocFromKSWithoutRecordId(keyString, loc, prefixToUse);
+ }
+ boost::optional<KeyStringEntry> finishSeekAfterProcessing();
+ void saveForward() {}
+ void saveReverse() {}
+ void restoreForward();
+ void restoreReverse();
+
+private:
+ // This is a helper function to check if the cursor is valid or not.
+ bool checkCursorValid();
+};
+
+// This function checks whether or not a cursor is valid. In particular, it checks 1) whether the
+// cursor is at end() or rend(), 2) whether the cursor is on the wrong side of the end position
+// if it was set, and 3) whether the cursor is still in the ident.
+bool CursorStandard::checkCursorValid() {
+ if (_forward) {
+ if (_forwardIt == _workingCopy->end()) {
+ return false;
+ }
+ if (endPosSet()) {
+ return *_endPos == _workingCopy->end() ||
+ _forwardIt->first.compare((*_endPos)->first) < 0;
+ }
+ return _forwardIt->first.compare(_KSForIdentEnd) <= 0;
+ } else {
+ // This is a reverse cursor
+ if (_reverseIt == _workingCopy->rend()) {
+ return false;
+ }
+ if (endPosSet()) {
+ return *_endPosReverse == _workingCopy->rend() ||
+ _reverseIt->first.compare((*_endPosReverse)->first) > 0;
+ }
+ return _reverseIt->first.compare(_KSForIdentStart) >= 0;
+ }
+}
+
+
+boost::optional<IndexKeyEntry> CursorStandard::next(RequestedInfo parts) {
+ if (!advanceNext()) {
+ return {};
+ }
+
+ if (_forward) {
+ return createIndexKeyEntryFromRadixKey(_forwardIt->first, _forwardIt->second, _order);
+ }
+ return createIndexKeyEntryFromRadixKey(_reverseIt->first, _reverseIt->second, _order);
+}
+
+boost::optional<KeyStringEntry> CursorStandard::nextKeyString() {
+ if (!advanceNext()) {
+ return {};
+ }
+
+ if (_forward) {
+ return createKeyStringEntryFromRadixKey(_forwardIt->first, _forwardIt->second, _order);
+ }
+ return createKeyStringEntryFromRadixKey(_reverseIt->first, _reverseIt->second, _order);
+}
+
+boost::optional<KeyStringEntry> CursorStandard::finishSeekAfterProcessing() {
+ // We have seeked to an entry in the tree.
+ if (_forward) {
+ return createKeyStringEntryFromRadixKey(_forwardIt->first, _forwardIt->second, _order);
+ } else {
+ return createKeyStringEntryFromRadixKey(_reverseIt->first, _reverseIt->second, _order);
+ }
+}
+
+void CursorStandard::restoreForward() {
+ if (!checkCursorValid()) {
+ _atEOF = true;
+ _lastMoveWasRestore = true;
+ return;
+ }
+ _lastMoveWasRestore = (_forwardIt->first.compare(_saveKey) != 0);
+}
+void CursorStandard::restoreReverse() {
+ if (!checkCursorValid()) {
+ _atEOF = true;
+ _lastMoveWasRestore = true;
+ return;
+ }
+ _lastMoveWasRestore = (_reverseIt->first.compare(_saveKey) != 0);
+}
+
+} // namespace
+
+SortedDataBuilderBase::SortedDataBuilderBase(OperationContext* opCtx,
+ bool dupsAllowed,
+ Ordering order,
+ const std::string& prefix,
+ const std::string& identEnd,
+ const IndexDescriptor* desc,
+ const std::string& indexName,
+ const BSONObj& keyPattern,
+ const BSONObj& collation)
+ : _opCtx(opCtx),
+ _dupsAllowed(dupsAllowed),
+ _order(order),
+ _prefix(prefix),
+ _identEnd(identEnd),
+ _desc(desc),
+ _indexName(indexName),
+ _keyPattern(keyPattern),
+ _collation(collation) {}
+
+void SortedDataBuilderBase::commit(bool mayInterrupt) {
+ WriteUnitOfWork wunit(_opCtx);
+ wunit.commit();
+}
+
+Status SortedDataBuilderUnique::addKey(const KeyString::Value& keyString) {
+ dassert(KeyString::decodeRecordIdAtEnd(keyString.getBuffer(), keyString.getSize()).isValid());
+ StringStore* workingCopy(RecoveryUnit::get(_opCtx)->getHead());
+ RecordId loc = KeyString::decodeRecordIdAtEnd(keyString.getBuffer(), keyString.getSize());
+
+ std::string key = createRadixKeyWithoutLocFromKS(keyString, _prefix);
+ auto it = workingCopy->find(key);
+ if (it != workingCopy->end()) {
+ if (!_dupsAllowed) {
+ // There was an attempt to create an index entry with a different RecordId while dups
+ // were not allowed.
+ auto obj = KeyString::toBson(keyString, _order);
+ return buildDupKeyErrorStatus(_opCtx, keyString, _order, _desc);
+ }
+
+ UniqueIndexData data(it->second);
+ // Bulk builder add keys in ascending order so we should insert at the end
+ auto added = data.add(loc, keyString.getTypeBits());
+ if (!added) {
+ // Already indexed
+ return Status::OK();
+ }
+
+ workingCopy->update({std::move(key), *added});
+ } else {
+ UniqueIndexData data;
+ workingCopy->insert({std::move(key), *data.add(loc, keyString.getTypeBits())});
+ }
+
+ RecoveryUnit::get(_opCtx)->makeDirty();
+ return Status::OK();
+}
+
+// We append \1 to all idents we get, and therefore the KeyString with ident + \0 will only be
+// before elements in this ident, and the KeyString with ident + \2 will only be after elements in
+// this ident.
+SortedDataInterfaceBase::SortedDataInterfaceBase(OperationContext* opCtx,
+ StringData ident,
+ const IndexDescriptor* desc)
+ : ::mongo::SortedDataInterface(KeyString::Version::V1, Ordering::make(desc->keyPattern())),
+ // All entries in this ident will have a prefix of ident + \1.
+ _prefix(ident.toString().append(1, '\1')),
+ // Therefore, the string ident + \2 will be greater than all elements in this ident.
+ _identEnd(ident.toString().append(1, '\2')),
+ _desc(desc),
+ _indexName(desc->indexName()),
+ _keyPattern(desc->keyPattern()),
+ _collation(desc->collation()),
+ _isPartial(desc->isPartial()) {}
+
+SortedDataInterfaceBase::SortedDataInterfaceBase(const Ordering& ordering, StringData ident)
+ : ::mongo::SortedDataInterface(KeyString::Version::V1, ordering),
+ _prefix(ident.toString().append(1, '\1')),
+ _identEnd(ident.toString().append(1, '\2')),
+ _isPartial(false) {}
+
+SortedDataBuilderInterface* SortedDataInterfaceUnique::getBulkBuilder(OperationContext* opCtx,
+ bool dupsAllowed) {
+ return new SortedDataBuilderUnique(opCtx,
+ dupsAllowed,
+ _ordering,
+ _prefix,
+ _identEnd,
+ _desc,
+ _indexName,
+ _keyPattern,
+ _collation);
+}
+
+// We append \1 to all idents we get, and therefore the KeyString with ident + \0 will only be
+// before elements in this ident, and the KeyString with ident + \2 will only be after elements in
+// this ident.
+SortedDataInterfaceUnique::SortedDataInterfaceUnique(OperationContext* opCtx,
+ StringData ident,
+ const IndexDescriptor* desc)
+ : SortedDataInterfaceBase(opCtx, ident, desc) {
+ // This is the string representation of the KeyString before elements in this ident, which is
+ // ident + \0. This is before all elements in this ident.
+ _KSForIdentStart =
+ createRadixKeyWithoutLocFromObj(BSONObj(), ident.toString().append(1, '\0'), _ordering);
+ // Similarly, this is the string representation of the KeyString for something greater than
+ // all other elements in this ident.
+ _KSForIdentEnd = createRadixKeyWithoutLocFromObj(BSONObj(), _identEnd, _ordering);
+}
+
+SortedDataInterfaceUnique::SortedDataInterfaceUnique(const Ordering& ordering, StringData ident)
+ : SortedDataInterfaceBase(ordering, ident) {
+ _KSForIdentStart =
+ createRadixKeyWithoutLocFromObj(BSONObj(), ident.toString().append(1, '\0'), _ordering);
+ _KSForIdentEnd = createRadixKeyWithoutLocFromObj(BSONObj(), _identEnd, _ordering);
+}
+
+Status SortedDataInterfaceUnique::insert(OperationContext* opCtx,
+ const KeyString::Value& keyString,
+ bool dupsAllowed) {
+ StringStore* workingCopy(RecoveryUnit::get(opCtx)->getHead());
+ RecordId loc = KeyString::decodeRecordIdAtEnd(keyString.getBuffer(), keyString.getSize());
+
+ std::string key = createRadixKeyWithoutLocFromKS(keyString, _prefix);
+ auto it = workingCopy->find(key);
+ if (it != workingCopy->end()) {
+ if (!dupsAllowed) {
+ // There was an attempt to create an index entry with a different RecordId while
+ // dups were not allowed.
+ return buildDupKeyErrorStatus(opCtx, keyString, _ordering, _desc);
+ }
+
+ UniqueIndexData data(it->second);
+ auto added = data.add(loc, keyString.getTypeBits());
+ if (!added) {
+ // Already indexed
+ return Status::OK();
+ }
+
+ workingCopy->update({std::move(key), *added});
+ } else {
+ UniqueIndexData data;
+ workingCopy->insert({std::move(key), *data.add(loc, keyString.getTypeBits())});
+ }
+ RecoveryUnit::get(opCtx)->makeDirty();
+ return Status::OK();
+}
+
+void SortedDataInterfaceUnique::unindex(OperationContext* opCtx,
+ const KeyString::Value& keyString,
+ bool dupsAllowed) {
+ StringStore* workingCopy(RecoveryUnit::get(opCtx)->getHead());
+ RecordId loc = KeyString::decodeRecordIdAtEnd(keyString.getBuffer(), keyString.getSize());
+
+ auto key = createRadixKeyWithoutLocFromKS(keyString, _prefix);
+ auto it = workingCopy->find(key);
+ if (it != workingCopy->end()) {
+ UniqueIndexData data(it->second);
+ auto removed = data.remove(loc);
+ if (!removed)
+ return; // loc not found, nothing to unindex
+
+ if (UniqueIndexData(*removed).empty()) {
+ workingCopy->erase(key);
+ } else {
+ workingCopy->update({std::move(key), *removed});
+ }
+ RecoveryUnit::get(opCtx)->makeDirty();
+ }
+}
+
+// This function is, as of now, not in the interface, but there exists a server ticket to add
+// truncate to the list of commands able to be used.
+Status SortedDataInterfaceBase::truncate(mongo::RecoveryUnit* ru) {
+ auto bRu = checked_cast<ephemeral_for_test::RecoveryUnit*>(ru);
+ StringStore* workingCopy(bRu->getHead());
+ std::vector<std::string> toDelete;
+ auto end = workingCopy->upper_bound(_KSForIdentEnd);
+ for (auto it = workingCopy->lower_bound(_KSForIdentStart); it != end; ++it) {
+ toDelete.push_back(it->first);
+ }
+ if (!toDelete.empty()) {
+ for (const auto& key : toDelete)
+ workingCopy->erase(key);
+ bRu->makeDirty();
+ }
+
+ return Status::OK();
+}
+
+Status SortedDataInterfaceUnique::dupKeyCheck(OperationContext* opCtx,
+ const KeyString::Value& key) {
+ StringStore* workingCopy(RecoveryUnit::get(opCtx)->getHead());
+
+ std::string radixKey = createRadixKeyWithoutLocFromKSWithoutRecordId(key, _prefix);
+ auto it = workingCopy->find(radixKey);
+ if (it == workingCopy->end())
+ return Status::OK();
+
+ UniqueIndexData data(it->second);
+ if (data.size() > 1) {
+ return buildDupKeyErrorStatus(opCtx, key, _ordering, _desc);
+ }
+
+ return Status::OK();
+}
+
+void SortedDataInterfaceUnique::fullValidate(OperationContext* opCtx,
+ long long* numKeysOut,
+ ValidateResults* fullResults) const {
+ StringStore* workingCopy(RecoveryUnit::get(opCtx)->getHead());
+ long long numKeys = 0;
+ auto it = workingCopy->lower_bound(_KSForIdentStart);
+ while (it != workingCopy->end() && it->first.compare(_KSForIdentEnd) < 0) {
+ numKeys += UniqueIndexData(it->second).size();
+ ++it;
+ }
+ *numKeysOut = numKeys;
+}
+
+bool SortedDataInterfaceBase::appendCustomStats(OperationContext* opCtx,
+ BSONObjBuilder* output,
+ double scale) const {
+ return false;
+}
+
+long long SortedDataInterfaceBase::getSpaceUsedBytes(OperationContext* opCtx) const {
+ StringStore* workingCopy(RecoveryUnit::get(opCtx)->getHead());
+ size_t totalSize = 0;
+ StringStore::const_iterator it = workingCopy->lower_bound(_KSForIdentStart);
+ StringStore::const_iterator end = workingCopy->upper_bound(_KSForIdentEnd);
+ int64_t numElements = workingCopy->distance(it, end);
+ for (int i = 0; i < numElements; i++) {
+ totalSize += it->first.length();
+ ++it;
+ }
+ return (long long)totalSize;
+}
+
+bool SortedDataInterfaceBase::isEmpty(OperationContext* opCtx) {
+ StringStore* workingCopy(RecoveryUnit::get(opCtx)->getHead());
+ return workingCopy->distance(workingCopy->lower_bound(_KSForIdentStart),
+ workingCopy->upper_bound(_KSForIdentEnd)) == 0;
+}
+
+std::unique_ptr<mongo::SortedDataInterface::Cursor> SortedDataInterfaceUnique::newCursor(
+ OperationContext* opCtx, bool isForward) const {
+ StringStore* workingCopy(RecoveryUnit::get(opCtx)->getHead());
+
+ return std::make_unique<CursorUnique>(opCtx,
+ isForward,
+ _prefix,
+ _identEnd,
+ workingCopy,
+ _ordering,
+ _KSForIdentStart,
+ _KSForIdentEnd);
+}
+
+Status SortedDataInterfaceBase::initAsEmpty(OperationContext* opCtx) {
+ return Status::OK();
+}
+
+Status SortedDataBuilderStandard::addKey(const KeyString::Value& keyString) {
+ dassert(KeyString::decodeRecordIdAtEnd(keyString.getBuffer(), keyString.getSize()).isValid());
+ StringStore* workingCopy(RecoveryUnit::get(_opCtx)->getHead());
+ RecordId loc = KeyString::decodeRecordIdAtEnd(keyString.getBuffer(), keyString.getSize());
+
+ std::string key = createRadixKeyWithLocFromKS(keyString, loc, _prefix);
+ bool inserted =
+ workingCopy->insert({std::move(key), IndexDataEntry::create(loc, keyString.getTypeBits())})
+ .second;
+ if (inserted)
+ RecoveryUnit::get(_opCtx)->makeDirty();
+ return Status::OK();
+}
+
+SortedDataBuilderInterface* SortedDataInterfaceStandard::getBulkBuilder(OperationContext* opCtx,
+ bool dupsAllowed) {
+ return new SortedDataBuilderStandard(opCtx,
+ dupsAllowed,
+ _ordering,
+ _prefix,
+ _identEnd,
+ _desc,
+ _indexName,
+ _keyPattern,
+ _collation);
+}
+
+// We append \1 to all idents we get, and therefore the KeyString with ident + \0 will only be
+// before elements in this ident, and the KeyString with ident + \2 will only be after elements in
+// this ident.
+SortedDataInterfaceStandard::SortedDataInterfaceStandard(OperationContext* opCtx,
+ StringData ident,
+ const IndexDescriptor* desc)
+ : SortedDataInterfaceBase(opCtx, ident, desc) {
+ // This is the string representation of the KeyString before elements in this ident, which is
+ // ident + \0. This is before all elements in this ident.
+ _KSForIdentStart = createRadixKeyWithLocFromObj(
+ BSONObj(), RecordId::min(), ident.toString().append(1, '\0'), _ordering);
+ // Similarly, this is the string representation of the KeyString for something greater than
+ // all other elements in this ident.
+ _KSForIdentEnd = createRadixKeyWithLocFromObj(BSONObj(), RecordId::min(), _identEnd, _ordering);
+}
+
+SortedDataInterfaceStandard::SortedDataInterfaceStandard(const Ordering& ordering, StringData ident)
+ : SortedDataInterfaceBase(ordering, ident) {
+ _KSForIdentStart = createRadixKeyWithLocFromObj(
+ BSONObj(), RecordId::min(), ident.toString().append(1, '\0'), _ordering);
+ _KSForIdentEnd = createRadixKeyWithLocFromObj(BSONObj(), RecordId::min(), _identEnd, _ordering);
+}
+
+Status SortedDataInterfaceStandard::insert(OperationContext* opCtx,
+ const KeyString::Value& keyString,
+ bool dupsAllowed) {
+ StringStore* workingCopy(RecoveryUnit::get(opCtx)->getHead());
+ RecordId loc = KeyString::decodeRecordIdAtEnd(keyString.getBuffer(), keyString.getSize());
+
+ std::string key = createRadixKeyWithLocFromKS(keyString, loc, _prefix);
+ bool inserted =
+ workingCopy->insert({std::move(key), IndexDataEntry::create(loc, keyString.getTypeBits())})
+ .second;
+ if (inserted)
+ RecoveryUnit::get(opCtx)->makeDirty();
+ return Status::OK();
+}
+
+void SortedDataInterfaceStandard::unindex(OperationContext* opCtx,
+ const KeyString::Value& keyString,
+ bool dupsAllowed) {
+ StringStore* workingCopy(RecoveryUnit::get(opCtx)->getHead());
+ RecordId loc = KeyString::decodeRecordIdAtEnd(keyString.getBuffer(), keyString.getSize());
+
+ auto key = createRadixKeyWithLocFromKS(keyString, loc, _prefix);
+ if (workingCopy->erase(key))
+ RecoveryUnit::get(opCtx)->makeDirty();
+}
+
+Status SortedDataInterfaceStandard::dupKeyCheck(OperationContext* opCtx,
+ const KeyString::Value& key) {
+ invariant(false);
+ return Status::OK();
+}
+
+void SortedDataInterfaceStandard::fullValidate(OperationContext* opCtx,
+ long long* numKeysOut,
+ ValidateResults* fullResults) const {
+ StringStore* workingCopy(RecoveryUnit::get(opCtx)->getHead());
+ long long numKeys = 0;
+ auto it = workingCopy->lower_bound(_KSForIdentStart);
+ while (it != workingCopy->end() && it->first.compare(_KSForIdentEnd) < 0) {
+ ++numKeys;
+ ++it;
+ }
+ *numKeysOut = numKeys;
+}
+
+std::unique_ptr<mongo::SortedDataInterface::Cursor> SortedDataInterfaceStandard::newCursor(
+ OperationContext* opCtx, bool isForward) const {
+ StringStore* workingCopy(RecoveryUnit::get(opCtx)->getHead());
+
+ return std::make_unique<CursorStandard>(opCtx,
+ isForward,
+ _prefix,
+ _identEnd,
+ workingCopy,
+ _ordering,
+ _KSForIdentStart,
+ _KSForIdentEnd);
+}
+
+} // namespace ephemeral_for_test
+} // namespace mongo