001: /*
002:
003: Derby - Class org.apache.derby.impl.store.raw.data.UpdateOperation
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.error.StandardException;
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:
039: import org.apache.derby.iapi.store.raw.log.LogInstant;
040: import org.apache.derby.iapi.store.raw.xact.RawTransaction;
041:
042: import org.apache.derby.iapi.types.DataValueDescriptor;
043:
044: import org.apache.derby.iapi.services.io.FormatableBitSet;
045: import org.apache.derby.iapi.util.ByteArray;
046: import org.apache.derby.iapi.services.io.CompressedNumber;
047: import org.apache.derby.iapi.services.io.DynamicByteArrayOutputStream;
048: import org.apache.derby.iapi.services.io.DynamicByteArrayOutputStream;
049:
050: import java.io.OutputStream;
051: import java.io.ObjectOutput;
052: import java.io.ObjectInput;
053: import java.io.IOException;
054: import org.apache.derby.iapi.services.io.LimitObjectInput;
055:
056: /**
057: Represents the update of a particular row on a page.
058:
059: <PRE>
060: @format_id LOGOP_UPDATE
061: the formatId is written by FormatIdOutputStream when this object is
062: written out by writeObject
063: @purpose update a record on the page
064: @upgrade
065: @disk_layout
066: PhysicalPageOperation the super class
067: doMeSlot(CompressedInt) the slot the updated record is in
068: recordId(CompressedInt) the recordId of the updated record
069:
070: OptionalData The new image of the record (length included),
071: follow by the old image of the record (length included)
072: @end_format
073: </PRE>
074: */
075:
076: public final class UpdateOperation extends PhysicalPageOperation {
077:
078: protected int doMeSlot; // record slot - only valid during a doMe() operation
079: protected int recordId; // record id
080: transient protected int nextColumn; // next column that needs to be updated in a row.
081:
082: transient protected ByteArray preparedLog;
083:
084: public UpdateOperation(RawTransaction t, BasePage page, int slot,
085: int recordId, Object[] row, FormatableBitSet validColumns,
086: int realStartColumn,
087: DynamicByteArrayOutputStream logBuffer,
088: int realSpaceOnPage, RecordHandle headRowHandle)
089: throws StandardException {
090: super (page);
091:
092: this .doMeSlot = slot;
093: this .recordId = recordId;
094: this .nextColumn = -1;
095:
096: // RESOLVE SRW-DJD/YYZ
097: try {
098: writeOptionalDataToBuffer(t,
099: (DynamicByteArrayOutputStream) logBuffer, row,
100: validColumns, realStartColumn, realSpaceOnPage,
101: headRowHandle);
102: } catch (IOException ioe) {
103: throw StandardException.newException(
104: SQLState.DATA_UNEXPECTED_EXCEPTION, ioe);
105: }
106:
107: }
108:
109: /*
110: * Formatable methods
111: */
112:
113: // no-arg constructor, required by Formatable
114: public UpdateOperation() {
115: super ();
116: }
117:
118: public void writeExternal(ObjectOutput out) throws IOException {
119: super .writeExternal(out);
120: CompressedNumber.writeInt(out, doMeSlot);
121: CompressedNumber.writeInt(out, recordId);
122: }
123:
124: /**
125: Read this in
126: @exception IOException error reading from log stream
127: @exception ClassNotFoundException log stream corrupted
128: */
129: public void readExternal(ObjectInput in) throws IOException,
130: ClassNotFoundException {
131: super .readExternal(in);
132: doMeSlot = CompressedNumber.readInt(in);
133: recordId = CompressedNumber.readInt(in);
134: }
135:
136: /**
137: Return my format identifier.
138: */
139: public int getTypeFormatId() {
140: return StoredFormatIds.LOGOP_UPDATE;
141: }
142:
143: /**
144: Return the last column of the row this operation logged
145: */
146: public int getNextStartColumn() {
147: return nextColumn;
148: }
149:
150: /*
151: * Loggable methods
152: */
153: /**
154: Store the new record directly over the old record, the implementation
155: of storeRecord is responsible for removing any old data.
156:
157: @exception StandardException Thrown by methods I call
158: @exception IOException Thrown by methods I call
159:
160: @see BasePage#storeRecord
161: @see org.apache.derby.iapi.store.raw.Loggable#doMe
162: */
163: public void doMe(Transaction xact, LogInstant instant,
164: LimitObjectInput in) throws StandardException, IOException {
165: this .page.storeRecord(instant, doMeSlot, false, in);
166: }
167:
168: /*
169: * PhysicalPageOperation methods
170: */
171:
172: /**
173: Store the old record directly over the new record, the implementation
174: of storeRecord is responsible for removing any new data.
175:
176: @exception StandardException Thrown by methods I call
177: @exception IOException Thrown by methods I call
178:
179: @see BasePage#storeRecord
180: @see PhysicalPageOperation#undoMe
181: */
182: public void undoMe(Transaction xact, BasePage undoPage,
183: LogInstant CLRInstant, LimitObjectInput in)
184: throws StandardException, IOException {
185:
186: int slot = undoPage.findRecordById(recordId,
187: Page.FIRST_SLOT_NUMBER);
188:
189: // skip the after image of the record
190: undoPage.skipRecord(in);
191:
192: undoPage.storeRecord(CLRInstant, slot, false, in);
193: undoPage.setAuxObject(null);
194: }
195:
196: /*
197: methods to support prepared log
198:
199: the following two methods should not be called during recover
200: */
201:
202: public ByteArray getPreparedLog() {
203: return (this .preparedLog);
204: }
205:
206: /**
207: Write out the changed colums of new record (from the row) followed by
208: changed columns of the old record (from the page).
209:
210: @exception StandardException Thrown by methods I call
211: @exception IOException Thrown by methods I call
212: */
213: private void writeOptionalDataToBuffer(RawTransaction t,
214: DynamicByteArrayOutputStream logBuffer, Object[] row,
215: FormatableBitSet validColumns, int realStartColumn,
216: int realSpaceOnPage, RecordHandle headRowHandle)
217: throws StandardException, IOException {
218:
219: if (SanityManager.DEBUG) {
220: SanityManager.ASSERT(this .page != null);
221: }
222:
223: if (realStartColumn == (-1)) {
224: logBuffer = t.getLogBuffer();
225: }
226:
227: int optionalDataStart = logBuffer.getPosition();
228:
229: if (SanityManager.DEBUG) {
230:
231: SanityManager
232: .ASSERT(
233: (realStartColumn != -1 || optionalDataStart == 0),
234: "Buffer for writing optional data should start at position 0");
235: }
236:
237: this .nextColumn = this .page.logRow(doMeSlot, false, recordId,
238: row, validColumns, logBuffer, 0, Page.INSERT_OVERFLOW,
239: realStartColumn, realSpaceOnPage, 100);
240:
241: FormatableBitSet loggedColumns = validColumns;
242:
243: // If this update results in moving columns off the current page to
244: // another page, then we must log the before image values of the columns
245: // being moved (deleted from this page) in addition to logging the
246: // columns actually being changed as part of the update.
247:
248: if ((nextColumn != -1) && (validColumns != null)) {
249: // if nextColumn is not -1, then this must be an update which moves
250: // columns off of the current page. If validColumns == null then
251: // we are logging all of the before image columns anyway.
252:
253: // get total number of fields of the old record.
254: int numberFields = page.getHeaderAtSlot(doMeSlot)
255: .getNumberFields();
256:
257: // create new bit map, copying all bits that were set in original
258: loggedColumns = new FormatableBitSet(validColumns);
259:
260: // make sure there is room in the bit map to add the columns being
261: // deleted from the end of the row.
262: // The important thing is that endField must be at least as big as
263: // the number of columns in the entire record (including previous
264: // pages of a long row) up to the end of this page.
265: int endField = nextColumn + numberFields;
266: loggedColumns.grow(endField);
267: // now include all columns being deleted.
268: // This actually sets too many bits in this bit set but
269: // logRecord will just ignore the extra bits.
270: for (int i = nextColumn; i < endField; i++) {
271: loggedColumns.set(i);
272: }
273: }
274:
275: // log the old version of the changed data
276: this .page.logRecord(doMeSlot, BasePage.LOG_RECORD_FOR_UPDATE,
277: recordId, loggedColumns, logBuffer, headRowHandle);
278:
279: // get length of all the optional data.
280: optionalDataStart = logBuffer.getBeginPosition();
281: int optionalDataLength = logBuffer.getPosition()
282: - optionalDataStart;
283:
284: // set the position to the beginning of the buffer
285: logBuffer.setPosition(optionalDataStart);
286:
287: this .preparedLog = new ByteArray(logBuffer.getByteArray(),
288: optionalDataStart, optionalDataLength);
289: }
290:
291: /*
292: * PageBasicOperation
293: */
294:
295: /**
296: * restore the before image of the page
297: *
298: * @exception StandardException Standard Cloudscape Error Policy
299: * @exception IOException problem reading the complete log record from the
300: * input stream
301: */
302: public void restoreMe(Transaction xact, BasePage undoPage,
303: LogInstant CLRInstant, LimitObjectInput in)
304: throws StandardException, IOException {
305: undoMe(xact, undoPage, CLRInstant, in);
306: }
307:
308: public String toString() {
309: if (SanityManager.DEBUG) {
310: return super .toString() + "Update " + " Slot=" + doMeSlot
311: + " recordId=" + recordId;
312: } else
313: return null;
314: }
315: }
|