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; }
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; }