001: /*
002:
003: Derby - Class org.apache.derby.impl.store.raw.data.CopyRowsOperation
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: 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.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.log.LogInstant;
036: import org.apache.derby.iapi.store.raw.xact.RawTransaction;
037:
038: import org.apache.derby.iapi.error.StandardException;
039:
040: import org.apache.derby.iapi.services.io.CompressedNumber;
041: import org.apache.derby.iapi.services.io.FormatableBitSet;
042: import org.apache.derby.iapi.util.ByteArray;
043: import org.apache.derby.iapi.services.io.DynamicByteArrayOutputStream;
044:
045: import java.io.OutputStream;
046: import java.io.ObjectOutput;
047: import java.io.ObjectInput;
048: import java.io.IOException;
049: import org.apache.derby.iapi.services.io.LimitObjectInput;
050:
051: /**
052: Represents copying num_rows from one page to another page.
053:
054: <PRE>
055: @format_id LOGOP_COPY_ROWS
056: the formatId is written by FormatIdOutputStream when this object is
057: written out by writeObject
058: @purpose copy some rows from one page to another
059: @upgrade
060: @disk_layout
061: PhysicalPageOperation the superclass
062: num_rows(CompressedInt) number of rows to copy
063: destSlot(CompressedInt) the first slot number at the destination page
064: recordIds(CompressedInt[num_rows]) the recordIds at the destination page
065:
066: OptionalData the after image of the rows to be inserted into the
067: destination page
068: @end_format
069: </PRE>
070: */
071: public class CopyRowsOperation extends PhysicalPageOperation {
072:
073: protected int num_rows;
074: protected int destSlot; // copy into this page starting from destSlot
075: protected int[] recordIds; // num_rows of recordIds (use these for undo)
076: protected int[] reservedSpace; // number of bytes to reserve for each row.
077:
078: transient protected ByteArray preparedLog;
079:
080: public CopyRowsOperation(RawTransaction t, BasePage destPage,
081: BasePage srcPage, int destSlot, int num_rows, int srcSlot,
082: int[] recordIds) throws StandardException {
083: super (destPage);
084:
085: this .num_rows = num_rows;
086: this .destSlot = destSlot;
087: this .recordIds = recordIds;
088:
089: try {
090: reservedSpace = new int[num_rows];
091: for (int i = 0; i < num_rows; i++) {
092: reservedSpace[i] = srcPage
093: .getReservedCount(i + srcSlot);
094: }
095:
096: writeOptionalDataToBuffer(t, srcPage, srcSlot);
097: } catch (IOException ioe) {
098: throw StandardException.newException(
099: SQLState.DATA_UNEXPECTED_EXCEPTION, ioe);
100: }
101: }
102:
103: /*
104: * Formatable methods
105: */
106:
107: // no-arg constructor, required by Formatable
108: public CopyRowsOperation() {
109: super ();
110: }
111:
112: /*
113: public CopyRowsOperation(BasePage destPage) { super(destPage); }
114: */
115: public void writeExternal(ObjectOutput out) throws IOException {
116: super .writeExternal(out);
117:
118: CompressedNumber.writeInt(out, num_rows);
119: CompressedNumber.writeInt(out, destSlot);
120:
121: for (int i = 0; i < num_rows; i++) {
122: CompressedNumber.writeInt(out, recordIds[i]);
123: CompressedNumber.writeInt(out, reservedSpace[i]);
124: }
125: }
126:
127: /**
128: Read this in
129: @exception IOException error reading from log stream
130: @exception ClassNotFoundException log stream corrupted
131: */
132: public void readExternal(ObjectInput in) throws IOException,
133: ClassNotFoundException {
134: super .readExternal(in);
135:
136: num_rows = CompressedNumber.readInt(in);
137: destSlot = CompressedNumber.readInt(in);
138:
139: recordIds = new int[num_rows];
140: reservedSpace = new int[num_rows];
141: for (int i = 0; i < num_rows; i++) {
142: recordIds[i] = CompressedNumber.readInt(in);
143: reservedSpace[i] = CompressedNumber.readInt(in);
144: }
145: }
146:
147: /**
148: Return my format identifier.
149: */
150: public int getTypeFormatId() {
151: return StoredFormatIds.LOGOP_COPY_ROWS;
152: }
153:
154: /*
155: * Loggable methods
156: */
157: /**
158: @exception IOException Can be thrown by any of the methods of ObjectInput.
159: @exception StandardException Standard Cloudscape policy.
160:
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: /*
166: * this operation's do me will bump the page version by more than 1
167: */
168: for (int i = 0; i < num_rows; i++) {
169: page.storeRecord(instant, destSlot + i, true, in);
170:
171: if (reservedSpace[i] > 0)
172: page.reserveSpaceForSlot(instant, destSlot + i,
173: reservedSpace[i]);
174: }
175: }
176:
177: /*
178: * PhysicalPageOperation method
179: */
180:
181: /**
182: to undo this operation, purge all records that were copied over.
183:
184: @exception IOException Can be thrown by any of the methods of ObjectInput.
185: @exception StandardException Standard Cloudscape error policy
186: @see PhysicalPageOperation#undoMe
187: */
188: public void undoMe(Transaction xact, BasePage undoPage,
189: LogInstant CLRInstant, LimitObjectInput in)
190: throws StandardException, IOException {
191: // purge the records in the stored version
192: // since we search for each recordId, it doesn't matter whether we
193: // purge from high to low. In most cases, it will be in order so do it
194: // from high to low to save some work.
195:
196: int slot;
197:
198: for (int i = num_rows - 1; i >= 0; i--) {
199: slot = undoPage.findRecordById(recordIds[i], i);
200: undoPage.purgeRecord(CLRInstant, slot, recordIds[i]);
201: }
202:
203: undoPage.setAuxObject(null);
204: }
205:
206: /*
207: * PageBasicOperation method to support BeforeImageLogging
208: */
209:
210: /**
211: * restore the before image of the page
212: *
213: * @exception IOException problem reading the complete log record from the input stream
214: * @exception StandardException Standard Cloudscape Error Policy
215: */
216: public void restoreMe(Transaction xact, BasePage undoPage,
217: LogInstant CLRInstant, LimitObjectInput in)
218: throws StandardException, IOException {
219: undoMe(xact, undoPage, CLRInstant, in);
220: }
221:
222: /*
223: methods to support prepared log
224:
225: the following two methods should not be called during recover
226: */
227:
228: public ByteArray getPreparedLog() {
229: return (this .preparedLog);
230: }
231:
232: /**
233: Write the rows that are to be copied into this page
234:
235: @exception IOException Can be thrown by any of the methods of ObjectOutput.
236: @exception StandardException Standard Cloudscape policy.
237:
238: */
239: private void writeOptionalDataToBuffer(RawTransaction t,
240: BasePage srcPage, int srcSlot) throws StandardException,
241: IOException {
242: if (SanityManager.DEBUG) {
243: SanityManager.ASSERT(this .page != null);
244: SanityManager.ASSERT(srcPage != null);
245: }
246:
247: DynamicByteArrayOutputStream logBuffer = t.getLogBuffer();
248: int optionalDataStart = logBuffer.getPosition();
249:
250: if (SanityManager.DEBUG) {
251: SanityManager
252: .ASSERT(optionalDataStart == 0,
253: "Buffer for writing the optional data should start at position 0");
254: }
255:
256: // check to make sure the destination page have the necessary space to
257: // take the rows
258: int[] spaceNeeded = new int[num_rows];
259: int startPosition = logBuffer.getPosition();
260:
261: for (int i = 0; i < num_rows; i++) {
262: // the recordId passed in is the record Id this row will have at
263: // the destination page, not the record Id this row has on the
264: // srcPage.
265: srcPage.logRecord(i + srcSlot, BasePage.LOG_RECORD_DEFAULT,
266: recordIds[i], (FormatableBitSet) null, logBuffer,
267: (RecordHandle) null);
268: spaceNeeded[i] = logBuffer.getPosition() - startPosition;
269: startPosition = logBuffer.getPosition();
270:
271: // now spaceNeeded[i] has the actual record size. However, the src
272: // page may actually leave more space for the record due to
273: // reserved space. Because we want to copy the reserve space as well,
274: // we need to take into account that amount.
275: spaceNeeded[i] += reservedSpace[i];
276: }
277:
278: // page is the destination page.
279: if (!page.spaceForCopy(num_rows, spaceNeeded)) {
280: throw StandardException
281: .newException(SQLState.DATA_NO_SPACE_FOR_RECORD);
282: }
283:
284: int optionalDataLength = logBuffer.getPosition()
285: - optionalDataStart;
286:
287: if (SanityManager.DEBUG) {
288: if (optionalDataLength != logBuffer.getUsed())
289: SanityManager
290: .THROWASSERT("wrong optional data length, optionalDataLength = "
291: + optionalDataLength
292: + ", logBuffer.getUsed() = "
293: + logBuffer.getUsed());
294: }
295:
296: // set the position to the beginning of the buffer
297: logBuffer.setPosition(optionalDataStart);
298:
299: this .preparedLog = new ByteArray(logBuffer.getByteArray(),
300: optionalDataStart, optionalDataLength);
301: }
302:
303: /**
304: DEBUG: Print self.
305: */
306: public String toString() {
307: if (SanityManager.DEBUG) {
308: String str = super .toString() + "CopyRows : " + num_rows
309: + " to slots starting at " + destSlot;
310:
311: for (int i = 0; i < num_rows; i++) {
312: str += " (recordId=" + recordIds[i] + ")";
313: }
314: return str;
315: } else
316: return null;
317: }
318: }
|