001: /*
002:
003: Derby - Class org.apache.derby.impl.store.raw.data.InsertOperation
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: import org.apache.derby.impl.store.raw.data.BasePage;
026: import org.apache.derby.impl.store.raw.data.ReclaimSpace;
027:
028: import org.apache.derby.iapi.services.sanity.SanityManager;
029: import org.apache.derby.iapi.services.io.FormatIdUtil;
030: import org.apache.derby.iapi.services.io.StoredFormatIds;
031:
032: import org.apache.derby.iapi.store.access.conglomerate.LogicalUndo;
033:
034: import org.apache.derby.iapi.store.raw.PageKey;
035: import org.apache.derby.iapi.store.raw.Compensation;
036: import org.apache.derby.iapi.store.raw.Page;
037: import org.apache.derby.iapi.store.raw.RecordHandle;
038: import org.apache.derby.iapi.store.raw.Transaction;
039:
040: import org.apache.derby.iapi.store.raw.log.LogInstant;
041: import org.apache.derby.iapi.store.raw.xact.RawTransaction;
042:
043: import org.apache.derby.iapi.error.StandardException;
044:
045: import org.apache.derby.iapi.types.DataValueDescriptor;
046:
047: import org.apache.derby.iapi.services.io.CompressedNumber;
048: import org.apache.derby.iapi.services.io.FormatableBitSet;
049: import org.apache.derby.iapi.util.ByteArray;
050: import org.apache.derby.iapi.services.io.DynamicByteArrayOutputStream;
051: import org.apache.derby.iapi.services.io.DynamicByteArrayOutputStream;
052:
053: import java.io.OutputStream;
054: import java.io.ObjectOutput;
055: import java.io.ObjectInput;
056: import java.io.IOException;
057: import org.apache.derby.iapi.services.io.LimitObjectInput;
058:
059: /**
060: Represents an insert of a record onto a page.
061:
062: <PRE>
063: @format_id LOGOP_INSERT
064: the formatId is written by FormatIdOutputStream when this object is
065: written out by writeObject
066: @purpose insert a row onto a page
067: @upgrade
068: @disk_layout
069: LogicalPageOperation the superclass
070: doMeSlot(CompressedInt) which slot to operate on
071: insertFlat(byte) to undo with purge or with delete
072:
073: OptionalData The after image of the row to be inserted.
074: @end_format
075: </PRE>
076: @see Page#insertAtSlot
077: */
078: public final class InsertOperation extends LogicalPageOperation {
079:
080: protected int doMeSlot; // insert slot - only valid during doMe()
081: protected byte insertFlag; // see page insertFlag
082:
083: /** next column that need to be inserted. */
084: transient protected int startColumn;
085:
086: transient protected ByteArray preparedLog;
087:
088: // yyz: revisit later, whether we need preparedLog, maybe everything will be prepared...
089: public InsertOperation(RawTransaction t, BasePage page, int slot,
090: int recordId, Object[] row, FormatableBitSet validColumns,
091: LogicalUndo undo, byte insertFlag, int startColumn,
092: boolean isLongColumn, int realStartColumn,
093: DynamicByteArrayOutputStream logBuffer,
094: int realSpaceOnPage, int overflowThreshold)
095: throws StandardException {
096: super (page, undo, recordId);
097:
098: this .doMeSlot = slot;
099: this .insertFlag = insertFlag;
100: this .startColumn = startColumn;
101:
102: try {
103: writeOptionalDataToBuffer(t, logBuffer, row, validColumns,
104: isLongColumn, realStartColumn, realSpaceOnPage,
105: overflowThreshold);
106: } catch (IOException ioe) {
107: throw StandardException.newException(
108: SQLState.DATA_UNEXPECTED_EXCEPTION, ioe);
109: }
110: }
111:
112: /*
113: * Formatable methods
114: */
115:
116: // no-arg constructor, required by Formatable
117: public InsertOperation() {
118: super ();
119: }
120:
121: /**
122: Write this out.
123: @exception IOException error writing to log stream
124: */
125: public void writeExternal(ObjectOutput out) throws IOException {
126: super .writeExternal(out);
127: CompressedNumber.writeInt(out, doMeSlot);
128: out.writeByte(insertFlag);
129:
130: }
131:
132: /**
133: Read this in
134: @exception IOException error reading from log stream
135: @exception ClassNotFoundException log stream corrupted
136: */
137: public void readExternal(ObjectInput in) throws IOException,
138: ClassNotFoundException {
139: super .readExternal(in);
140: doMeSlot = CompressedNumber.readInt(in);
141: insertFlag = in.readByte();
142: }
143:
144: /**
145: Return my format identifier.
146: */
147: public int getTypeFormatId() {
148: return StoredFormatIds.LOGOP_INSERT;
149: }
150:
151: /*
152: * Loggable methods
153: */
154: /**
155: @exception IOException Can be thrown by any of the methods of ObjectInput.
156: @exception StandardException Standard Cloudscape policy.
157:
158: @see org.apache.derby.iapi.store.raw.Loggable#doMe
159: */
160: public void doMe(Transaction xact, LogInstant instant,
161: LimitObjectInput in) throws StandardException, IOException {
162: this .page.storeRecord(instant, doMeSlot, true, in);
163: }
164:
165: /*
166: * PageOperation methods
167: */
168:
169: /**
170: Undo the insert by simply marking the just inserted record as deleted.
171: All logical undo logic has already been taken care of by generateUndo.
172:
173: @exception IOException Can be thrown by any of the methods of ObjectInput.
174: @exception StandardException Standard Cloudscape policy.
175:
176: @see LogicalPageOperation#undoMe
177: */
178: public void undoMe(Transaction xact, BasePage undoPage,
179: int undoRecordId, LogInstant CLRInstant, LimitObjectInput in)
180: throws StandardException, IOException {
181: int slot = undoPage.findRecordById(undoRecordId,
182: Page.FIRST_SLOT_NUMBER);
183:
184: if (SanityManager.DEBUG) {
185: // if the record Id has changed, the page had better changed
186: // this can only happen during recovery since in run time undo,
187: // this resetRecordHandle gets called and this object have the new
188: // page number and recordId
189: if (undoRecordId != this .recordId)
190: if (undoPage.getPageNumber() == getPageId()
191: .getPageNumber())
192: SanityManager.THROWASSERT("recordId changed from "
193: + this .recordId + " to " + undoRecordId
194: + " but page number did not change "
195: + undoPage.getPageNumber());
196:
197: if (slot == -1)
198: SanityManager.THROWASSERT("recordId " + undoRecordId
199: + " not found on page "
200: + undoPage.getPageNumber());
201: }
202:
203: if ((insertFlag & Page.INSERT_UNDO_WITH_PURGE) != 0) {
204: undoPage.purgeRecord(CLRInstant, slot, undoRecordId);
205:
206: RawTransaction rxact = (RawTransaction) xact;
207:
208: // If we purged the last row off an overflow page, reclaim that
209: // page - we have to do this post transaction termination because we
210: // are underneath the log right now and cannot interrupt the log
211: // stream.
212: if (rxact.handlesPostTerminationWork()
213: && undoPage.isOverflowPage()
214: && undoPage.recordCount() == 0) {
215: ReclaimSpace work = new ReclaimSpace(ReclaimSpace.PAGE,
216: (PageKey) undoPage.getIdentity(), rxact
217: .getDataFactory(), true /* service ASAP */);
218: rxact.addPostTerminationWork(work);
219: }
220: } else {
221: undoPage.setDeleteStatus(CLRInstant, slot, true);
222: }
223:
224: undoPage.setAuxObject(null);
225: }
226:
227: /*
228: * LogicalUndoable methods
229: */
230:
231: /**
232: Restore the row stored in the optional data of the log record.
233:
234: @exception IOException error reading from log stream
235: @exception StandardException Standard Cloudscape error policy
236: */
237: public void restoreLoggedRow(Object[] row, LimitObjectInput in)
238: throws StandardException, IOException
239:
240: {
241: Page p = null;
242:
243: try {
244: // the optional data is written by the page in the same format it
245: // stores record on the page,
246: // only a page knows how to restore a logged row back to a storable row
247: // first get the page where the insert went even though the row may no
248: // longer be there
249: p = getContainer().getPage(getPageId().getPageNumber());
250:
251: ((BasePage) p).restoreRecordFromStream(in, row);
252:
253: } finally {
254:
255: if (p != null) {
256: p.unlatch();
257: p = null;
258: }
259: }
260: }
261:
262: /*
263: * method to support BeforeImageLogging
264: */
265:
266: /**
267: * restore the before image of the page
268: *
269: * @exception StandardException Standard Cloudscape Error Policy
270: * @exception IOException problem reading the complete log record from the
271: * input stream
272: */
273:
274: public void restoreMe(Transaction xact, BasePage undoPage,
275: LogInstant CLRinstant, LimitObjectInput in)
276: throws StandardException, IOException {
277: if (SanityManager.DEBUG) {
278: int slot = undoPage.findRecordById(recordId,
279: Page.FIRST_SLOT_NUMBER);
280:
281: if (!getPageId().equals(undoPage.getPageId()))
282: SanityManager
283: .THROWASSERT("restoreMe cannot restore to a different page. "
284: + "doMe page:"
285: + getPageId()
286: + " undoPage:" + undoPage.getPageId());
287: if (slot != doMeSlot)
288: SanityManager
289: .THROWASSERT("restoreMe cannot restore to a different slot. "
290: + "doMe slot:"
291: + doMeSlot
292: + " undoMe slot: "
293: + slot
294: + " recordId:" + recordId);
295: }
296: insertFlag |= Page.INSERT_UNDO_WITH_PURGE;
297:
298: // undo the insert with purge.
299: undoMe(xact, undoPage, recordId, CLRinstant, in);
300: }
301:
302: /*
303: methods to support prepared log
304:
305: the following two methods should not be called during recover
306: */
307:
308: public ByteArray getPreparedLog() {
309: return (this .preparedLog);
310: }
311:
312: public int getNextStartColumn() {
313: return (this .startColumn);
314: }
315:
316: /**
317: Writes out the row that is to be inserted as the optional data.
318:
319: @exception IOException Can be thrown by any of the methods of ObjectOutput
320: @exception StandardException Standard Cloudscape policy.
321: */
322: private void writeOptionalDataToBuffer(RawTransaction t,
323: DynamicByteArrayOutputStream logBuffer, Object[] row,
324: FormatableBitSet validColumns, boolean isLongColumn,
325: int realStartColumn, int realSpaceOnPage,
326: int overflowThreshold) throws StandardException,
327: IOException {
328:
329: if (SanityManager.DEBUG) {
330: SanityManager.ASSERT(this .page != null);
331: }
332:
333: DynamicByteArrayOutputStream localLogBuffer = null;
334: if (logBuffer != null) {
335: localLogBuffer = (DynamicByteArrayOutputStream) logBuffer;
336: } else {
337: realStartColumn = -1;
338: realSpaceOnPage = -1;
339: localLogBuffer = t.getLogBuffer();
340: }
341:
342: if (isLongColumn) {
343: this .startColumn = this .page.logLongColumn(doMeSlot,
344: recordId, row[0], localLogBuffer);
345: } else {
346: this .startColumn = this .page.logRow(doMeSlot, true,
347: recordId, row, validColumns, localLogBuffer,
348: this .startColumn, insertFlag, realStartColumn,
349: realSpaceOnPage, overflowThreshold);
350: }
351:
352: int optionalDataStart = localLogBuffer.getBeginPosition();
353: int optionalDataLength = localLogBuffer.getPosition()
354: - optionalDataStart;
355:
356: this .preparedLog = new ByteArray(localLogBuffer.getByteArray(),
357: optionalDataStart, optionalDataLength);
358: }
359:
360: /**
361: DEBUG: Print self.
362: */
363: public String toString() {
364: if (SanityManager.DEBUG) {
365: return super .toString() + "Insert : " + " Slot=" + doMeSlot
366: + " recordId=" + recordId;
367: } else
368: return null;
369: }
370:
371: }
|