001: /*
002:
003: Derby - Class org.apache.derby.impl.store.raw.data.PageBasicOperation
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.iapi.reference.SQLState;
025:
026: import org.apache.derby.impl.store.raw.data.BasePage;
027:
028: import org.apache.derby.iapi.store.raw.ContainerHandle;
029: import org.apache.derby.iapi.store.raw.LockingPolicy;
030: import org.apache.derby.iapi.store.raw.Loggable;
031: import org.apache.derby.iapi.store.raw.Page;
032: import org.apache.derby.iapi.store.raw.RePreparable;
033: import org.apache.derby.iapi.store.raw.Transaction;
034: import org.apache.derby.iapi.store.raw.PageKey;
035:
036: import org.apache.derby.iapi.store.raw.xact.RawTransaction;
037: import org.apache.derby.iapi.store.raw.data.RawContainerHandle;
038: import org.apache.derby.iapi.store.raw.log.LogInstant;
039: import org.apache.derby.iapi.store.raw.RawStoreFactory;
040:
041: import org.apache.derby.iapi.error.StandardException;
042: import org.apache.derby.iapi.services.sanity.SanityManager;
043:
044: import org.apache.derby.iapi.types.DataValueDescriptor;
045:
046: import org.apache.derby.iapi.services.io.CompressedNumber;
047: import org.apache.derby.iapi.util.ByteArray;
048: import org.apache.derby.iapi.services.io.DynamicByteArrayOutputStream;
049: import org.apache.derby.iapi.services.property.PropertyUtil;
050:
051: import java.io.InputStream;
052: import java.io.ObjectOutput;
053: import java.io.ObjectInput;
054: import java.io.IOException;
055: import org.apache.derby.iapi.services.io.LimitObjectInput;
056:
057: /**
058: A PageBasicOperation changed the content of a page, this is the root class of all
059: page oriented operation. Each PageBasicOperation record change(s)
060: that apply to <B>one and only one page</B>. The pageID that is changed
061: must be recorded in the log operation - in other words, redo
062: must be physical (or more correctly, in Gray's term, physiological, since
063: changes are logical <B>within</B> a page).
064: <BR>Undo can be logical, but the undo logic must be hidden in
065: generateUndo. By the time a compensation operation is logged as a
066: LogOperation, the page that needs roll back must be determined.
067:
068: <PRE>
069: @format_id no format id, an abstract class.
070: @purpose provide methods for logical undo
071: @upgrade
072: @disk_layout
073: pageId(PageKey) the page this operation applies to
074: pageVersion(CompressedLong) the page version this operation applied to
075: OptionalData none
076: @end_format
077: </PRE>
078:
079: @see Loggable
080: */
081:
082: public abstract class PageBasicOperation implements Loggable,
083: RePreparable {
084:
085: /* page info this operation changed */
086: private PageKey pageId;
087: private long pageVersion;
088:
089: /* runtime page and data necessary to maintain it */
090: transient protected BasePage page;
091: transient protected RawContainerHandle containerHdl;
092: transient protected boolean foundHere;
093:
094: protected PageBasicOperation(BasePage page) {
095: if (SanityManager.DEBUG) {
096: SanityManager
097: .ASSERT(page != null,
098: "cannot create page operation on a null page pointer");
099: }
100:
101: // runtime info
102: this .page = page;
103:
104: // info which will be logged.
105: pageId = page.getPageId();
106: pageVersion = page.getPageVersion();
107: }
108:
109: // no-arg constructor, required by Formatable
110: public PageBasicOperation() {
111: }
112:
113: public String toString() {
114: if (SanityManager.DEBUG) {
115: return "Page Operation: " + pageId.toString()
116: + " pageVersion " + pageVersion + " : ";
117: } else
118: return null;
119: }
120:
121: /*
122: * Formatable methods
123: */
124:
125: public void writeExternal(ObjectOutput out) throws IOException {
126: pageId.writeExternal(out);
127: CompressedNumber.writeLong(out, pageVersion);
128: }
129:
130: public void readExternal(ObjectInput in) throws IOException,
131: ClassNotFoundException {
132: pageId = PageKey.read(in);
133:
134: pageVersion = CompressedNumber.readLong(in);
135: }
136:
137: /*
138: * Loggable methods
139: */
140:
141: /** Returns true if this op should be redone during recovery redo,
142: if so, get and latched the page.
143:
144: @exception StandardException Standard Cloudscape policy.
145: */
146: public final boolean needsRedo(Transaction xact)
147: throws StandardException {
148: if (findpage(xact) == null) // committed dropped container
149: return false;
150:
151: long pversion = page.getPageVersion();
152: if (pversion == pageVersion)
153: return true;
154:
155: releaseResource(xact);
156:
157: if (pversion > pageVersion)
158: return false;
159: else
160: throw StandardException.newException(
161: SQLState.DATA_MISSING_LOG, pageId, new Long(
162: pversion), new Long(pageVersion));
163: }
164:
165: /** Release latched page and any other resources acquired during a previous
166: findpage, safe to call multiple times.
167:
168: In this RawStore implementataion, resource is acquired by a log
169: operation in one of two places
170: <nl>
171: <li> during runtime or recovery undo in PageOperation.generateUndo()
172: <li> during recovery redo in PageBasicOperation.needsRedo()
173: </nl>
174: */
175: public void releaseResource(Transaction xact) {
176: if (!foundHere) // don't release anything not found by this
177: return;
178:
179: if (page != null) {
180: page.unlatch();
181: page = null;
182: }
183:
184: if (containerHdl != null) {
185: containerHdl.close();
186: containerHdl = null;
187: }
188:
189: foundHere = false;
190: }
191:
192: /**
193: A page operation is a RAWSTORE log record
194: */
195: public int group() {
196: return (Loggable.RAWSTORE | Loggable.XA_NEEDLOCK);
197: }
198:
199: /**
200: the default for optional data is set to null. If an operation has optional data,
201: the operation need to prepare the optional data for this method.
202:
203: WARNING: If a log operation extends this class, and the operation has optional data,
204: it MUST overwrite this method to return a ByteArray that contains the optional data.
205:
206: @exception StandardException Standard Cloudscape policy.
207: */
208: public ByteArray getPreparedLog() throws StandardException {
209: return (ByteArray) null;
210: }
211:
212: /**************************************************************************
213: * Public Methods of RePreparable Interface:
214: **************************************************************************
215: */
216:
217: /**
218: * reclaim locks associated with the changes in this log record.
219: * <p>
220: *
221: * @exception StandardException Standard exception policy.
222: **/
223: public void reclaimPrepareLocks(Transaction t,
224: LockingPolicy locking_policy) throws StandardException {
225: if (SanityManager.DEBUG)
226: SanityManager.DEBUG_PRINT("",
227: "PageBasicOperation.reclaimPrepareLocks().");
228: }
229:
230: /*
231: * Methods specific to this class
232: */
233:
234: /**
235: Reset the pageNumber
236: */
237: protected final void resetPageNumber(long pageNumber) {
238: pageId = new PageKey(pageId.getContainerId(), pageNumber);
239: }
240:
241: protected final PageKey getPageId() {
242: return pageId;
243: }
244:
245: /** Find the page the operation applies to and latch it, this only
246: uses the segmentId, containerId, and pageId stored in this log
247: record to find the page.
248:
249: @return null if container is dropped and committed (possibly
250: stubbified), else return the latched page
251:
252: @exception StandardException Standard Cloudscape policy.
253: */
254: public final BasePage findpage(Transaction xact)
255: throws StandardException {
256: releaseResource(xact);
257:
258: RawTransaction rtran = (RawTransaction) xact;
259: containerHdl = rtran.openDroppedContainer(pageId
260: .getContainerId(), (LockingPolicy) null);
261:
262: if (containerHdl == null) {
263: throw StandardException.newException(
264: SQLState.DATA_CONTAINER_VANISHED, pageId
265: .getContainerId());
266: }
267:
268: foundHere = true;
269:
270: // if container is dropped and committed, cannot look at any page,
271: // it may be a container stub
272: if (containerHdl.getContainerStatus() == RawContainerHandle.COMMITTED_DROP) {
273: releaseResource(xact);
274: return null;
275: }
276:
277: StandardException getPageException = null;
278: try {
279: // get and latch page - we don't know the status of the page or what
280: // kind of page we are looking for, get any type of page
281: page = (BasePage) (containerHdl.getAnyPage(pageId
282: .getPageNumber()));
283: } catch (StandardException se) {
284: getPageException = se;
285: }
286:
287: //Try to initialize the page if page not found exception occurs during
288: //recovery and the page version is zero(Init Page).
289: //We do this if derby.storage.patchInitPageRecoverError is set.
290: if (page == null && getPageException != null
291: && pageVersion == 0)
292: if (PropertyUtil
293: .getSystemBoolean(RawStoreFactory.PATCH_INITPAGE_RECOVER_ERROR))
294: page = getPageForRedoRecovery(xact);
295:
296: // maybe we are in rollforward recovery and this is an init page operation,
297: // give subclass a chance to create the page
298: if (page == null && getPageException != null) {
299: //if are rolloforward recovery reload the page using load tran methods
300: //that initialize the page. because in rollforward recovery, we
301: //might be actually recreating the page container did not exist
302: //in the backup when we started the rollforward recovery.
303:
304: if (rtran.inRollForwardRecovery()) {
305: if (SanityManager.DEBUG)
306: if (SanityManager.DEBUG_ON("LoadTran"))
307: SanityManager
308: .DEBUG_PRINT(
309: "Trace",
310: "got null page "
311: + pageId
312: + " and getPageException, attempt last ditch effort");
313:
314: page = getPageForRedoRecovery(xact);
315:
316: if (SanityManager.DEBUG)
317: if (SanityManager.DEBUG_ON("LoadTran"))
318: SanityManager.DEBUG_PRINT("Trace",
319: " getPageForRedoRecovery, got page="
320: + (page != null));
321: }
322: }
323:
324: if (page == null) {
325: if (getPageException != null) {
326: throw getPageException; // that is the original error
327: } else {
328: throw StandardException.newException(
329: SQLState.DATA_MISSING_PAGE, pageId);
330: }
331: }
332:
333: return page;
334: }
335:
336: /**
337: Subclass (e.g., init page) that wishes to do something about missing
338: pages in load tran should override this method to return the page
339:
340: @exception StandardException Cloudscape Standard error policy
341: */
342: protected BasePage getPageForRedoRecovery(Transaction xact)
343: throws StandardException {
344: return null;
345: }
346:
347: public final Page getPage() {
348: return page;
349: }
350:
351: public final long getPageVersion() {
352: return pageVersion;
353: }
354:
355: /**
356: Undo the change indicated by this log operation and optional data.
357: The page the undo should apply to is the latched undoPage.
358: The undoPage must be the same page as the doMe page and the undo
359: operation must restore the before image of the row that changed.
360:
361: <BR> this can only be used under special circumstances: namely
362: table level locking, and no internal or nested transaction, and all
363: operations are rollec back with restoreMe instead of undoMe.
364:
365: <BR><B>This method is here to support BeforeImageLogging</B>
366:
367: @param xact the Transaction doing the rollback
368: @param undoPage the page to rollback changes on
369: @param CLRinstant the log instant of this (PageUndo) operation
370: @param in optional data for the rollback operation
371:
372: @exception IOException Can be thrown by any of the methods of ObjectInput.
373: @exception StandardException Standard Cloudscape policy.
374: */
375: abstract public void restoreMe(Transaction xact, BasePage undoPage,
376: LogInstant CLRinstant, LimitObjectInput in)
377: throws StandardException, IOException;
378:
379: }
|