bool CopyOnWriteContext::notifyTupleDelete(TableTuple &tuple) { assert(m_iterator != NULL); if (tuple.isDirty() || m_finishedTableScan) { return true; } /** * Find out which block the address is contained in. Lower bound returns the first entry * in the index >= the address. Unless the address happens to be equal then the block * we are looking for is probably the previous entry. Then check if the address fits * in the previous entry. If it doesn't then the block is something new. */ TBPtr block = PersistentTable::findBlock(tuple.address(), m_blocks, getTable().getTableAllocationSize()); if (block.get() == NULL) { // tuple not in snapshot region, don't care about this tuple return true; } /** * Now check where this is relative to the COWIterator. */ CopyOnWriteIterator *iter = reinterpret_cast<CopyOnWriteIterator*>(m_iterator.get()); return !iter->needToDirtyTuple(block->address(), tuple.address()); }
void CopyOnWriteContext::markTupleDirty(TableTuple tuple, bool newTuple) { assert(m_iterator != NULL); if (newTuple) { m_inserts++; } else { m_updates++; } /** * If this an update or a delete of a tuple that is already dirty then no further action is * required. */ if (!newTuple && tuple.isDirty()) { return; } /** * If the table has been scanned already there is no need to continue marking tuples dirty * If the tuple is dirty then it has already been backed up. */ if (m_finishedTableScan) { tuple.setDirtyFalse(); return; } /** * Find out which block the address is contained in. */ TBPtr block = PersistentTable::findBlock(tuple.address(), m_blocks, getTable().getTableAllocationSize()); if (block.get() == NULL) { // tuple not in snapshot region, don't care about this tuple, no need to dirty it tuple.setDirtyFalse(); return; } /** * Now check where this is relative to the COWIterator. */ CopyOnWriteIterator *iter = reinterpret_cast<CopyOnWriteIterator*>(m_iterator.get()); if (iter->needToDirtyTuple(block->address(), tuple.address())) { tuple.setDirtyTrue(); /** * Don't back up a newly introduced tuple, just mark it as dirty. */ if (!newTuple) { m_backedUpTuples->insertTupleNonVirtualWithDeepCopy(tuple, &m_pool); } } else { tuple.setDirtyFalse(); return; } }
/** * When a tuple is "dirty" it is still active, but will never be a "found" tuple * since it is skipped. The tuple may be dirty because it was deleted (this is why it is always skipped). In that * case the CopyOnWriteContext calls this to ensure that the iteration finds the correct number of tuples * in the used portion of the table blocks and doesn't overrun to the uninitialized block memory because * it skiped a dirty tuple and didn't end up with the right found tuple count upon reaching the end. */ bool CopyOnWriteIterator::needToDirtyTuple(char *tupleAddress) { if (m_tableEmpty) { // snapshot was activated when the table was empty. // Tuple is not in snapshot region, don't care about this tuple assert(m_currentBlock == NULL); return false; } /** * Find out which block the address is contained in. Lower bound returns the first entry * in the index >= the address. Unless the address happens to be equal then the block * we are looking for is probably the previous entry. Then check if the address fits * in the previous entry. If it doesn't then the block is something new. */ TBPtr block = PersistentTable::findBlock(tupleAddress, m_blocks, m_table->getTableAllocationSize()); if (block.get() == NULL) { // tuple not in snapshot region, don't care about this tuple return false; } assert(m_currentBlock != NULL); /** * Now check where this is relative to the COWIterator. */ const char *blockAddress = block->address(); if (blockAddress > m_currentBlock->address()) { return true; } assert(blockAddress == m_currentBlock->address()); if (tupleAddress >= m_location) { return true; } else { return false; } }