use of org.apache.derby.iapi.store.raw.xact.RawTransaction in project derby by apache.
the class InsertOperation method undoMe.
/*
* PageOperation methods
*/
/**
* Undo the insert by simply marking the just inserted record as deleted.
* All logical undo logic has already been taken care of by generateUndo.
*
* @exception IOException Can be thrown by any of the methods of ObjectInput.
* @exception StandardException Standard Derby policy.
*
* @see LogicalPageOperation#undoMe
*/
public void undoMe(Transaction xact, BasePage undoPage, int undoRecordId, LogInstant CLRInstant, LimitObjectInput in) throws StandardException, IOException {
int slot = undoPage.findRecordById(undoRecordId, Page.FIRST_SLOT_NUMBER);
if (SanityManager.DEBUG) {
// page number and recordId
if (undoRecordId != this.recordId)
if (undoPage.getPageNumber() == getPageId().getPageNumber())
SanityManager.THROWASSERT("recordId changed from " + this.recordId + " to " + undoRecordId + " but page number did not change " + undoPage.getPageNumber());
if (slot == -1)
SanityManager.THROWASSERT("recordId " + undoRecordId + " not found on page " + undoPage.getPageNumber());
}
RawTransaction rxact = (RawTransaction) xact;
if ((insertFlag & Page.INSERT_UNDO_WITH_PURGE) != 0) {
undoPage.purgeRecord(CLRInstant, slot, undoRecordId);
// stream.
if (rxact.handlesPostTerminationWork() && undoPage.isOverflowPage() && undoPage.recordCount() == 0) {
ReclaimSpace work = new ReclaimSpace(ReclaimSpace.PAGE, (PageKey) undoPage.getIdentity(), rxact.getDataFactory(), true);
rxact.addPostTerminationWork(work);
}
} else {
undoPage.setDeleteStatus(CLRInstant, slot, true);
if (rxact.handlesPostTerminationWork() && !undoPage.isOverflowPage()) {
if (undoPage.shouldReclaimSpace(undoPage.getPageNumber() == 1 ? 1 : 0, slot)) {
((BaseDataFileFactory) rxact.getDataFactory()).insertUndoNotify(rxact, undoPage.getPageKey());
}
}
}
undoPage.setAuxObject(null);
}
use of org.apache.derby.iapi.store.raw.xact.RawTransaction in project derby by apache.
the class LogicalPageOperation method generateUndo.
/**
* Undoable method
*/
/**
* Generate a Compensation (PageUndoOperation) that will rollback the
* changes of this page operation. If this Page operation cannot or need not
* be rolled back (redo only), overwrite this function to return null.
*
* @see LogicalUndo
* @exception StandardException Standard Derby policy.
* @exception IOException Method may read from ObjectInput
*/
public Compensation generateUndo(Transaction xact, LimitObjectInput in) throws StandardException, IOException {
// if logical undo is not necessary, use normal physical undo
if (undo == null) {
BasePage undoPage = findpage(xact);
// Needs to pre-dirty this page so that if a checkpoint is taken
// any time after the CLR is sent to the log stream, it will wait
// for the actual undo to happen on the page. We need this to
// preserve the integrity of the redoLWM.
undoPage.preDirty();
return new LogicalUndoOperation(undoPage, recordId, this);
} else {
if (SanityManager.DEBUG) {
// Sanity check to make sure logical undo is not called inside
// internal transaction
RawTransaction rtran = (RawTransaction) xact;
rtran.checkLogicalOperationOk();
}
BasePage logicalUndoPage = findLogicalPage(xact, undo, in);
// Needs to pre-dirty this page so that if a checkpoint is taken
// any time after the CLR is sent to the log stream, it will wait
// for the actual undo to happen on the page. We need this to
// preserve the integrity of the redoLWM.
logicalUndoPage.preDirty();
// undo.findUndo is not called.
return new LogicalUndoOperation(logicalUndoPage, recordId, this);
}
}
use of org.apache.derby.iapi.store.raw.xact.RawTransaction in project derby by apache.
the class XactFactory method rollbackAllTransactions.
/**
* Rollback all active transactions that has updated the raw store.
* Use the recovery Transaction that is passed in to do all the work.
* Used in recovery only.
*
* <P>
* Transactions are rolled back in the following order:
* <OL>
* <LI>internal transactions in reversed beginXact chronological order,
* <LI>all other transactions in reversed beginXact chronological order,
* </NL>
*
* @param recoveryTransaction use this transaction to do all the user
* transaction work
*
* @exception StandardException any exception thrown during rollback
*/
public void rollbackAllTransactions(RawTransaction recoveryTransaction, RawStoreFactory rsf) throws StandardException {
if (SanityManager.DEBUG) {
if (rawStoreFactory != null)
SanityManager.ASSERT(rawStoreFactory == rsf, "raw store factory different");
SanityManager.ASSERT(recoveryTransaction != null, "recovery transaction null");
}
int irbcount = 0;
// First undo internal transactions if there is any
if (ttab.hasRollbackFirstTransaction()) {
RawTransaction internalTransaction = startInternalTransaction(rsf, recoveryTransaction.getContextManager());
// make this transaction be aware that it is being used by recovery
internalTransaction.recoveryTransaction();
if (SanityManager.DEBUG)
SanityManager.ASSERT(internalTransaction.handlesPostTerminationWork() == false, "internal recovery xact handles post termination work");
while (ttab.getMostRecentRollbackFirstTransaction(internalTransaction)) {
irbcount++;
internalTransaction.abort();
}
internalTransaction.close();
}
if (SanityManager.DEBUG) {
SanityManager.ASSERT(ttab.hasRollbackFirstTransaction() == false, "cant rollback user xacts with existing active internal xacts");
}
int rbcount = 0;
// recoveryTransacion assumes the identity of the most recent xact
while (ttab.getMostRecentTransactionForRollback(recoveryTransaction)) {
if (SanityManager.DEBUG) {
SanityManager.ASSERT(recoveryTransaction.handlesPostTerminationWork() == false, "recovery transaction handles post termination work");
}
rbcount++;
recoveryTransaction.abort();
}
if (SanityManager.DEBUG) {
if (rbcount > 0 || irbcount > 0) {
// RESOLVE: put this in the log trace
// System.out.println(
// "Recovery rolled back " + irbcount +
// " internal transactions,"
// + rbcount + " user transactions");
}
}
}
use of org.apache.derby.iapi.store.raw.xact.RawTransaction in project derby by apache.
the class StoredPage method logRecordDataPortion.
private void logRecordDataPortion(int slot, int flag, StoredRecordHeader recordHeader, FormatableBitSet validColumns, OutputStream out, RecordHandle headRowHandle) throws StandardException, IOException {
int offset = getRecordOffset(slot);
// now skip over the original header before writing the data
int oldHeaderLength = recordHeader.size();
offset += oldHeaderLength;
// write out the record data (FH+data+...) from the page data
int startField = recordHeader.getFirstField();
int endField = startField + recordHeader.getNumberFields();
int validColumnsSize = (validColumns == null) ? 0 : validColumns.getLength();
for (int fieldId = startField; fieldId < endField; fieldId++) {
rawDataIn.setPosition(offset);
// get the field header information from the page
int fieldStatus = StoredFieldHeader.readStatus(rawDataIn);
int fieldDataLength = StoredFieldHeader.readFieldDataLength(rawDataIn, fieldStatus, slotFieldSize);
// for purges unless the field is overflow pointer for a long column.
if (((validColumns != null) && !(validColumnsSize > fieldId && validColumns.isSet(fieldId))) || ((flag & BasePage.LOG_RECORD_FOR_PURGE) != 0 && !StoredFieldHeader.isOverflow(fieldStatus))) {
// nope, move page offset along
offset += StoredFieldHeader.size(fieldStatus, fieldDataLength, slotFieldSize);
offset += fieldDataLength;
// write a non-existent field
fieldStatus = StoredFieldHeader.setInitial();
fieldStatus = StoredFieldHeader.setNonexistent(fieldStatus);
StoredFieldHeader.write(out, fieldStatus, 0, slotFieldSize);
continue;
}
// If temp container, don't do anything.
if (((flag & BasePage.LOG_RECORD_FOR_UPDATE) != 0) && headRowHandle != null && StoredFieldHeader.isOverflow(fieldStatus) && owner.isTemporaryContainer() == false) {
// remember the page offset
int saveOffset = rawDataIn.getPosition();
long overflowPage = CompressedNumber.readLong((InputStream) rawDataIn);
int overflowId = CompressedNumber.readInt((InputStream) rawDataIn);
// Remember the time stamp on the first page of the column
// chain. This is to prevent the case where the post commit
// work gets fired twice, in that case, the second time it is
// fired, this overflow page may not part of this row chain
// that is being updated.
Page firstPageOnColumnChain = getOverflowPage(overflowPage);
PageTimeStamp ts = firstPageOnColumnChain.currentTimeStamp();
firstPageOnColumnChain.unlatch();
RawTransaction rxact = (RawTransaction) owner.getTransaction();
ReclaimSpace work = new ReclaimSpace(ReclaimSpace.COLUMN_CHAIN, headRowHandle, // long column about to be orphaned by update
fieldId, // page where the long column starts
overflowPage, // record Id of the beginning of the long column
overflowId, ts, rxact.getDataFactory(), true);
rxact.addPostCommitWork(work);
// Just to be safe, reset data stream
rawDataIn.setPosition(saveOffset);
}
// write the field header for the log
offset += StoredFieldHeader.write(out, fieldStatus, fieldDataLength, slotFieldSize);
if (fieldDataLength != 0) {
// write the actual data
out.write(pageData, offset, fieldDataLength);
offset += fieldDataLength;
}
}
}
use of org.apache.derby.iapi.store.raw.xact.RawTransaction in project derby by apache.
the class StoredPage method doUpdateAtSlot.
/**
* Perform an update.
*
* @exception StandardException Standard Derby policy
*/
public void doUpdateAtSlot(RawTransaction t, int slot, int id, Object[] row, FormatableBitSet validColumns) throws StandardException {
// If this is a head page, the recordHandle is the head row handle.
// If this is not a head page, we are calling updateAtSlot inside some
// convoluted loop that updates an overflow chain. There is nothing we
// can doing about it anyway.
RecordHandle headRowHandle = isOverflowPage() ? null : getRecordHandleAtSlot(slot);
// RESOLVE: djd/yyz what does a null row means? (sku)
if (row == null) {
owner.getActionSet().actionUpdate(t, this, slot, id, row, validColumns, -1, (DynamicByteArrayOutputStream) null, -1, headRowHandle);
return;
}
// startColumn is the first column to be updated.
int startColumn = RowUtil.nextColumn(row, validColumns, 0);
if (startColumn == -1)
return;
if (SanityManager.DEBUG) {
// exactly N columns are passed in via the row array.
if (!isOverflowPage() && validColumns != null) {
if (RowUtil.getNumberOfColumns(-1, validColumns) > row.length)
SanityManager.THROWASSERT("updating slot " + slot + " on page " + getIdentity() + " " + RowUtil.getNumberOfColumns(-1, validColumns) + " bits are set in validColumns but only " + row.length + " columns in row[]");
}
}
// Keep track of row shrinkage in the head row piece. If any row piece
// shrinks, file a post commit work to clear all reserved space for the
// entire row chain.
boolean rowHasReservedSpace = false;
StoredPage curPage = this;
for (; ; ) {
StoredRecordHeader rh = curPage.getHeaderAtSlot(slot);
int startField = rh.getFirstField();
int endFieldExclusive = startField + rh.getNumberFields();
// curPage contains column[startField] to column[endFieldExclusive-1]
// Need to cope with an update that is increasing the number of
// columns. If this occurs we want to make sure that we perform a
// single update to the last portion of a record, and not an update
// of the current columns and then an update to append a column.
long nextPage = -1;
int realStartColumn = -1;
int realSpaceOnPage = -1;
if (!rh.hasOverflow() || ((startColumn >= startField) && (startColumn < endFieldExclusive))) {
boolean hitLongColumn;
int nextColumn = -1;
Object[] savedFields = null;
DynamicByteArrayOutputStream logBuffer = null;
do {
try {
// Update this portion of the record.
// Pass in headRowHandle in case we are to update any
// long column and they need to be cleaned up by post
// commit processing. We don't want to purge the
// columns right now because in order to reclaim the
// page, we need to remove them. But it would be bad
// to remove them now because the transaction may not
// commit for a long time. We can do both purging of
// the long column and page removal together in the
// post commit.
nextColumn = owner.getActionSet().actionUpdate(t, curPage, slot, id, row, validColumns, realStartColumn, logBuffer, realSpaceOnPage, headRowHandle);
hitLongColumn = false;
} catch (LongColumnException lce) {
if (lce.getRealSpaceOnPage() == -1) {
// an update that has caused the row to increase
// in size *and* push some fields off the page
// that need to be inserted in an overflow page
// no need to make a copy as we are going to use
// this buffer right away
logBuffer = lce.getLogBuffer();
savedFields = (Object[]) lce.getColumn();
realStartColumn = lce.getNextColumn();
realSpaceOnPage = -1;
hitLongColumn = true;
continue;
}
// we caught a real long column exception
// three things should happen here:
// 1. insert the long column into overflow pages.
// 2. append the overflow field header in the main chain.
// 3. continue the update in the main data chain.
logBuffer = new DynamicByteArrayOutputStream(lce.getLogBuffer());
// step 1: insert the long column ... if this update
// operation rolls back, purge the after image column
// chain and reclaim the overflow page because the
// whole chain will be orphaned anyway.
RecordHandle longColumnHandle = insertLongColumn(curPage, lce, Page.INSERT_UNDO_WITH_PURGE);
// step 2: append overflow field header to log buffer
int overflowFieldLen = 0;
try {
overflowFieldLen += appendOverflowFieldHeader(logBuffer, longColumnHandle);
} catch (IOException ioe) {
throw StandardException.newException(SQLState.DATA_UNEXPECTED_EXCEPTION, ioe);
}
// step 3: continue the insert in the main data chain
// need to pass the log buffer, and start column to the
// next insert.
realStartColumn = lce.getNextColumn() + 1;
realSpaceOnPage = lce.getRealSpaceOnPage() - overflowFieldLen;
hitLongColumn = true;
} catch (NoSpaceOnPage nsop) {
throw StandardException.newException(SQLState.DATA_UNEXPECTED_NO_SPACE_ON_PAGE, nsop, ((PageKey) curPage.getIdentity()).toString(), getPageDumpString(), slot, id, validColumns.toString(), realStartColumn, 0, headRowHandle);
}
} while (hitLongColumn);
// See if we completed all the columns that are on this page.
int validColumnsSize = (validColumns == null) ? 0 : validColumns.getLength();
if (nextColumn != -1) {
if (SanityManager.DEBUG) {
if ((nextColumn < startField) || (rh.hasOverflow() && (nextColumn >= endFieldExclusive))) {
SanityManager.THROWASSERT("nextColumn out of range = " + nextColumn + " expected between " + startField + " and " + endFieldExclusive);
}
}
// Need to insert rows from nextColumn to endFieldExclusive
// onto a new overflow page.
// If the column is not being updated we
// pick it up from the current page. If it is being updated
// we take it from the new value.
int possibleLastFieldExclusive = endFieldExclusive;
if (!rh.hasOverflow()) {
// we might be adding a field here
if (validColumns == null) {
if (row.length > possibleLastFieldExclusive)
possibleLastFieldExclusive = row.length;
} else {
if (validColumnsSize > possibleLastFieldExclusive)
possibleLastFieldExclusive = validColumnsSize;
}
}
// use a sparse row
Object[] newRow = new Object[possibleLastFieldExclusive];
FormatableBitSet newColumnList = new FormatableBitSet(possibleLastFieldExclusive);
ByteArrayOutputStream fieldStream = null;
for (int i = nextColumn; i < possibleLastFieldExclusive; i++) {
if ((validColumns == null) || (validColumnsSize > i && validColumns.isSet(i))) {
newColumnList.set(i);
// use the new value
newRow[i] = RowUtil.getColumn(row, validColumns, i);
} else if (i < endFieldExclusive) {
newColumnList.set(i);
// use the old value
newRow[i] = savedFields[i - nextColumn];
}
}
RecordHandle handle = curPage.getRecordHandleAtSlot(slot);
// there cannot be any updates to do.
if (rh.hasOverflow()) {
// We have to carry across the overflow information
// from the current record, if any.
nextPage = rh.getOverflowPage();
id = rh.getOverflowId();
// find the next starting column before unlatching page
startColumn = RowUtil.nextColumn(row, validColumns, endFieldExclusive);
} else {
startColumn = -1;
nextPage = 0;
}
// Don't bother with temp container.
if (!rowHasReservedSpace && headRowHandle != null && curPage != null && !owner.isTemporaryContainer()) {
rowHasReservedSpace = curPage.checkRowReservedSpace(slot);
}
// insert the record portion on a new overflow page at slot
// 0 this will automatically handle any overflows in
// this new portion
// BasePage op = getNewOverflowPage();
BasePage op = curPage.getOverflowPageForInsert(slot, newRow, newColumnList, nextColumn);
// We have all the information from this page so unlatch it
if (curPage != this) {
curPage.unlatch();
curPage = null;
}
byte mode = Page.INSERT_OVERFLOW;
if (nextPage != 0)
mode |= Page.INSERT_FOR_SPLIT;
RecordHandle nextPortionHandle = nextPage == 0 ? null : owner.makeRecordHandle(nextPage, id);
// RESOLVED (sku): even though we would like to roll back
// these inserts with PURGE rather than with delete,
// we have to delete because if we purge the last row
// from an overflow page, the purge will queue a post
// commit to remove the page.
// While this is OK with long columns, we cannot do this
// for long rows because long row overflow pages can be
// shared by more than one long rows, and thus it is unsafe
// to remove the page without first latching the head page.
// However, the insert log record do not have the head
// row's page number so the rollback cannot put that
// information into the post commit work.
RecordHandle portionHandle;
try {
portionHandle = op.insertAllowOverflow(0, newRow, newColumnList, nextColumn, mode, 100, nextPortionHandle);
} catch (NoSpaceOnPage nsop) {
throw StandardException.newException(SQLState.DATA_UNEXPECTED_NO_SPACE_ON_PAGE, nsop, ((PageKey) op.getIdentity()).toString(), getPageDumpString(), slot, id, newColumnList.toString(), nextColumn, mode, nextPortionHandle);
}
// Update the previous record header to point to new portion
if (curPage == this)
updateOverflowDetails(this, handle, portionHandle);
else
updateOverflowDetails(handle, portionHandle);
op.unlatch();
} else {
// See earlier comments on checking row reserved space.
if (!rowHasReservedSpace && headRowHandle != null && curPage != null && !owner.isTemporaryContainer()) {
rowHasReservedSpace = curPage.checkRowReservedSpace(slot);
}
// find the next starting column before we unlatch the page
startColumn = rh.hasOverflow() ? RowUtil.nextColumn(row, validColumns, endFieldExclusive) : -1;
}
// have we completed this update?
if (startColumn == -1) {
if ((curPage != this) && (curPage != null))
curPage.unlatch();
// break out of the for loop
break;
}
}
if (nextPage == -1) {
if (SanityManager.DEBUG) {
SanityManager.ASSERT(curPage != null, "Current page is null be no overflow information has been obtained");
}
// Get the next page info while we still have the page
// latched.
nextPage = rh.getOverflowPage();
id = rh.getOverflowId();
}
if ((curPage != this) && (curPage != null))
curPage.unlatch();
// get the next portion page and find the correct slot
curPage = (StoredPage) owner.getPage(nextPage);
if (SanityManager.DEBUG) {
SanityManager.ASSERT(curPage.isOverflowPage(), "following row chain gets a non-overflow page");
}
slot = curPage.findRecordById(id, FIRST_SLOT_NUMBER);
}
// row post commit.
if (rowHasReservedSpace) {
RawTransaction rxact = (RawTransaction) owner.getTransaction();
ReclaimSpace work = new ReclaimSpace(ReclaimSpace.ROW_RESERVE, headRowHandle, rxact.getDataFactory(), true);
rxact.addPostCommitWork(work);
}
}
Aggregations