virtual Status insert(OperationContext* opCtx,
                          const BSONObj& key,
                          const RecordId& loc,
                          bool dupsAllowed) {
        invariant(loc.isNormal());
        invariant(!hasFieldNames(key));

        if (key.objsize() >= TempKeyMaxSize) {
            string msg = mongoutils::str::stream()
                << "EphemeralForTestBtree::insert: key too large to index, failing " << ' '
                << key.objsize() << ' ' << key;
            return Status(ErrorCodes::KeyTooLong, msg);
        }

        // TODO optimization: save the iterator from the dup-check to speed up insert
        if (!dupsAllowed && isDup(*_data, key, loc))
            return dupKeyError(key);

        IndexKeyEntry entry(key.getOwned(), loc);
        if (_data->insert(entry).second) {
            _currentKeySize += key.objsize();
            opCtx->recoveryUnit()->registerChange(new IndexChange(_data, entry, true));
        }
        return Status::OK();
    }
    Status addKey(const BSONObj& key, const RecordId& loc) {
        // inserts should be in ascending (key, RecordId) order.

        if (key.objsize() >= TempKeyMaxSize) {
            return Status(ErrorCodes::KeyTooLong, "key too big");
        }

        invariant(loc.isNormal());
        invariant(!hasFieldNames(key));

        if (!_data->empty()) {
            // Compare specified key with last inserted key, ignoring its RecordId
            int cmp = _comparator.compare(IndexKeyEntry(key, RecordId()), *_last);
            if (cmp < 0 || (_dupsAllowed && cmp == 0 && loc < _last->loc)) {
                return Status(ErrorCodes::InternalError,
                              "expected ascending (key, RecordId) order in bulk builder");
            } else if (!_dupsAllowed && cmp == 0 && loc != _last->loc) {
                return dupKeyError(key);
            }
        }

        BSONObj owned = key.getOwned();
        _last = _data->insert(_data->end(), IndexKeyEntry(owned, loc));
        *_currentKeySize += key.objsize();

        return Status::OK();
    }
    void KVRecordStore::KVRecordCursor::_setCursor(const RecordId id) {
        // We should no cursor at this point, either because we're getting newly
        // constructed or because we're recovering from saved state (and so
        // the old cursor needed to be dropped).
        invariant(!_cursor);
        _cursor.reset();
        _savedLoc = RecordId();
        _savedVal = Slice();

        // A new iterator with no start position will be either min() or max()
        invariant(id.isNormal() || id == RecordId::min() || id == RecordId::max());
        const int dir = _isForward ? 1 : -1;
        _cursor.reset(_db->getCursor(_txn, Slice::of(KeyString(id)), dir));
    }
    virtual void unindex(OperationContext* opCtx,
                         const BSONObj& key,
                         const RecordId& loc,
                         bool dupsAllowed) {
        invariant(loc.isNormal());
        invariant(!hasFieldNames(key));

        IndexKeyEntry entry(key.getOwned(), loc);
        const size_t numDeleted = _data->erase(entry);
        invariant(numDeleted <= 1);
        if (numDeleted == 1) {
            _currentKeySize -= key.objsize();
            opCtx->recoveryUnit()->registerChange(new IndexChange(_data, entry, false));
        }
    }
RecordId WiredTigerRecordStore::_nextId() {
    invariant(!_useOplogHack);
    RecordId out = RecordId(_nextIdNum.fetchAndAdd(1));
    invariant(out.isNormal());
    return out;
}
Exemple #6
0
TEST(KeyStringTest, RecordIds) {
    for (int i = 0; i < 63; i++) {
        const RecordId rid = RecordId(1ll << i);

        {  // Test encoding / decoding of single RecordIds
            const KeyString ks(rid);
            ASSERT_GTE(ks.getSize(), 2u);
            ASSERT_LTE(ks.getSize(), 10u);

            ASSERT_EQ(KeyString::decodeRecordIdAtEnd(ks.getBuffer(), ks.getSize()), rid);

            {
                BufReader reader(ks.getBuffer(), ks.getSize());
                ASSERT_EQ(KeyString::decodeRecordId(&reader), rid);
                ASSERT(reader.atEof());
            }

            if (rid.isNormal()) {
                ASSERT_GT(ks, KeyString(RecordId()));
                ASSERT_GT(ks, KeyString(RecordId::min()));
                ASSERT_LT(ks, KeyString(RecordId::max()));

                ASSERT_GT(ks, KeyString(RecordId(rid.repr() - 1)));
                ASSERT_LT(ks, KeyString(RecordId(rid.repr() + 1)));
            }
        }

        for (int j = 0; j < 63; j++) {
            RecordId other = RecordId(1ll << j);

            if (rid == other)
                ASSERT_EQ(KeyString(rid), KeyString(other));
            if (rid < other)
                ASSERT_LT(KeyString(rid), KeyString(other));
            if (rid > other)
                ASSERT_GT(KeyString(rid), KeyString(other));

            {
                // Test concatenating RecordIds like in a unique index.
                KeyString ks;
                ks.appendRecordId(RecordId::max());  // uses all bytes
                ks.appendRecordId(rid);
                ks.appendRecordId(RecordId(0xDEADBEEF));  // uses some extra bytes
                ks.appendRecordId(rid);
                ks.appendRecordId(RecordId(1));  // uses no extra bytes
                ks.appendRecordId(rid);
                ks.appendRecordId(other);

                ASSERT_EQ(KeyString::decodeRecordIdAtEnd(ks.getBuffer(), ks.getSize()), other);

                // forward scan
                BufReader reader(ks.getBuffer(), ks.getSize());
                ASSERT_EQ(KeyString::decodeRecordId(&reader), RecordId::max());
                ASSERT_EQ(KeyString::decodeRecordId(&reader), rid);
                ASSERT_EQ(KeyString::decodeRecordId(&reader), RecordId(0xDEADBEEF));
                ASSERT_EQ(KeyString::decodeRecordId(&reader), rid);
                ASSERT_EQ(KeyString::decodeRecordId(&reader), RecordId(1));
                ASSERT_EQ(KeyString::decodeRecordId(&reader), rid);
                ASSERT_EQ(KeyString::decodeRecordId(&reader), other);
                ASSERT(reader.atEof());
            }
        }
    }
}
RecordId EphemeralForTestRecordStore::allocateLoc() {
    RecordId out = RecordId(_data->nextId++);
    invariant(out.isNormal());
    return out;
}
RecordId MobileRecordStore::_nextId() {
    RecordId out = RecordId(_nextIdNum.fetchAndAdd(1));
    invariant(out.isNormal());
    return out;
}