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 testSetEndPosition_Restore_Reverse(bool unique) {
    auto harnessHelper = newHarnessHelper();
    auto opCtx = harnessHelper->newOperationContext();
    auto sorted = harnessHelper->newSortedDataInterface(
        unique,
        {
         {key1, loc1}, {key2, loc1}, {key3, loc1}, {key4, loc1},
        });

    auto cursor = sorted->newCursor(opCtx.get(), false);
    cursor->setEndPosition(key2, false);  // Should never see key1 or key2.

    ASSERT_EQ(cursor->seek(key4, true), IndexKeyEntry(key4, loc1));

    cursor->save();
    cursor->restore();

    ASSERT_EQ(cursor->next(), IndexKeyEntry(key3, loc1));

    cursor->save();
    removeFromIndex(opCtx,
                    sorted,
                    {
                     {key2, loc1}, {key3, loc1},
                    });
    cursor->restore();

    ASSERT_EQ(cursor->next(), boost::none);
}
void testSetEndPosition_RestoreEndCursor_Reverse(bool unique) {
    auto harnessHelper = newHarnessHelper();
    auto opCtx = harnessHelper->newOperationContext();
    auto sorted = harnessHelper->newSortedDataInterface(unique,
                                                        {
                                                         {key1, loc1}, {key4, loc1},
                                                        });

    auto cursor = sorted->newCursor(opCtx.get(), false);
    cursor->setEndPosition(key3, true);

    ASSERT_EQ(cursor->seek(key4, true), IndexKeyEntry(key4, loc1));

    cursor->saveUnpositioned();
    insertToIndex(opCtx,
                  sorted,
                  {
                   {key2, loc1},  // in range
                   {key3, loc1},  // out of range
                  });
    cursor->restore();  // must restore end cursor even with saveUnpositioned().

    ASSERT_EQ(cursor->seek(key4, true), IndexKeyEntry(key4, loc1));
    ASSERT_EQ(cursor->next(), IndexKeyEntry(key3, loc1));
    ASSERT_EQ(cursor->next(), boost::none);
}
void testSetEndPosition_Next_Reverse(bool unique, bool inclusive) {
    auto harnessHelper = newHarnessHelper();
    auto opCtx = harnessHelper->newOperationContext();
    auto sorted = harnessHelper->newSortedDataInterface(
        unique,
        {
         {key1, loc1}, {key2, loc1}, {key3, loc1}, {key4, loc1}, {key5, loc1},
        });

    // Dup key on end point. Illegal for unique indexes.
    if (!unique)
        insertToIndex(opCtx, sorted, {{key3, loc2}});

    auto cursor = sorted->newCursor(opCtx.get(), false);
    cursor->setEndPosition(key3, inclusive);

    ASSERT_EQ(cursor->seek(key5, true), IndexKeyEntry(key5, loc1));
    ASSERT_EQ(cursor->next(), IndexKeyEntry(key4, loc1));
    if (inclusive) {
        if (!unique)
            ASSERT_EQ(cursor->next(), IndexKeyEntry(key3, loc2));
        ASSERT_EQ(cursor->next(), IndexKeyEntry(key3, loc1));
    }
    ASSERT_EQ(cursor->next(), boost::none);
    ASSERT_EQ(cursor->next(), boost::none);  // don't resurrect.
}
Example #5
0
    /**
     * Constructs an IndexKeyEntry from a slice containing the bytes of a BSONObject followed
     * by the bytes of a RecordId
     */
    static IndexKeyEntry makeIndexKeyEntry(const WT_ITEM *keyCols) {
        const char* data = static_cast<const char*>( keyCols->data );
        BSONObj key( data );
        if ( keyCols->size == static_cast<size_t>( key.objsize() ) ) {
            // in unique mode
            return IndexKeyEntry( key, RecordId() );
        }
        invariant( keyCols->size == key.objsize() + sizeof(RecordIdRepr) );

        return IndexKeyEntry( key, getRecordIdAt(data + key.objsize()));
    }
Example #6
0
 /**
  * Constructs an IndexKeyEntry from a slice containing the bytes of a BSONObject followed
  * by the bytes of a RecordId
  */
 static IndexKeyEntry makeIndexKeyEntry(const WT_ITEM *keyCols) {
     const char* data = reinterpret_cast<const char*>( keyCols->data );
     BSONObj key( data );
     if ( keyCols->size == static_cast<size_t>( key.objsize() ) ) {
         // in unique mode
         return IndexKeyEntry( key, RecordId() );
     }
     invariant( keyCols->size == key.objsize() + sizeof(RecordId) );
     RecordId loc = reinterpret_cast<const RecordId*>( data + key.objsize() )[0];
     return IndexKeyEntry( key, loc );
 }
Example #7
0
        Status addKey(const BSONObj& key, const DiskLoc& loc) {
            // inserts should be in ascending (key, DiskLoc) order.

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


            invariant(!loc.isNull());
            invariant(loc.isValid());
            invariant(!hasFieldNames(key));

            if (!_data->empty()) {
                if (key < _last->key || (_dupsAllowed && key == _last->key && loc < _last->loc)) {
                    return Status(ErrorCodes::InternalError,
                                  "expected ascending (key, DiskLoc) order in bulk builder");
                }
                else if (!_dupsAllowed && key == _last->key && loc != _last->loc) {
                    return dupKeyError(key);
                }
            }

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

            return Status::OK();
        }
Example #8
0
        virtual Status insert(OperationContext* txn,
                              const BSONObj& key,
                              const DiskLoc& loc,
                              bool dupsAllowed) {

            invariant(!loc.isNull());
            invariant(loc.isValid());
            invariant(!hasFieldNames(key));

            if ( key.objsize() >= TempKeyMaxSize ) {
                string msg = mongoutils::str::stream()
                    << "Heap1Btree::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);

            BSONObj owned = key.getOwned();
            if ( _data->insert(IndexKeyEntry(owned, loc)).second ) {
                _currentKeySize += key.objsize();
                Heap1RecoveryUnit::notifyIndexInsert( txn, this, owned, loc );
            }
            return Status::OK();
        }
void testSetEndPosition_Seek_Reverse(bool unique, bool inclusive) {
    auto harnessHelper = newHarnessHelper();
    auto opCtx = harnessHelper->newOperationContext();
    auto sorted = harnessHelper->newSortedDataInterface(unique,
                                                        {
                                                         {key1, loc1},
                                                         {key2, loc1},
                                                         // No key3
                                                         {key4, loc1},
                                                        });

    auto cursor = sorted->newCursor(opCtx.get(), false);
    cursor->setEndPosition(key2, inclusive);

    // Directly seeking past end is considered out of range.
    ASSERT_EQ(cursor->seek(key1, true), boost::none);
    ASSERT_EQ(cursor->seekExact(key1), boost::none);

    // Seeking to key2 directly or indirectly is only returned if endPosition is inclusive.
    auto maybeKey2 = inclusive ? boost::make_optional(IndexKeyEntry(key2, loc1)) : boost::none;

    // direct
    ASSERT_EQ(cursor->seek(key2, true), maybeKey2);
    ASSERT_EQ(cursor->seekExact(key2), maybeKey2);

    // indirect
    ASSERT_EQ(cursor->seek(key3, true), maybeKey2);

    cursor->saveUnpositioned();
    removeFromIndex(opCtx, sorted, {{key2, loc1}});
    cursor->restore();

    ASSERT_EQ(cursor->seek(key3, true), boost::none);
    ASSERT_EQ(cursor->seek(key2, true), boost::none);
}
void testSetEndPosition_Empty_Reverse(bool unique, bool inclusive) {
    auto harnessHelper = newHarnessHelper();
    auto opCtx = harnessHelper->newOperationContext();
    auto sorted = harnessHelper->newSortedDataInterface(unique,
                                                        {
                                                         {key1, loc1}, {key2, loc1}, {key3, loc1},
                                                        });

    auto cursor = sorted->newCursor(opCtx.get(), false);
    cursor->setEndPosition(BSONObj(), inclusive);

    ASSERT_EQ(cursor->seek(key3, true), IndexKeyEntry(key3, loc1));
    ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc1));
    ASSERT_EQ(cursor->next(), IndexKeyEntry(key1, loc1));
    ASSERT_EQ(cursor->next(), boost::none);
}
    // Tests seekExact on reverse cursor when it hits something with dup keys. Doesn't make sense
    // for unique indexes.
    TEST(SortedDataInterface, SeekExact_HitWithDups_Reverse) {
        auto harnessHelper = newHarnessHelper();
        auto opCtx = harnessHelper->newOperationContext();
        auto sorted = harnessHelper->newSortedDataInterface(false, {
            {key1, loc1},
            {key2, loc1},
            {key2, loc2},
            {key3, loc1},
        });

        auto cursor = sorted->newCursor(opCtx.get(), false);

        ASSERT_EQ(cursor->seekExact(key2), IndexKeyEntry(key2, loc2));
        ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc1));
        ASSERT_EQ(cursor->next(), IndexKeyEntry(key1, loc1));
        ASSERT_EQ(cursor->next(), boost::none);
    }
bool isDup(const IndexSet& data, const BSONObj& key, RecordId loc) {
    const IndexSet::const_iterator it = data.find(IndexKeyEntry(key, RecordId()));
    if (it == data.end())
        return false;

    // Not a dup if the entry is for the same loc.
    return it->loc != loc;
}
Example #13
0
Status IndexAccessMethod::update(OperationContext* opCtx,
                                 const UpdateTicket& ticket,
                                 int64_t* numInserted,
                                 int64_t* numDeleted) {
    invariant(numInserted);
    invariant(numDeleted);

    *numInserted = 0;
    *numDeleted = 0;

    if (!ticket._isValid) {
        return Status(ErrorCodes::InternalError, "Invalid UpdateTicket in update");
    }

    if (ticket.oldKeys.size() + ticket.added.size() - ticket.removed.size() > 1 ||
        isMultikeyFromPaths(ticket.newMultikeyPaths)) {
        _btreeState->setMultikey(opCtx, ticket.newMultikeyPaths);
    }

    for (size_t i = 0; i < ticket.removed.size(); ++i) {
        _newInterface->unindex(opCtx, ticket.removed[i], ticket.loc, ticket.dupsAllowed);
        IndexKeyEntry indexEntry = IndexKeyEntry(ticket.removed[i], ticket.loc);
    }

    for (size_t i = 0; i < ticket.added.size(); ++i) {
        Status status =
            _newInterface->insert(opCtx, ticket.added[i], ticket.loc, ticket.dupsAllowed);
        if (!status.isOK()) {
            if (status.code() == ErrorCodes::KeyTooLong && ignoreKeyTooLong(opCtx)) {
                // Ignore.
                IndexKeyEntry indexEntry = IndexKeyEntry(ticket.added[i], ticket.loc);
                continue;
            }

            return status;
        }

        IndexKeyEntry indexEntry = IndexKeyEntry(ticket.added[i], ticket.loc);
    }

    *numInserted = ticket.added.size();
    *numDeleted = ticket.removed.size();

    return Status::OK();
}
    // Tests seekExact when it hits something.
    void testSeekExact_Hit(bool unique, bool forward) {
        auto harnessHelper = newHarnessHelper();
        auto opCtx = harnessHelper->newOperationContext();
        auto sorted = harnessHelper->newSortedDataInterface(unique, {
            {key1, loc1},
            {key2, loc1},
            {key3, loc1},
        });

        auto cursor = sorted->newCursor(opCtx.get(), forward);

        ASSERT_EQ(cursor->seekExact(key2), IndexKeyEntry(key2, loc1));

        // Make sure iterating works. We may consider loosening this requirement if it is a hardship
        // for some storage engines.
        ASSERT_EQ(cursor->next(), IndexKeyEntry(forward ? key3 : key1, loc1));
        ASSERT_EQ(cursor->next(), boost::none);
    }
    // Ensure that restore lands as close as possible to original position, even if data inserted
    // while saved.
    void testSaveAndRestorePositionSeesNewInserts(bool forward, bool unique) {
        auto harnessHelper = newHarnessHelper();
        auto opCtx = harnessHelper->newOperationContext();
        auto sorted = harnessHelper->newSortedDataInterface(unique, {
            {key1, loc1},
            {key3, loc1},
        });

        auto cursor = sorted->newCursor(opCtx.get(), forward);
        const auto seekPoint = forward ? key1 : key3;

        ASSERT_EQ(cursor->seek(seekPoint, true), IndexKeyEntry(seekPoint, loc1));

        cursor->savePositioned();
        insertToIndex(opCtx, sorted, {{key2, loc1}});
        cursor->restore(opCtx.get());

        ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc1));
    }
bool isDup(const IndexSet& data, const BSONObj& key) {
    IndexSet::const_iterator it = data.find(IndexKeyEntry(key, RecordId()));
    if (it == data.end())
        return false;

    ++it;
    if (it == data.end())
        return false;

    return it->key.woCompare(key, BSONObj(), false) == 0;
}
Example #17
0
            virtual bool locate(const BSONObj& keyRaw, const DiskLoc& loc) {
                const BSONObj key = stripFieldNames(keyRaw);
                _it = _data.lower_bound(IndexKeyEntry(key, loc)); // lower_bound is >= key
                if ( _it == _data.end() ) {
                    return false;
                }

                if ( _it->key != key ) {
                    return false;
                }

                return _it->loc == loc;
            }
    // Make sure we restore to a RecordId at or ahead of save point if same key on reverse cursor.
    void testSaveAndRestorePositionConsidersRecordId_Reverse(bool unique) {
        auto harnessHelper = newHarnessHelper();
        auto opCtx = harnessHelper->newOperationContext();
        auto sorted = harnessHelper->newSortedDataInterface(unique, {
            {key0, loc1},
            {key1, loc1},
            {key2, loc2},
        });

        auto cursor = sorted->newCursor(opCtx.get(), false);

        ASSERT_EQ(cursor->seek(key2, true), IndexKeyEntry(key2, loc2));

        cursor->savePositioned();
        removeFromIndex(opCtx, sorted, {{key2, loc2}});
        insertToIndex(opCtx, sorted, {{key2, loc1}});
        cursor->restore(opCtx.get());

        ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc1));

        cursor->savePositioned();
        removeFromIndex(opCtx, sorted, {{key2, loc1}});
        insertToIndex(opCtx, sorted, {{key2, loc2}});
        cursor->restore(opCtx.get());

        ASSERT_EQ(cursor->next(), IndexKeyEntry(key1, loc1));

        cursor->savePositioned();
        removeFromIndex(opCtx, sorted, {{key1, loc1}});
        cursor->restore(opCtx.get());

        cursor->savePositioned();
        insertToIndex(opCtx, sorted, {{key1, loc1}});
        cursor->restore(opCtx.get()); // Lands at same point as initial save.

        // Advances from restore point since restore didn't move position.
        ASSERT_EQ(cursor->next(), IndexKeyEntry(key0, loc1));
    }
    StatusWith<SpecialFormatInserted> addKey(const BSONObj& key, const RecordId& loc) {
        // inserts should be in ascending (key, RecordId) order.

        invariant(loc.isValid());
        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 buildDupKeyErrorStatus(key, _collectionNamespace, _indexName, _keyPattern);
            }
        }

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

        return StatusWith<SpecialFormatInserted>(SpecialFormatInserted::NoSpecialFormatInserted);
    }
Example #20
0
        virtual bool unindex(OperationContext* txn, const BSONObj& key, const DiskLoc& loc) {
            invariant(!loc.isNull());
            invariant(loc.isValid());
            invariant(!hasFieldNames(key));

            const size_t numDeleted = _data->erase(IndexKeyEntry(key, loc));
            invariant(numDeleted <= 1);
            if ( numDeleted == 1 ) {
                _currentKeySize -= key.objsize();
                Heap1RecoveryUnit::notifyIndexRemove( txn, this, key, loc );
            }

            return numDeleted == 1;
        }
    // Ensure that SaveUnpositioned allows later use of the cursor.
    TEST(SortedDataInterface, SaveUnpositionedAndRestore) {
        auto harnessHelper = newHarnessHelper();
        auto opCtx = harnessHelper->newOperationContext();
        auto sorted = harnessHelper->newSortedDataInterface(false, {
            {key1, loc1},
            {key2, loc1},
            {key3, loc1},
        });

        auto cursor = sorted->newCursor(opCtx.get());

        ASSERT_EQ(cursor->seek(key2, true), IndexKeyEntry(key2, loc1));

        cursor->saveUnpositioned();
        removeFromIndex(opCtx, sorted, {{key2, loc1}});
        cursor->restore(opCtx.get());

        ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1));

        cursor->saveUnpositioned();
        cursor->restore(opCtx.get());

        ASSERT_EQ(cursor->seek(key3, true), IndexKeyEntry(key3, loc1));
    }
    // Ensure that repeated restores lands as close as possible to original position, even if data
    // inserted while saved and the current position removed in a way that temporarily makes the
    // cursor EOF.
    void testSaveAndRestorePositionSeesNewInsertsAfterEOF(bool forward, bool unique) {
        auto harnessHelper = newHarnessHelper();
        auto opCtx = harnessHelper->newOperationContext();
        auto sorted = harnessHelper->newSortedDataInterface(false, {
            {key1, loc1},
        });

        auto cursor = sorted->newCursor(opCtx.get(), forward);

        ASSERT_EQ(cursor->seek(key1, true), IndexKeyEntry(key1, loc1));
        // next() would return EOF now.

        cursor->savePositioned();
        removeFromIndex(opCtx, sorted, {{key1, loc1}});
        cursor->restore(opCtx.get());
        // The restore may have seeked to EOF.

        auto insertPoint = forward ? key2 : key0;
        cursor->savePositioned(); // Should still save key1 as "current position".
        insertToIndex(opCtx, sorted, {{insertPoint, loc1}});
        cursor->restore(opCtx.get());

        ASSERT_EQ(cursor->next(), IndexKeyEntry(insertPoint, loc1));
    }
    // Ensure that repeated restores lands as close as possible to original position, even if data
    // inserted while saved and the current position removed.
    void testSaveAndRestorePositionSeesNewInsertsAfterRemove(bool forward, bool unique) {
        auto harnessHelper = newHarnessHelper();
        auto opCtx = harnessHelper->newOperationContext();
        auto sorted = harnessHelper->newSortedDataInterface(unique, {
            {key1, loc1},
            {key3, loc1},
        });

        auto cursor = sorted->newCursor(opCtx.get(), forward);
        const auto seekPoint = forward ? key1 : key3;

        ASSERT_EQ(cursor->seek(seekPoint, true), IndexKeyEntry(seekPoint, loc1));

        cursor->savePositioned();
        removeFromIndex(opCtx, sorted, {{key1, loc1}});
        cursor->restore(opCtx.get());
        // The restore may have seeked since it can't return to the saved position.

        cursor->savePositioned(); // Should still save originally saved key as "current position".
        insertToIndex(opCtx, sorted, {{key2, loc1}});
        cursor->restore(opCtx.get());

        ASSERT_EQ(cursor->next(), IndexKeyEntry(key2, loc1));
    }
Example #24
0
 virtual void customLocate(const BSONObj& keyBegin,
                           int keyBeginLen,
                           bool afterKey,
                           const vector<const BSONElement*>& keyEnd,
                           const vector<bool>& keyEndInclusive) {
     // makeQueryObject handles stripping of fieldnames for us.
     _it = lower_bound(IndexKeyEntry(IndexEntryComparison::makeQueryObject(
                                       keyBegin,
                                       keyBeginLen,
                                       afterKey,
                                       keyEnd,
                                       keyEndInclusive,
                                       -1), // reverse
                                  DiskLoc()));
 }
Example #25
0
void IndexAccessMethod::removeOneKey(OperationContext* opCtx,
                                     const BSONObj& key,
                                     const RecordId& loc,
                                     bool dupsAllowed) {

    try {
        _newInterface->unindex(opCtx, key, loc, dupsAllowed);
        IndexKeyEntry indexEntry = IndexKeyEntry(key, loc);
    } catch (AssertionException& e) {
        log() << "Assertion failure: _unindex failed " << _descriptor->indexNamespace();
        log() << "Assertion failure: _unindex failed: " << redact(e) << "  key:" << key.toString()
              << "  dl:" << loc;
        logContext();
    }
}
        void locate(const BSONObj& key, const RecordId& loc) {
            _isEOF = false;
            const auto query = IndexKeyEntry(key, loc);
            _it = _data.lower_bound(query);
            if (_forward) {
                if (_it == _data.end())
                    _isEOF = true;
            } else {
                // lower_bound lands us on or after query. Reverse cursors must be on or before.
                if (_it == _data.end() || _data.value_comp().compare(*_it, query) > 0)
                    advance();  // sets _isEOF if there is nothing more to return.
            }

            if (atOrPastEndPointAfterSeeking())
                _isEOF = true;
        }
    // Tests seekExact when it doesn't hit the query.
    void testSeekExact_Miss(bool unique, bool forward) {
        auto harnessHelper = newHarnessHelper();
        auto opCtx = harnessHelper->newOperationContext();
        auto sorted = harnessHelper->newSortedDataInterface(unique, {
            {key1, loc1},
            // No key2.
            {key3, loc1},
        });

        auto cursor = sorted->newCursor(opCtx.get(), forward);

        ASSERT_EQ(cursor->seekExact(key2), boost::none);

        // Not testing iteration since the cursors position following a failed seekExact is
        // undefined. However, you must be able to seek somewhere else.
        ASSERT_EQ(cursor->seekExact(key1), IndexKeyEntry(key1, loc1));
    }
    // Insert multiple keys and try to iterate through all of them
    // using a reverse cursor while calling savePosition() and
    // restorePosition() in succession.
    TEST( SortedDataInterface, SaveAndRestorePositionWhileIterateCursorReversed ) {
        const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
        const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );

        {
            const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
            ASSERT( sorted->isEmpty( opCtx.get() ) );
        }

        int nToInsert = 10;
        for ( int i = 0; i < nToInsert; i++ ) {
            const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
            {
                WriteUnitOfWork uow( opCtx.get() );
                BSONObj key = BSON( "" << i );
                RecordId loc( 42, i * 2 );
                ASSERT_OK( sorted->insert( opCtx.get(), key, loc, true ) );
                uow.commit();
            }
        }

        {
            const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
            ASSERT_EQUALS( nToInsert, sorted->numEntries( opCtx.get() ) );
        }

        {
            const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
            const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get(), false) );
            int i = nToInsert - 1;
            for (auto entry = cursor->seek(maxKey, true); entry; i--, entry = cursor->next()) {
                ASSERT_GTE(i, 0);
                ASSERT_EQ(entry, IndexKeyEntry(BSON( "" << i), RecordId(42, i * 2)));

                cursor->savePositioned();
                cursor->restore( opCtx.get() );
            }
            ASSERT( !cursor->next() );
            ASSERT_EQ(i, -1);
        }
    }
    // Insert the same key multiple times and try to iterate through each
    // occurrence using a forward cursor while calling savePosition() and
    // restorePosition() in succession. Verify that the RecordId is saved
    // as part of the current position of the cursor.
    TEST( SortedDataInterface, SaveAndRestorePositionWhileIterateCursorWithDupKeys ) {
        const std::unique_ptr<HarnessHelper> harnessHelper( newHarnessHelper() );
        const std::unique_ptr<SortedDataInterface> sorted( harnessHelper->newSortedDataInterface( false ) );

        {
            const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
            ASSERT( sorted->isEmpty( opCtx.get() ) );
        }

        int nToInsert = 10;
        for ( int i = 0; i < nToInsert; i++ ) {
            const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
            {
                WriteUnitOfWork uow( opCtx.get() );
                RecordId loc( 42, i * 2 );
                ASSERT_OK( sorted->insert( opCtx.get(), key1, loc, true /* allow duplicates */ ) );
                uow.commit();
            }
        }

        {
            const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
            ASSERT_EQUALS( nToInsert, sorted->numEntries( opCtx.get() ) );
        }

        {
            const std::unique_ptr<OperationContext> opCtx( harnessHelper->newOperationContext() );
            const std::unique_ptr<SortedDataInterface::Cursor> cursor( sorted->newCursor(opCtx.get()) );
            int i = 0;
            for (auto entry = cursor->seek(minKey, true); entry; i++, entry = cursor->next()) {
                ASSERT_LT(i, nToInsert);
                ASSERT_EQ(entry, IndexKeyEntry(key1, RecordId(42, i * 2)));

                cursor->savePositioned();
                cursor->restore( opCtx.get() );
            }
            ASSERT( !cursor->next() );
            ASSERT_EQ(i, nToInsert);
        }
    }
Example #30
0
        Status addKey(const BSONObj& key, const DiskLoc& loc) {
            // inserts should be in ascending order.

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


            invariant(!loc.isNull());
            invariant(loc.isValid());
            invariant(!hasFieldNames(key));

            // TODO optimization: dup check can assume dup is only possible with last inserted key
            // and avoid the log(n) lookup.
            if (!_dupsAllowed && isDup(*_data, key, loc))
                return dupKeyError(key);

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

            return Status::OK();
        }