001: /*
002:
003: Derby - Class org.apache.derby.impl.store.raw.data.UpdateFieldOperation
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.services.sanity.SanityManager;
029:
030: import org.apache.derby.iapi.services.io.FormatIdUtil;
031: import org.apache.derby.iapi.services.io.StoredFormatIds;
032:
033: import org.apache.derby.iapi.store.access.conglomerate.LogicalUndo;
034:
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.xact.RawTransaction;
039:
040: import org.apache.derby.iapi.store.raw.log.LogInstant;
041:
042: import org.apache.derby.iapi.error.StandardException;
043:
044: import org.apache.derby.iapi.types.DataValueDescriptor;
045:
046: import org.apache.derby.iapi.services.io.FormatableBitSet;
047: import org.apache.derby.iapi.services.io.CompressedNumber;
048: import org.apache.derby.iapi.util.ByteArray;
049: import org.apache.derby.iapi.services.io.DynamicByteArrayOutputStream;
050:
051: import java.io.OutputStream;
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: Represents the update of a particular field of a row on a page.
059:
060: <PRE>
061: @format_id LOGOP_UPDATE_FIELD
062: the formatId is written by FormatIdOutputStream when this object is
063: written out by writeObject
064: @purpose update a field of a record on the page
065: @upgrade
066: @disk_layout
067: LogicalPageOperation the super class
068: doMeSlot(CompressedInt) the slot of the record being updated
069: fieldId(CompressedInt) the recordId of the record being updated
070:
071: OptionalData The after image of the column (length included),
072: follow by the old image of the record (length
073: included). If this is logically undoable, then the
074: before image of the entire row is logged
075: @end_format
076: </PRE>
077: */
078: public final class UpdateFieldOperation extends LogicalPageOperation {
079:
080: protected int doMeSlot; // insert slot - only valid during a doMe() operation
081: protected int fieldId;
082:
083: transient protected ByteArray preparedLog;
084:
085: public UpdateFieldOperation(RawTransaction t, BasePage page,
086: int slot, int recordId, int fieldId, Object column,
087: LogicalUndo undo) throws StandardException {
088: super (page, undo, recordId);
089:
090: this .doMeSlot = slot;
091: this .fieldId = fieldId;
092:
093: try {
094: writeOptionalDataToBuffer(t, column);
095: } catch (IOException ioe) {
096: throw StandardException.newException(
097: SQLState.DATA_UNEXPECTED_EXCEPTION, ioe);
098: }
099: }
100:
101: /*
102: * Formatable methods
103: */
104:
105: // no-arg constructor, required by Formatable
106: public UpdateFieldOperation() {
107: super ();
108: }
109:
110: public void writeExternal(ObjectOutput out) throws IOException {
111: super .writeExternal(out);
112: CompressedNumber.writeInt(out, doMeSlot);
113: CompressedNumber.writeInt(out, fieldId);
114: }
115:
116: /**
117: Read this in
118: @exception IOException error reading from log stream
119: @exception ClassNotFoundException log stream corrupted
120: */
121: public void readExternal(ObjectInput in) throws IOException,
122: ClassNotFoundException {
123: super .readExternal(in);
124: doMeSlot = CompressedNumber.readInt(in);
125: fieldId = CompressedNumber.readInt(in);
126: }
127:
128: /**
129: Return my format identifier.
130: */
131: public int getTypeFormatId() {
132: return StoredFormatIds.LOGOP_UPDATE_FIELD;
133: }
134:
135: /*
136: * Loggable methods
137: */
138: /**
139: Change the value of a field.
140:
141: @exception IOException Can be thrown by any of the methods of ObjectInput.
142: @exception StandardException Standard Cloudscape policy.
143:
144: @see org.apache.derby.iapi.store.raw.Loggable#doMe
145: */
146: public void doMe(Transaction xact, LogInstant instant,
147: LimitObjectInput in) throws StandardException, IOException {
148: this .page.storeField(instant, doMeSlot, fieldId, in);
149: }
150:
151: /*
152: * Undoable methods
153: */
154:
155: /**
156: Restore field to its old value.
157:
158: @exception IOException Can be thrown by any of the methods of ObjectInput.
159: @exception StandardException Standard Cloudscape policy.
160:
161: @see LogicalPageOperation#undoMe
162: */
163: public void undoMe(Transaction xact, BasePage undoPage,
164: int undoRecordId, LogInstant CLRInstant, LimitObjectInput in)
165: throws StandardException, IOException {
166: int slot = undoPage.findRecordById(undoRecordId,
167: Page.FIRST_SLOT_NUMBER);
168:
169: if (SanityManager.DEBUG) {
170: // if the record Id has changed, the page had better changed
171: // this can only happen during recovery since in run time undo,
172: // this resetRecordHandle gets called and this object have the new
173: // page number and recordId
174: if (undoRecordId != this .recordId)
175: if (undoPage.getPageNumber() == getPageId()
176: .getPageNumber())
177: SanityManager.THROWASSERT("recordId changed from "
178: + this .recordId + " to " + undoRecordId
179: + " but page number did not change "
180: + undoPage.getPageNumber());
181:
182: if (slot == -1)
183: SanityManager.THROWASSERT("recordId " + undoRecordId
184: + " not found on page "
185: + undoPage.getPageNumber());
186: }
187:
188: undoPage.skipField((java.io.ObjectInput) in); // skip the after image of the column
189: undoPage.storeField(CLRInstant, slot, fieldId, in);
190: undoPage.setAuxObject(null);
191: }
192:
193: /*
194: * LogicalUndoable methods
195: */
196:
197: /**
198: Restore the row stored in the optional data of the log record.
199:
200: @exception IOException error reading from log stream
201: @exception StandardException Standard Cloudscape error policy
202: */
203: public void restoreLoggedRow(Object[] row, LimitObjectInput in)
204: throws StandardException, IOException {
205: BasePage p = null;
206:
207: try {
208: // the optional data is written by the page in the same format it
209: // stores record on the page,
210: // only a page knows how to restore a logged row back to a storable row
211: // first get the page where the insert went even though the row may no
212: // longer be there
213: p = (BasePage) (getContainer().getPage(getPageId()
214: .getPageNumber()));
215:
216: // skip over the before and after image of the column, position the
217: // input stream at the entire row
218: p.skipField(in); // AI of the column
219: p.skipField(in); // BI of the column
220:
221: p.restoreRecordFromStream(in, row);
222:
223: // RESOLVE: this returns the BI of the row, what we need is the AI
224: // of the row. We need to someone splice in the AI of the column
225: // into the storable row.
226:
227: } finally {
228:
229: if (p != null) {
230: p.unlatch();
231: p = null;
232: }
233: }
234: }
235:
236: /*
237: * method to support BeforeImageLogging
238: */
239:
240: /**
241: * restore the before image of the page
242: *
243: * @exception StandardException Standard Cloudscape Error Policy
244: * @exception IOException problem reading the complete log record from the
245: * input stream
246: */
247: public void restoreMe(Transaction xact, BasePage undoPage,
248: LogInstant CLRInstant, LimitObjectInput in)
249: throws StandardException, IOException {
250: int slot = undoPage.findRecordById(recordId,
251: Page.FIRST_SLOT_NUMBER);
252: if (SanityManager.DEBUG) {
253: if (!getPageId().equals(undoPage.getPageId()))
254: SanityManager
255: .THROWASSERT("restoreMe cannot restore to a different page. "
256: + "doMe page:"
257: + getPageId()
258: + " undoPage:" + undoPage.getPageId());
259: if (slot != doMeSlot)
260: SanityManager
261: .THROWASSERT("restoreMe cannot restore to a different slot. "
262: + "doMe slot:"
263: + doMeSlot
264: + " undoMe slot: "
265: + slot
266: + " recordId:" + recordId);
267: }
268:
269: undoPage.skipField(in); // skip the after image of the column
270: undoPage.storeField(CLRInstant, slot, fieldId, in);
271: undoPage.setAuxObject(null);
272: }
273:
274: /*
275: methods to support prepared log
276:
277: the following two methods should not be called during recover
278: */
279:
280: public ByteArray getPreparedLog() {
281: return (this .preparedLog);
282: }
283:
284: /**
285: Write the old column value and and new column value as optional data.
286: If logical undo, writes out the entire row's before image.
287:
288: @exception IOException Can be thrown by any of the methods of ObjectOutput.
289: @exception StandardException Standard Cloudscape policy.
290: */
291: private void writeOptionalDataToBuffer(RawTransaction t,
292: Object column) throws StandardException, IOException {
293:
294: if (SanityManager.DEBUG) {
295: SanityManager.ASSERT(this .page != null);
296: }
297:
298: DynamicByteArrayOutputStream logBuffer = t.getLogBuffer();
299: int optionalDataStart = logBuffer.getPosition();
300:
301: if (SanityManager.DEBUG) {
302: SanityManager
303: .ASSERT(optionalDataStart == 0,
304: "Buffer for writing optional data should start at position 0");
305: }
306:
307: this .page.logColumn(doMeSlot, fieldId, column, logBuffer, 100); // the after image of the column
308: this .page.logField(doMeSlot, fieldId, logBuffer); // the BI of the column
309: if (undo != null) {
310: // RESOLVE: we want the AFTER image of the row, not the BEFORE
311: // image. This works for now because only btree needs a logical
312: // undoable updateField and it always update only the pointer field
313: // to point to something else.
314: //
315: // But in the future, it needs to be changed.
316:
317: this .page.logRecord(doMeSlot, BasePage.LOG_RECORD_DEFAULT,
318: recordId, (FormatableBitSet) null, logBuffer,
319: (RecordHandle) null);
320: // log the BI of the entire row
321:
322: }
323:
324: int optionalDataLength = logBuffer.getPosition()
325: - optionalDataStart;
326:
327: if (SanityManager.DEBUG) {
328: if (optionalDataLength != logBuffer.getUsed())
329: SanityManager
330: .THROWASSERT("wrong optional data length, optionalDataLength = "
331: + optionalDataLength
332: + ", logBuffer.getUsed() = "
333: + logBuffer.getUsed());
334: }
335:
336: // set the position to the beginning of the buffer
337: logBuffer.setPosition(optionalDataStart);
338:
339: this .preparedLog = new ByteArray(logBuffer.getByteArray(),
340: optionalDataStart, optionalDataLength);
341: }
342:
343: /**
344: DEBUG: Print self.
345: */
346: public String toString() {
347: if (SanityManager.DEBUG) {
348: return super .toString() + "UpdateField : " + " Slot="
349: + doMeSlot + " recordId=" + recordId + " fieldId="
350: + fieldId;
351: } else
352: return null;
353: }
354:
355: }
|