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