001: /*
002:
003: Derby - Class org.apache.derby.impl.store.raw.data.LogicalPageOperation
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.store.raw.data;
023:
024: import org.apache.derby.impl.store.raw.data.RecordId;
025: import org.apache.derby.impl.store.raw.data.BasePage;
026:
027: import org.apache.derby.iapi.services.sanity.SanityManager;
028:
029: import org.apache.derby.iapi.store.access.conglomerate.LogicalUndo;
030:
031: import org.apache.derby.iapi.store.raw.ContainerHandle;
032: import org.apache.derby.iapi.store.raw.Compensation;
033: import org.apache.derby.iapi.store.raw.LogicalUndoable;
034: import org.apache.derby.iapi.store.raw.LockingPolicy;
035: import org.apache.derby.iapi.store.raw.Page;
036: import org.apache.derby.iapi.store.raw.RecordHandle;
037: import org.apache.derby.iapi.store.raw.Transaction;
038: import org.apache.derby.iapi.store.raw.Undoable;
039:
040: import org.apache.derby.iapi.store.raw.data.RawContainerHandle;
041: import org.apache.derby.iapi.store.raw.xact.RawTransaction;
042: import org.apache.derby.iapi.store.raw.log.LogInstant;
043:
044: import org.apache.derby.iapi.error.StandardException;
045:
046: import org.apache.derby.iapi.types.DataValueDescriptor;
047:
048: import org.apache.derby.iapi.services.io.CompressedNumber;
049:
050: import java.io.ObjectOutput;
051: import java.io.ObjectInput;
052: import java.io.IOException;
053: import org.apache.derby.iapi.services.io.LimitObjectInput;
054:
055: /**
056: An abstract class that is used for logical log operation. A logical log
057: operation is one where the undo of the operation may be applied to a
058: different page than the original operation.
059:
060: <PRE>
061: @format_id no format id, an abstract class.
062: @purpose provide methods for logical undo
063: @upgrade
064: @disk_layout
065: PageBasicOperation the super class
066: recordId(CompressedInt) the recordId this operation affects
067: undo(LogicalUndo) the piece of code that can figure out which page
068: the row has moved into
069: OptionalData none
070: @end_format
071: </PRE>
072:
073: */
074: public abstract class LogicalPageOperation extends PageBasicOperation
075: implements LogicalUndoable {
076:
077: protected LogicalUndo undo; // Callback to access for logical undo.
078: // If non-null, then logical undo is necessary
079: // for this operation. If null, then the
080: // operation really only needs physical undo
081:
082: protected int recordId; // record id - this is what the
083:
084: // recordId is during the doMe time, it
085: // may have been changed now since the
086: // record may move to another page.
087:
088: // no-arg constructor, required by Formatable
089: public LogicalPageOperation() {
090: super ();
091: }
092:
093: protected LogicalPageOperation(BasePage page, LogicalUndo undo,
094: int recordId) {
095: super (page);
096: this .undo = undo;
097: this .recordId = recordId;
098: }
099:
100: /*
101: * Formatable methods
102: */
103:
104: public void writeExternal(ObjectOutput out) throws IOException {
105: super .writeExternal(out);
106: CompressedNumber.writeInt(out, recordId);
107: out.writeObject(undo);
108: }
109:
110: /**
111: Read this in
112: @exception IOException error reading from log stream
113: @exception ClassNotFoundException log stream corrupted
114: */
115: public void readExternal(ObjectInput in) throws IOException,
116: ClassNotFoundException {
117: super .readExternal(in);
118: recordId = CompressedNumber.readInt(in);
119: undo = (LogicalUndo) in.readObject();
120: }
121:
122: /**
123: Undoable method
124: */
125:
126: /**
127: Generate a Compensation (PageUndoOperation) that will rollback the
128: changes of this page operation. If this Page operation cannot or need not
129: be rolled back (redo only), overwrite this function to return null.
130:
131: @see LogicalUndo
132: @exception StandardException Standard Cloudscape policy.
133: @exception IOException Method may read from ObjectInput
134: */
135:
136: public Compensation generateUndo(Transaction xact,
137: LimitObjectInput in) throws StandardException, IOException {
138: // if logical undo is not necessary, use normal physical undo
139: if (undo == null) {
140: BasePage undoPage = findpage(xact);
141:
142: // Needs to pre-dirty this page so that if a checkpoint is taken
143: // any time after the CLR is sent to the log stream, it will wait
144: // for the actual undo to happen on the page. We need this to
145: // preserve the integrity of the redoLWM.
146: undoPage.preDirty();
147:
148: return new LogicalUndoOperation(undoPage, recordId, this );
149: } else {
150: if (SanityManager.DEBUG) {
151: // Sanity check to make sure logical undo is not called inside
152: // internal transaction
153: RawTransaction rtran = (RawTransaction) xact;
154: rtran.checkLogicalOperationOk();
155: }
156:
157: BasePage logicalUndoPage = findLogicalPage(xact, undo, in);
158:
159: // Needs to pre-dirty this page so that if a checkpoint is taken
160: // any time after the CLR is sent to the log stream, it will wait
161: // for the actual undo to happen on the page. We need this to
162: // preserve the integrity of the redoLWM.
163: logicalUndoPage.preDirty();
164:
165: // find logical page is going to call undo.findUndo to find the
166: // right page to apply the CLR to. If the record has changed,
167: // logicalUndo should have resetRecordHandle to reset the page
168: // number and the recordId to the new record location. We need to
169: // store both of these in the clr since during recovery redo,
170: // undo.findUndo is not called.
171: return new LogicalUndoOperation(logicalUndoPage, recordId,
172: this );
173:
174: }
175: }
176:
177: /*
178: * LogicalUndoable methods
179: * These methods are called by undo.findUndo to extract information out of
180: * the log record for the purpose of logical undo.
181: */
182:
183: /**
184: Return the container handle where the log operated on
185: */
186: public ContainerHandle getContainer() {
187: if (SanityManager.DEBUG) {
188: SanityManager.ASSERT(containerHdl != null,
189: "accessing null container handle");
190: }
191:
192: return containerHdl;
193: }
194:
195: /**
196: After the logical undo logic figures out where the real record that
197: needs roll back is, reset this log operation to refer to that record
198: */
199: public void resetRecordHandle(RecordHandle rh) {
200: resetPageNumber(rh.getPageNumber());
201: recordId = rh.getId();
202: }
203:
204: /**
205: Return the record handle that correspond to the record that was changed
206: during roll forward. This is used as a hint by logical undo as a good
207: place to look for the record to apply the roll back.
208: */
209: public RecordHandle getRecordHandle() {
210: return new RecordId(getPageId(), recordId);
211: }
212:
213: /**************************************************************************
214: * Public Methods of RePreparable Interface:
215: **************************************************************************
216: */
217:
218: /**
219: * reclaim locks associated with the changes in this log record.
220: * <p>
221: * @param locking_policy The locking policy to use to claim the locks.
222: *
223: *
224: * @exception StandardException Standard exception policy.
225: **/
226: public void reclaimPrepareLocks(Transaction t,
227: LockingPolicy locking_policy) throws StandardException {
228: if (SanityManager.DEBUG) {
229: SanityManager.DEBUG_PRINT("", "reclaimPrepareLocks().");
230: SanityManager.ASSERT(getRecordHandle() != null);
231: }
232:
233: ContainerHandle ch = t
234: .openContainer(
235: getPageId().getContainerId(),
236: locking_policy,
237: (ContainerHandle.MODE_FORUPDATE
238: | ContainerHandle.MODE_OPEN_FOR_LOCK_ONLY | ContainerHandle.MODE_LOCK_NOWAIT));
239:
240: if (SanityManager.DEBUG) {
241: SanityManager.ASSERT(ch != null);
242: }
243:
244: if (ch != null)
245: ch.close();
246:
247: /*
248: // get the intent lock on the container.
249: boolean lock_granted =
250: locking_policy.lockContainer(
251: t,
252: getContainer(),
253: false, // don't wait for the lock, it is bug if a
254: // lock has to wait while reclaiming locks
255: // during recovery.
256: true); // lock for update.
257:
258: if (SanityManager.DEBUG)
259: {
260: SanityManager.ASSERT(lock_granted);
261: }
262:
263: */
264: // get the row lock on the c.
265: boolean lock_granted = locking_policy.lockRecordForWrite(t,
266: getRecordHandle(), false, // default is not for insert.
267: false); // don't wait for the lock, it is bug if a
268: // lock has to wait while reclaiming locks
269: // during recovery.
270:
271: releaseResource(t);
272:
273: if (SanityManager.DEBUG) {
274: SanityManager.ASSERT(lock_granted);
275: }
276: }
277:
278: /*
279: * method specific to this class
280: */
281:
282: /**
283: Find the page that the rollback operation should be applied to.
284:
285: <P>The actual logical log operation is expected to implement
286: Undoable.generateUndo. This utility function findLogicalPage is provided
287: for the common case scenario of using a LogicalUndo interface to find the
288: undo page. The subclass that implements Undoable.generateUndo can use
289: this function to find the logical page with its LogicalUndo callback function.
290: This method can be used with the default releaseResource().
291:
292: <P>During recovery redo, the logging system is page oriented and will use
293: the pageID stored in the PageUndoOperation to find the page. The
294: page will be latched and released using the default findpage and
295: releaseResource - this.releaseResource() will still be called so it has
296: to know not to release any resource it did not acquire.
297:
298: @param xact the transaction doing the compensating
299: @param in optional input
300:
301: @return the compensation operation that will rollback this change
302:
303: @exception StandardException Standard Cloudscape error policy
304: @exception IOException Method may read from ObjectInput
305:
306: @see PageBasicOperation
307: @see Undoable#generateUndo
308: @see org.apache.derby.iapi.store.raw.Loggable#releaseResource
309:
310:
311: */
312: private BasePage findLogicalPage(Transaction xact,
313: LogicalUndo undo, LimitObjectInput in)
314: throws StandardException, IOException {
315: releaseResource(xact);
316:
317: if (SanityManager.DEBUG) {
318: // the try,finally code makes these assumptions.
319: SanityManager.ASSERT(containerHdl == null);
320: SanityManager.ASSERT(page == null);
321: }
322:
323: boolean okExit = false;
324:
325: try {
326:
327: // open the container
328: RawTransaction rtran = (RawTransaction) xact;
329:
330: containerHdl = rtran.openDroppedContainer(getPageId()
331: .getContainerId(), (LockingPolicy) null);
332:
333: if (SanityManager.DEBUG) {
334: SanityManager.ASSERT(containerHdl != null,
335: "cannot open container");
336: SanityManager
337: .ASSERT(
338: containerHdl.getContainerStatus() != RawContainerHandle.COMMITTED_DROP,
339: "finding a page for undo in a committed dropped container");
340: }
341:
342: page = (BasePage) (undo.findUndo(xact, this , in));
343:
344: if (SanityManager.DEBUG) {
345: SanityManager.ASSERT(page != null,
346: "findUndo returns null page");
347: SanityManager
348: .ASSERT(page.getPageNumber() == getPageId()
349: .getPageNumber(),
350: "undo.findUndo did not reset the log op's recordHandle");
351: }
352:
353: // if you add code here then ensure that you handle page unlatching in the
354: // backout code.
355:
356: okExit = true;
357: } finally {
358:
359: if (!okExit) {
360:
361: if (containerHdl != null) {
362: containerHdl.close();
363: containerHdl = null;
364: }
365: }
366:
367: // no need to unlatch page here because is page is valid no
368: // exceptions can be thrown, until some adds code after the findUndo.
369: }
370:
371: foundHere = true;
372: return page;
373: }
374:
375: /**
376: Undo the change indicated by this log operation and optional data.
377: The undoPage and undoRecordId is the page, record the undo should apply to.
378: The undoRecorId differs from the roll forward recordId if the undoPage
379: differs from the page the roll forward operation was applied to, in
380: other words, the record moved to another page and the recordId changed.
381:
382: <BR>A logical operation can at most deal with 1 record.
383:
384: <P> The available() method of in indicates how much data can be read, i.e.
385: how much was originally written.
386:
387: <BR><B>In this RawStore implementation, should only only be called via
388: CompOp.doMe</B>.
389:
390:
391: @param xact the Transaction doing the rollback
392: @param undoPage the page to rollback changes on
393: @param undoRecordId the recordId to rollback changes on
394: @param CLRinstant the log instant of this (PageUndo) operation
395: @param in optional data for the rollback operation
396:
397: @exception IOException Can be thrown by any of the methods of ObjectInput.
398: @exception StandardException Standard Cloudscape policy.
399: */
400: abstract public void undoMe(Transaction xact, BasePage undoPage,
401: int undoRecordId, LogInstant CLRinstant, LimitObjectInput in)
402: throws StandardException, IOException;
403:
404: }
|