001: /*
002:
003: Derby - Class org.apache.derby.impl.store.raw.data.PurgeOperation
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.io.FormatIdUtil;
029: import org.apache.derby.iapi.services.io.StoredFormatIds;
030: import org.apache.derby.iapi.services.io.Storable;
031:
032: import org.apache.derby.iapi.store.raw.Page;
033: import org.apache.derby.iapi.store.raw.RecordHandle;
034: import org.apache.derby.iapi.store.raw.Transaction;
035: import org.apache.derby.iapi.store.raw.xact.RawTransaction;
036:
037: import org.apache.derby.iapi.store.raw.log.LogInstant;
038:
039: import org.apache.derby.iapi.error.StandardException;
040: import org.apache.derby.iapi.services.sanity.SanityManager;
041:
042: import org.apache.derby.iapi.services.io.CompressedNumber;
043: import org.apache.derby.iapi.services.io.FormatableBitSet;
044: import org.apache.derby.iapi.util.ByteArray;
045: import org.apache.derby.iapi.services.io.DynamicByteArrayOutputStream;
046:
047: import java.io.OutputStream;
048: import java.io.ObjectOutput;
049: import java.io.ObjectInput;
050: import java.io.IOException;
051: import org.apache.derby.iapi.services.io.LimitObjectInput;
052:
053: /**
054: USE WITH EXTREME Caution: Purge records from a Page.
055:
056: Represents purging of a range of rows from the page.
057:
058: <PRE>
059: @format_id LOGOP_PURGE
060: the formatId is written by FormatIdOutputStream when this object is
061: written out by writeObject
062: @purpose purge num_rows from the page
063: @upgrade
064: @disk_layout
065: PagePhysicalOperation the super class
066: slot(CompressedInt) the slot to start purging
067: num_rows(CompressedInt) number of rows rows to purge
068: recordIds(CompressedInt[num_rows]) the recordIds of the purged rows
069:
070: OptionalData the before images of the rows that were purged
071: @end_format
072: </PRE>
073:
074: @see Page#purgeAtSlot
075: */
076: public final class PurgeOperation extends PhysicalPageOperation {
077:
078: protected int slot; // purge num_rows records starting at this slot
079: // caller must guarentee that during undo of the
080: // log record, this slot is the correct slot to
081: // re-insert the purged record
082: protected int num_rows;
083: protected int[] recordIds; // record Id
084:
085: transient protected ByteArray preparedLog;
086:
087: public PurgeOperation(RawTransaction t, BasePage page, int slot,
088: int num_rows, int[] recordIds, boolean needDataLogged)
089: throws StandardException {
090: super (page);
091:
092: this .slot = slot;
093: this .num_rows = num_rows;
094: this .recordIds = recordIds;
095:
096: try {
097: writeOptionalDataToBuffer(t, needDataLogged);
098: } catch (IOException ioe) {
099: throw StandardException.newException(
100: SQLState.DATA_UNEXPECTED_EXCEPTION, ioe);
101: }
102:
103: }
104:
105: /*
106: * Formatable methods
107: */
108:
109: // no-arg constructor, required by Formatable
110: public PurgeOperation() {
111: super ();
112: }
113:
114: public void writeExternal(ObjectOutput out) throws IOException {
115: super .writeExternal(out);
116:
117: CompressedNumber.writeInt(out, slot);
118: CompressedNumber.writeInt(out, num_rows);
119:
120: for (int i = 0; i < num_rows; i++)
121: CompressedNumber.writeInt(out, recordIds[i]);
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: slot = CompressedNumber.readInt(in);
133: num_rows = CompressedNumber.readInt(in);
134:
135: recordIds = new int[num_rows];
136: for (int i = 0; i < num_rows; i++)
137: recordIds[i] = CompressedNumber.readInt(in);
138: }
139:
140: /**
141: Return my format identifier.
142: */
143: public int getTypeFormatId() {
144: return StoredFormatIds.LOGOP_PURGE;
145: }
146:
147: /*
148: * Loggable methods
149: */
150: /**
151: Apply the purge operation to the page.
152:
153: @exception IOException Can be thrown by any of the methods of ObjectInput.
154: @exception StandardException Standard Cloudscape policy.
155:
156: @see org.apache.derby.iapi.store.raw.Loggable#doMe
157: */
158: public void doMe(Transaction xact, LogInstant instant,
159: LimitObjectInput in) throws StandardException, IOException {
160: // purge the records in the stored version
161: // we need to remove from high to low because the slots will be moved down
162: // as soon as one is removed.
163:
164: // we could get the slot with the recordId but that will be a waste
165: // since the page was never unlatch and the slot number is good
166:
167: for (int i = num_rows - 1; i >= 0; i--) {
168: this .page.purgeRecord(instant, slot + i, recordIds[i]);
169: }
170: }
171:
172: /*
173: * PhysicalPageOperation methods
174: */
175:
176: /**
177: Undo the purge operation on the page.
178:
179: @exception IOException Can be thrown by any of the methods of ObjectInput.
180: @exception StandardException Standard Cloudscape policy.
181:
182: @see PhysicalPageOperation#undoMe
183: */
184: public void undoMe(Transaction xact, BasePage undoPage,
185: LogInstant CLRInstant, LimitObjectInput in)
186: throws StandardException, IOException {
187: for (int i = 0; i < num_rows; i++) {
188: undoPage.storeRecord(CLRInstant, slot + i, true, in);
189: }
190: undoPage.setAuxObject(null);
191: }
192:
193: /*
194: * PageBasicOperation
195: */
196:
197: /**
198: * restore the before image of the page
199: *
200: * @exception StandardException Standard Cloudscape Error Policy
201: * @exception IOException problem reading the complete log record from the
202: * input stream
203: */
204: public void restoreMe(Transaction xact, BasePage undoPage,
205: LogInstant CLRInstant, LimitObjectInput in)
206: throws StandardException, IOException {
207: undoMe(xact, undoPage, CLRInstant, in);
208: }
209:
210: /*
211: methods to support prepared log
212:
213: the following two methods should not be called during recover
214: */
215:
216: public ByteArray getPreparedLog() {
217: return (this .preparedLog);
218: }
219:
220: /**
221: Write out the purged record from the page. Used for undo only.
222:
223: @exception IOException Can be thrown by any of the methods of ObjectOutput.
224: @exception StandardException Standard Cloudscape policy.
225: */
226: private void writeOptionalDataToBuffer(RawTransaction t,
227: boolean needDataLogged) throws StandardException,
228: IOException {
229:
230: if (SanityManager.DEBUG) {
231: SanityManager.ASSERT(this .page != null);
232: }
233:
234: DynamicByteArrayOutputStream logBuffer = t.getLogBuffer();
235: int optionalDataStart = logBuffer.getPosition();
236:
237: if (SanityManager.DEBUG) {
238: SanityManager
239: .ASSERT(optionalDataStart == 0,
240: "Buffer for writing the optional data should start at position 0");
241: }
242:
243: for (int i = 0; i < num_rows; i++) {
244: if (needDataLogged) {
245: this .page.logRecord(i + slot,
246: BasePage.LOG_RECORD_DEFAULT, recordIds[i],
247: (FormatableBitSet) null, logBuffer,
248: (RecordHandle) null);
249: } else {
250: this .page.logRecord(i + slot,
251: BasePage.LOG_RECORD_FOR_PURGE, recordIds[i],
252: (FormatableBitSet) null, logBuffer,
253: (RecordHandle) null);
254: }
255: }
256:
257: int optionalDataLength = logBuffer.getPosition()
258: - optionalDataStart;
259:
260: if (SanityManager.DEBUG) {
261: if (optionalDataLength != logBuffer.getUsed())
262: SanityManager
263: .THROWASSERT("wrong optional data length, optionalDataLength = "
264: + optionalDataLength
265: + ", logBuffer.getUsed() = "
266: + logBuffer.getUsed());
267: }
268:
269: // set the position to the beginning of the buffer
270: logBuffer.setPosition(optionalDataStart);
271:
272: this .preparedLog = new ByteArray(logBuffer.getByteArray(),
273: optionalDataStart, optionalDataLength);
274: }
275:
276: /**
277: DEBUG: Print self.
278: */
279: public String toString() {
280: if (SanityManager.DEBUG) {
281: String str = super .toString() + "Purge : " + num_rows
282: + " slots starting at " + slot;
283:
284: for (int i = 0; i < num_rows; i++) {
285: str += " (recordId=" + recordIds[i] + ")";
286: }
287: return str;
288: } else
289: return null;
290: }
291: }
|