| 609 | } |
| 610 | |
| 611 | void NodeTable::commit(main::ClientContext* context, TableCatalogEntry* tableEntry, |
| 612 | LocalTable* localTable) { |
| 613 | const auto startNodeOffset = nodeGroups->getNumTotalRows(); |
| 614 | auto& localNodeTable = localTable->cast<LocalNodeTable>(); |
| 615 | |
| 616 | std::vector<column_id_t> columnIDsToCommit; |
| 617 | for (auto& property : tableEntry->getProperties()) { |
| 618 | auto columnID = tableEntry->getColumnID(property.getName()); |
| 619 | columnIDsToCommit.push_back(columnID); |
| 620 | } |
| 621 | |
| 622 | auto transaction = transaction::Transaction::Get(*context); |
| 623 | // 1. Append all tuples from local storage to nodeGroups regardless of deleted or not. |
| 624 | // Note: We cannot simply remove all deleted tuples in local node table, as they may have |
| 625 | // connected local rels. Directly removing them will cause shift of committed node offset, |
| 626 | // leading to an inconsistent result with connected rels. |
| 627 | nodeGroups->append(transaction, columnIDsToCommit, localNodeTable.getNodeGroups()); |
| 628 | // 2. Set deleted flag for tuples that are deleted in local storage. |
| 629 | row_idx_t numLocalRows = 0u; |
| 630 | for (auto localNodeGroupIdx = 0u; localNodeGroupIdx < localNodeTable.getNumNodeGroups(); |
| 631 | localNodeGroupIdx++) { |
| 632 | const auto localNodeGroup = localNodeTable.getNodeGroup(localNodeGroupIdx); |
| 633 | if (localNodeGroup->hasDeletions(transaction)) { |
| 634 | // TODO(Guodong): Assume local storage is small here. Should optimize the loop away by |
| 635 | // grabbing a set of deleted rows. |
| 636 | for (auto row = 0u; row < localNodeGroup->getNumRows(); row++) { |
| 637 | if (localNodeGroup->isDeleted(transaction, row)) { |
| 638 | const auto nodeOffset = startNodeOffset + numLocalRows + row; |
| 639 | const auto nodeGroupIdx = StorageUtils::getNodeGroupIdx(nodeOffset); |
| 640 | const auto rowIdxInGroup = |
| 641 | nodeOffset - StorageUtils::getStartOffsetOfNodeGroup(nodeGroupIdx); |
| 642 | [[maybe_unused]] const bool isDeleted = |
| 643 | nodeGroups->getNodeGroup(nodeGroupIdx)->delete_(transaction, rowIdxInGroup); |
| 644 | DASSERT(isDeleted); |
| 645 | if (transaction->shouldAppendToUndoBuffer()) { |
| 646 | transaction->pushDeleteInfo(nodeGroupIdx, rowIdxInGroup, 1, |
| 647 | &versionRecordHandler); |
| 648 | } |
| 649 | } |
| 650 | } |
| 651 | } |
| 652 | numLocalRows += localNodeGroup->getNumRows(); |
| 653 | } |
| 654 | |
| 655 | // 3. Scan index columns for newly inserted tuples. |
| 656 | for (auto& index : indexes) { |
| 657 | if (!index.needCommitInsert()) { |
| 658 | continue; |
| 659 | } |
| 660 | if (!index.isLoaded()) { |
| 661 | throw RuntimeException( |
| 662 | "Cannot commit index insertions for index " + index.getName() + |
| 663 | ", because it is not loaded. Please load the extension for the index first."); |
| 664 | } |
| 665 | UncommittedIndexInserter indexInserter{startNodeOffset, this, index.getIndex(), |
| 666 | getVisibleFunc(transaction)}; |
| 667 | // We need to scan from local storage here because some tuples in local node groups might |
| 668 | // have been deleted. |
nothing calls this directly
no test coverage detected