RecordId KVRecordStore::KVRecordCursor::getNext() { if (isEOF()) { return RecordId(); } // We need valid copies of _savedLoc / _savedVal since we are // about to advance the underlying cursor. _saveLocAndVal(); _cursor->advance(_txn); if (!isEOF()) { if (_idTracker) { RecordId currentId = curr(); if (!_lowestInvisible.isNull()) { // oplog if (currentId >= _lowestInvisible) { _cursor.reset(); } else if (RecordId(currentId.repr() + 1) == _lowestInvisible && !_idTracker->canReadId(currentId)) { _cursor.reset(); } } else if (!_idTracker->canReadId(currentId)) { _cursor.reset(); } } } return _savedLoc; }
Status TerarkDbRecordStore::updateRecord(OperationContext* txn, const RecordId& id, const char* data, int len, bool enforceQuota, UpdateNotifier* notifier) { CompositeTable* tab = m_table->m_tab.get(); terark::db::IncrementGuard_size_t incrGuard(tab->m_inprogressWritingCount); llong recId = id.repr() - 1; { terark::db::MyRwLock lock(tab->m_rwMutex, false); size_t segIdx = tab->getSegmentIndexOfRecordIdNoLock(recId); if (segIdx >= tab->getSegNum()) { return {ErrorCodes::InvalidIdField, "record id is out of range"}; } auto seg = tab->getSegmentPtr(segIdx); if (seg->m_isFreezed) { return {ErrorCodes::NeedsDocumentMove, "segment of record is frozen"}; } } auto& td = m_table->getMyThreadData(); BSONObj bson(data); td.m_coder.encode(&tab->rowSchema(), nullptr, bson, &td.m_buf); llong newRecId = tab->updateRow(recId, td.m_buf, &*td.m_dbCtx); invariant(newRecId == recId); return Status::OK(); }
Status MobileRecordStore::insertRecords(OperationContext* opCtx, std::vector<Record>* inOutRecords, const std::vector<Timestamp>& timestamps) { // Inserts record into SQLite table (or replaces if duplicate record id). MobileSession* session = MobileRecoveryUnit::get(opCtx)->getSession(opCtx, false); SqliteStatement insertStmt( *session, "INSERT OR REPLACE INTO \"", _ident, "\"(rec_id, data) VALUES(?, ?);"); for (auto& record : *inOutRecords) { const auto data = record.data.data(); const auto len = record.data.size(); _changeNumRecs(opCtx, 1); _changeDataSize(opCtx, len); RecordId recId = _nextId(); insertStmt.bindInt(0, recId.repr()); insertStmt.bindBlob(1, data, len); insertStmt.step(SQLITE_DONE); record.id = recId; insertStmt.reset(); } return Status::OK(); }
void MobileRecordStore::deleteRecord(OperationContext* opCtx, const RecordId& recId) { MobileSession* session = MobileRecoveryUnit::get(opCtx)->getSession(opCtx, false); SqliteStatement dataSizeStmt( *session, "SELECT IFNULL(LENGTH(data), 0) FROM \"", _ident, "\" WHERE rec_id = ?;"); dataSizeStmt.bindInt(0, recId.repr()); dataSizeStmt.step(SQLITE_ROW); int64_t dataSizeBefore = dataSizeStmt.getColInt(0); _changeNumRecs(opCtx, -1); _changeDataSize(opCtx, -dataSizeBefore); SqliteStatement deleteStmt(*session, "DELETE FROM \"", _ident, "\" WHERE rec_id = ?;"); deleteStmt.bindInt(0, recId.repr()); deleteStmt.step(SQLITE_DONE); }
Status MobileRecordStore::updateRecord(OperationContext* opCtx, const RecordId& recId, const char* data, int len) { MobileSession* session = MobileRecoveryUnit::get(opCtx)->getSession(opCtx, false); SqliteStatement dataSizeStmt( *session, "SELECT IFNULL(LENGTH(data), 0) FROM \"", _ident, "\" WHERE rec_id = ?;"); dataSizeStmt.bindInt(0, recId.repr()); dataSizeStmt.step(SQLITE_ROW); int64_t dataSizeBefore = dataSizeStmt.getColInt(0); _changeDataSize(opCtx, -dataSizeBefore + len); SqliteStatement updateStmt( *session, "UPDATE \"", _ident, "\" SET data = ? ", "WHERE rec_id = ?;"); updateStmt.bindBlob(0, data, len); updateStmt.bindInt(1, recId.repr()); updateStmt.step(SQLITE_DONE); return Status::OK(); }
bool TerarkDbRecordStore::findRecord(OperationContext* txn, const RecordId& id, RecordData* out) const { if (id.isNull()) return false; llong recIdx = id.repr() - 1; CompositeTable* tab = m_table->m_tab.get(); auto& td = m_table->getMyThreadData(); tab->getValue(recIdx, &td.m_buf, &*td.m_dbCtx); SharedBuffer bson = td.m_coder.decode(&tab->rowSchema(), td.m_buf); // size_t bufsize = sizeof(SharedBuffer::Holder) + bson.objsize(); int bufsize = ConstDataView(bson.get()).read<LittleEndian<int>>(); *out = RecordData(bson, bufsize); return true; }
boost::optional<Record> seekExact(const RecordId& id) final { // Set the saved position and use save/restore to reprepare the SQL statement so that // the cursor restarts at the parameter id. int decr = (_forward ? -1 : 1); _savedId = RecordId(id.repr() + decr); _eof = false; save(); restore(); boost::optional<Record> rec = next(); if (rec && rec->id != id) { // The record we found isn't the one the caller asked for. return boost::none; } return rec; }
bool MobileRecordStore::findRecord(OperationContext* opCtx, const RecordId& recId, RecordData* rd) const { MobileSession* session = MobileRecoveryUnit::get(opCtx)->getSession(opCtx); SqliteStatement stmt(*session, "SELECT data FROM \"", _ident, "\" WHERE rec_id = ?;"); stmt.bindInt(0, recId.repr()); int status = stmt.step(); if (status == SQLITE_DONE) { return false; } embedded::checkStatus(status, SQLITE_ROW, "sqlite3_step"); const void* recData = stmt.getColBlob(0); int nBytes = stmt.getColBytes(0); *rd = RecordData(static_cast<const char*>(recData), nBytes).getOwned(); return true; }
void putRecordId(void* dest, RecordId loc) { const RecordIdRepr repr = loc.repr(); memcpy(dest, &repr, sizeof(repr)); }
void TerarkDbRecordStore::deleteRecord(OperationContext* txn, const RecordId& id) { auto& td = m_table->getMyThreadData(); m_table->m_tab->removeRow(id.repr()-1, &*td.m_dbCtx); }
int64_t WiredTigerRecordStore::_makeKey(const RecordId& loc) { return loc.repr(); }
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()); } } } }