Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions src/include/storage/table/group_collection.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,19 @@ class GroupCollection {
T* getGroup(const common::UniqLock& lock, common::idx_t groupIdx) const {
DASSERT(lock.isLocked());
UNUSED(lock);
DASSERT(groupIdx < groups.size());
// A stale/invalid CSR row can map to an out-of-range group index (e.g.
// getQuotientRemainder(INVALID_ROW_IDX)). Returning nullptr instead of
// performing an out-of-bounds std::vector access lets callers skip the
// stale row rather than dereferencing into undefined behavior (SIGSEGV).
if (groupIdx >= groups.size()) {
return nullptr;
}
return groups[groupIdx].get();
}
T* getGroupNoLock(common::idx_t groupIdx) const {
DASSERT(groupIdx < groups.size());
if (groupIdx >= groups.size()) {
return nullptr;
}
return groups[groupIdx].get();
}
void replaceGroup(const common::UniqLock& lock, common::idx_t groupIdx,
Expand Down
26 changes: 24 additions & 2 deletions src/storage/table/csr_node_group.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,10 @@ NodeGroupScanResult CSRNodeGroup::scanCommittedInMemSequential(const Transaction
const auto lock = chunkedGroups.lock();
chunkedGroup = chunkedGroups.getGroup(lock, chunkIdx);
}
if (chunkedGroup == nullptr) {
// Stale/invalid committed-in-memory row: nothing to scan.
return NODE_GROUP_SCAN_EMPTY_RESULT;
}
chunkedGroup->scan(transaction, tableState, nodeGroupScanState, startRowInChunk, numRows);
nodeGroupScanState.nextRowToScan += numRows;
return NodeGroupScanResult{startRow, numRows};
Expand Down Expand Up @@ -319,6 +323,11 @@ NodeGroupScanResult CSRNodeGroup::scanCommittedInMemRandom(const Transaction* tr
const auto lock = chunkedGroups.lock();
chunkedGroup = chunkedGroups.getGroup(lock, chunkIdx);
}
if (chunkedGroup == nullptr) {
// Stale/invalid committed-in-memory row: skip it.
nextRow++;
continue;
}
DASSERT(chunkedGroup);
numSelected += chunkedGroup->lookup(transaction, tableState, nodeGroupScanState, rowInChunk,
numSelected);
Expand Down Expand Up @@ -410,6 +419,10 @@ void CSRNodeGroup::update(const Transaction* transaction, CSRNodeGroupScanSource
StorageConfig::CHUNKED_NODE_GROUP_CAPACITY);
const auto lock = chunkedGroups.lock();
const auto chunkedGroup = chunkedGroups.getGroup(lock, chunkIdx);
if (chunkedGroup == nullptr) {
// Stale/invalid committed-in-memory row: target row no longer exists.
return;
}
return chunkedGroup->update(transaction, rowInChunk, columnID, propertyVector);
}
default: {
Expand All @@ -432,6 +445,10 @@ bool CSRNodeGroup::delete_(const Transaction* transaction, CSRNodeGroupScanSourc
StorageConfig::CHUNKED_NODE_GROUP_CAPACITY);
const auto lock = chunkedGroups.lock();
const auto chunkedGroup = chunkedGroups.getGroup(lock, chunkIdx);
if (chunkedGroup == nullptr) {
// Stale/invalid committed-in-memory row: nothing to delete.
return false;
}
return chunkedGroup->delete_(transaction, rowInChunk);
}
default: {
Expand Down Expand Up @@ -854,6 +871,10 @@ std::vector<ChunkCheckpointState> CSRNodeGroup::checkpointColumnInRegion(const U
auto [chunkIdx, rowInChunk] = StorageUtils::getQuotientRemainder(row,
StorageConfig::CHUNKED_NODE_GROUP_CAPACITY);
const auto chunkedGroup = chunkedGroups.getGroup(lock, chunkIdx);
if (chunkedGroup == nullptr) {
// Stale/invalid committed-in-memory row: skip the insertion write.
continue;
}
writeInMemoryCSRInsertion(txn, writeCursor, *chunkedGroup, rowInChunk, columnID,
chunkState);
}
Expand Down Expand Up @@ -914,7 +935,7 @@ void CSRNodeGroup::collectInMemRegionChangesAndUpdateHeaderLength(const UniqLock
auto [chunkIdx, rowInChunk] = StorageUtils::getQuotientRemainder(row,
StorageConfig::CHUNKED_NODE_GROUP_CAPACITY);
const auto chunkedGroup = chunkedGroups.getGroup(lock, chunkIdx);
if (chunkedGroup->isDeleted(txn, rowInChunk)) {
if (chunkedGroup == nullptr || chunkedGroup->isDeleted(txn, rowInChunk)) {
csrIndex->indices[nodeOffset].turnToNonSequential();
csrIndex->indices[nodeOffset].setInvalid(i);
numInMemDeletionsInCSR++;
Expand Down Expand Up @@ -1118,7 +1139,8 @@ void CSRNodeGroup::populateCSRLengthInMemOnly(const UniqLock& lock, offset_t num
auto [chunkIdx, rowInChunk] =
StorageUtils::getQuotientRemainder(row, StorageConfig::CHUNKED_NODE_GROUP_CAPACITY);
const auto chunkedGroup = chunkedGroups.getGroup(lock, chunkIdx);
const auto isDeleted = chunkedGroup->isDeleted(txn, rowInChunk);
const auto isDeleted =
(chunkedGroup == nullptr) || chunkedGroup->isDeleted(txn, rowInChunk);
if (isDeleted) {
csrIndex->indices[offset].turnToNonSequential();
csrIndex->indices[offset].setInvalid(i);
Expand Down
Loading