001: /*
002:
003: Derby - Class org.apache.derby.impl.store.access.btree.index.B2IUndo
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.access.btree.index;
023:
024: import org.apache.derby.iapi.reference.SQLState;
025:
026: import org.apache.derby.iapi.services.io.ArrayInputStream;
027:
028: import org.apache.derby.iapi.services.sanity.SanityManager;
029:
030: import org.apache.derby.iapi.services.io.Formatable;
031: import org.apache.derby.iapi.services.io.FormatIdUtil;
032: import org.apache.derby.iapi.services.io.StoredFormatIds;
033: import org.apache.derby.iapi.error.StandardException;
034:
035: import org.apache.derby.iapi.store.access.conglomerate.LogicalUndo;
036: import org.apache.derby.iapi.store.access.conglomerate.TransactionManager;
037:
038: import org.apache.derby.iapi.store.access.RowUtil;
039: import org.apache.derby.iapi.store.access.DynamicCompiledOpenConglomInfo;
040: import org.apache.derby.iapi.store.access.Qualifier;
041: import org.apache.derby.iapi.store.access.ScanController;
042: import org.apache.derby.iapi.store.access.TransactionController;
043:
044: import org.apache.derby.iapi.store.raw.ContainerHandle;
045: import org.apache.derby.iapi.store.raw.FetchDescriptor;
046: import org.apache.derby.iapi.store.raw.LockingPolicy;
047: import org.apache.derby.iapi.store.raw.LogicalUndoable;
048: import org.apache.derby.iapi.store.raw.Page;
049: import org.apache.derby.iapi.store.raw.RecordHandle;
050: import org.apache.derby.iapi.store.raw.Transaction;
051:
052: import org.apache.derby.iapi.types.DataValueDescriptor;
053:
054: // imports of inherited impl's
055: import org.apache.derby.impl.store.access.btree.BTree;
056: import org.apache.derby.impl.store.access.btree.BTreeLockingPolicy;
057: import org.apache.derby.impl.store.access.btree.ControlRow;
058: import org.apache.derby.impl.store.access.btree.OpenBTree;
059: import org.apache.derby.impl.store.access.btree.SearchParameters;
060:
061: import java.io.ObjectInput;
062: import java.io.IOException;
063: import java.io.ObjectOutput;
064: import org.apache.derby.iapi.services.io.LimitObjectInput;
065:
066: import org.apache.derby.iapi.services.io.FormatableBitSet;
067:
068: /**
069: * @format_id ACCESS_B2IUNDO_V1_ID
070: *
071: * @purpose Implements the LogicalUndo and Formatable interfaces, basically
072: * providing a way for raw store recovery to "call back" access code
073: * to provide logical undo ability.
074: *
075: * @upgrade RESOLVE.
076: *
077: * @disk_layout
078: * No state associated with this format.
079: *
080: **/
081:
082: /**
083:
084: The B2IUndo interface packages up the routines which the rawstore needs
085: to call to perform logical undo of a record in a B2i. The rawstore will
086: determine that a page has changed since the record was written, and if it
087: has it will call the findUndo() interface, to find the page where the record
088: exists (as it may have moved).
089: <p>
090: This class must not contain any persistent state, as this class is stored
091: in the log record of the insert/delete.
092:
093: @see org.apache.derby.iapi.store.raw.LogicalUndoable
094: @see org.apache.derby.iapi.store.raw.Undoable#generateUndo
095: **/
096: public class B2IUndo implements LogicalUndo, Formatable {
097: /**
098: * Find the page and record to undo. If no logical undo is necessary,
099: * i.e., row has not moved, then just return the latched page where undo
100: * should go. If the record has moved, it has a new recordId on the new
101: * page, this routine needs to call pageOp.resetRecord with the new
102: * RecordHandle so that the logging system can update the compensation
103: * Operation with the new location.
104: *
105: * @param rawtran the transaction doing the rollback
106: * @param pageOp the page operation that supports logical undo. This
107: * LogicalUndo function pointer is a field of that
108: * pageOperation
109: * @param in data stored in the log stream that contains the record
110: * data necessary to restore the row.
111: *
112: * @exception StandardException Standard Cloudscape error policy
113: * @exception IOException Method may read from InputStream
114: *
115: */
116: public Page findUndo(Transaction rawtran, LogicalUndoable pageOp,
117: LimitObjectInput in) throws StandardException, IOException {
118: ControlRow root = null;
119: ControlRow control_row = null;
120: DataValueDescriptor[] logged_index_row_template = null;
121: DataValueDescriptor[] template = null;
122: Page ret_page = null;
123: ContainerHandle container = pageOp.getContainer();
124: RecordHandle rechandle = pageOp.getRecordHandle();
125: boolean ok_exit = false;
126: int compare_result = 1;
127: B2I btree = null;
128:
129: // Open the btree to associate the open contain handle, thus the
130: // current xact with all subsequent operations during undo.
131:
132: try {
133:
134: // Need Conglomerate to create templates - get from the root page.
135: root = ControlRow.Get(container, BTree.ROOTPAGEID);
136:
137: if (SanityManager.DEBUG)
138: SanityManager.ASSERT(root.getPage().isLatched());
139:
140: btree = (B2I) root.getConglom(B2I.FORMAT_NUMBER);
141:
142: if (SanityManager.DEBUG)
143: SanityManager.ASSERT(btree instanceof B2I);
144:
145: // create a template for the logged index row from the conglomerate.
146: logged_index_row_template = btree.createTemplate();
147:
148: // create a template for the page index row from the conglomerate.
149: template = btree.createTemplate();
150: } finally {
151: if (root != null)
152: root.release();
153: }
154:
155: // Get logged row from record.
156: pageOp.restoreLoggedRow(logged_index_row_template, in);
157:
158: // RESOLVE (mikem) - currently restoreLoggedRow() may latch and unlatch
159: // a page in the container (see ST059).
160: // Now get the page where the record used to be.
161:
162: ok_exit = false;
163: try {
164: // "open" the btree, using recovery's already opened container
165: OpenBTree open_btree = new OpenBTree();
166:
167: open_btree.init(
168: (TransactionManager) null, // current user xact - not needed
169: (TransactionManager) null, // current xact - not needed
170: pageOp.getContainer(), // recovery already opened container
171: rawtran, false,
172: ContainerHandle.MODE_FORUPDATE,
173: // open_mode not used - container is
174: // already opened.
175: TransactionManager.MODE_NONE,
176: (BTreeLockingPolicy) null, // don't get locks during undo
177: btree, (LogicalUndo) null, // no logical undo necessary, as
178: // this code only does read.
179: (DynamicCompiledOpenConglomInfo) null);
180:
181: // System.out.println(
182: // "calling logical undo, recordhandle = " + rechandle);
183: // System.out.println("calling logical undo, record= " +
184: // logged_index_row_template);
185:
186: // Get the page where the record was originally, before splits
187: // could have possibly moved it.
188: control_row = ControlRow.Get(open_btree, rechandle
189: .getPageNumber());
190:
191: // init compare_result, if record doesn't exist do the search
192: compare_result = 1;
193:
194: if (control_row.getPage().recordExists(rechandle, true)) {
195:
196: if (SanityManager.DEBUG) {
197: SanityManager
198: .ASSERT(control_row.getPage()
199: .fetchNumFields(rechandle) == logged_index_row_template.length);
200: }
201:
202: // create template for the page index row from the conglomerate.
203: RecordHandle ret_rechandle = control_row.getPage()
204: .fetchFromSlot(
205: (RecordHandle) null,
206: control_row.getPage().getSlotNumber(
207: rechandle), template,
208: (FetchDescriptor) null, true);
209:
210: // compare the 2 rows, and if they are the same then the raw
211: // store has the right page and record and there is no work to
212: // be done (this is usual case).
213: compare_result = ControlRow.CompareIndexRowToKey(
214: template, logged_index_row_template,
215: logged_index_row_template.length, 1, open_btree
216: .getColumnSortOrderInfo());
217: }
218:
219: if (compare_result == 0) {
220: ret_page = control_row.getPage();
221: } else {
222: // if the 2 don't compare equal, search the btree from the root
223: // for the logged row, find the leaf, reset the row for the raw
224: // store, and return the new page latched.
225:
226: // Create the objects needed for the insert.
227: SearchParameters sp = new SearchParameters(
228: logged_index_row_template, ScanController.GE,
229: template, open_btree, false);
230:
231: control_row.release();
232: control_row = null;
233: control_row = ControlRow.Get(open_btree,
234: BTree.ROOTPAGEID).search(sp);
235:
236: if (!sp.resultExact) {
237: if (SanityManager.DEBUG) {
238: SanityManager
239: .THROWASSERT("B2IUndo - could not find key being searched for:"
240: + ";key = "
241: + RowUtil
242: .toString(logged_index_row_template)
243: + ";sp = "
244: + sp
245: + "control_row = "
246: + control_row
247: + "control_row.debugPage() = "
248: + control_row
249: .debugPage(open_btree)
250: + "control_row.getPage() = "
251: + control_row.getPage());
252: }
253:
254: throw StandardException
255: .newException(SQLState.BTREE_ROW_NOT_FOUND_DURING_UNDO);
256: } else {
257: RecordHandle rh = control_row.getPage()
258: .fetchFromSlot((RecordHandle) null,
259: sp.resultSlot,
260: new DataValueDescriptor[0],
261: (FetchDescriptor) null, true);
262:
263: pageOp.resetRecordHandle(rh);
264:
265: ret_page = control_row.getPage();
266: }
267: }
268: ok_exit = true;
269: } finally {
270: //System.out.println("B2iUndo returning with rec handle: " +
271: // pageOp.getRecordHandle());
272: if ((!ok_exit) && (control_row != null))
273: control_row.release();
274: }
275:
276: return (ret_page);
277: }
278:
279: /**
280: Return my format identifier.
281:
282: @see org.apache.derby.iapi.services.io.TypedFormat#getTypeFormatId
283: */
284: public int getTypeFormatId() {
285: return StoredFormatIds.ACCESS_B2IUNDO_V1_ID;
286: }
287:
288: /**
289: This object has no state, so nothing to write.*/
290:
291: public void writeExternal(ObjectOutput out) throws IOException {
292: return;
293: }
294:
295: /**
296: Restore the in-memory representation from the stream.
297:
298: This object has no state, so nothing to restore.
299: @exception ClassNotFoundException Thrown if the stored representation is
300: serialized and a class named in the stream could not be found.
301:
302: @see java.io.Externalizable#readExternal
303: */
304: public void readExternal(ObjectInput in) throws IOException,
305: ClassNotFoundException {
306: return;
307: }
308:
309: public void readExternal(ArrayInputStream in) throws IOException,
310: ClassNotFoundException {
311: return;
312: }
313: }
|