--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.store.raw.data.UpdateOperation\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.store.raw.data;\r
+\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.impl.store.raw.data.BasePage;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.services.io.FormatIdUtil;\r
+import org.apache.derby.iapi.services.io.StoredFormatIds;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.iapi.store.raw.Page;\r
+import org.apache.derby.iapi.store.raw.RecordHandle;\r
+import org.apache.derby.iapi.store.raw.Transaction;\r
+\r
+import org.apache.derby.iapi.store.raw.log.LogInstant;\r
+import org.apache.derby.iapi.store.raw.xact.RawTransaction; \r
+\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+\r
+import org.apache.derby.iapi.services.io.FormatableBitSet;\r
+import org.apache.derby.iapi.util.ByteArray;\r
+import org.apache.derby.iapi.services.io.CompressedNumber;\r
+import org.apache.derby.iapi.services.io.DynamicByteArrayOutputStream;\r
+import org.apache.derby.iapi.services.io.DynamicByteArrayOutputStream;\r
+\r
+import java.io.OutputStream;\r
+import java.io.ObjectOutput;\r
+import java.io.ObjectInput;\r
+import java.io.IOException;\r
+import org.apache.derby.iapi.services.io.LimitObjectInput;\r
+\r
+\r
+/**\r
+ Represents the update of a particular row on a page.\r
+\r
+ <PRE>\r
+ @format_id LOGOP_UPDATE\r
+ the formatId is written by FormatIdOutputStream when this object is\r
+ written out by writeObject\r
+ @purpose update a record on the page\r
+ @upgrade\r
+ @disk_layout\r
+ PhysicalPageOperation the super class\r
+ doMeSlot(CompressedInt) the slot the updated record is in\r
+ recordId(CompressedInt) the recordId of the updated record\r
+\r
+ OptionalData The new image of the record (length included), \r
+ follow by the old image of the record (length included)\r
+ @end_format\r
+ </PRE>\r
+*/\r
+\r
+public final class UpdateOperation extends PhysicalPageOperation {\r
+\r
+ protected int doMeSlot; // record slot - only valid during a doMe() operation\r
+ protected int recordId; // record id\r
+ transient protected int nextColumn; // next column that needs to be updated in a row.\r
+\r
+ transient protected ByteArray preparedLog;\r
+ \r
+ public UpdateOperation(\r
+ RawTransaction t, \r
+ BasePage page, \r
+ int slot, \r
+ int recordId,\r
+ Object[] row, \r
+ FormatableBitSet validColumns,\r
+ int realStartColumn, \r
+ DynamicByteArrayOutputStream logBuffer, \r
+ int realSpaceOnPage, \r
+ RecordHandle headRowHandle)\r
+ throws StandardException\r
+ {\r
+ super(page);\r
+\r
+ this.doMeSlot = slot;\r
+ this.recordId = recordId;\r
+ this.nextColumn = -1;\r
+ \r
+ // RESOLVE SRW-DJD/YYZ\r
+ try {\r
+ writeOptionalDataToBuffer(t, (DynamicByteArrayOutputStream) logBuffer,\r
+ row, validColumns, realStartColumn,\r
+ realSpaceOnPage, headRowHandle);\r
+ } catch (IOException ioe) {\r
+ throw StandardException.newException(\r
+ SQLState.DATA_UNEXPECTED_EXCEPTION, ioe);\r
+ }\r
+\r
+ }\r
+\r
+ /*\r
+ * Formatable methods\r
+ */\r
+\r
+ // no-arg constructor, required by Formatable \r
+ public UpdateOperation() { super(); }\r
+\r
+ public void writeExternal(ObjectOutput out) throws IOException \r
+ {\r
+ super.writeExternal(out);\r
+ CompressedNumber.writeInt(out, doMeSlot);\r
+ CompressedNumber.writeInt(out, recordId);\r
+ }\r
+\r
+ /**\r
+ Read this in\r
+ @exception IOException error reading from log stream\r
+ @exception ClassNotFoundException log stream corrupted\r
+ */\r
+ public void readExternal(ObjectInput in)\r
+ throws IOException, ClassNotFoundException\r
+ {\r
+ super.readExternal(in);\r
+ doMeSlot = CompressedNumber.readInt(in);\r
+ recordId = CompressedNumber.readInt(in);\r
+ }\r
+\r
+ /**\r
+ Return my format identifier.\r
+ */\r
+ public int getTypeFormatId() {\r
+ return StoredFormatIds.LOGOP_UPDATE;\r
+ }\r
+\r
+ /**\r
+ Return the last column of the row this operation logged\r
+ */\r
+ public int getNextStartColumn() {\r
+ return nextColumn;\r
+ }\r
+\r
+ /*\r
+ * Loggable methods\r
+ */\r
+ /**\r
+ Store the new record directly over the old record, the implementation\r
+ of storeRecord is responsible for removing any old data.\r
+\r
+ @exception StandardException Thrown by methods I call\r
+ @exception IOException Thrown by methods I call\r
+\r
+ @see BasePage#storeRecord\r
+ @see org.apache.derby.iapi.store.raw.Loggable#doMe\r
+ */\r
+ public void doMe(Transaction xact, LogInstant instant, LimitObjectInput in)\r
+ throws StandardException, IOException \r
+ {\r
+ this.page.storeRecord(instant, doMeSlot, false, in);\r
+ }\r
+ \r
+\r
+ /*\r
+ * PhysicalPageOperation methods\r
+ */\r
+\r
+ /**\r
+ Store the old record directly over the new record, the implementation\r
+ of storeRecord is responsible for removing any new data.\r
+\r
+ @exception StandardException Thrown by methods I call\r
+ @exception IOException Thrown by methods I call\r
+\r
+ @see BasePage#storeRecord\r
+ @see PhysicalPageOperation#undoMe\r
+ */\r
+ public void undoMe(Transaction xact, BasePage undoPage,\r
+ LogInstant CLRInstant, LimitObjectInput in)\r
+ throws StandardException, IOException \r
+ {\r
+\r
+ int slot = undoPage.findRecordById(recordId, Page.FIRST_SLOT_NUMBER);\r
+\r
+ // skip the after image of the record\r
+ undoPage.skipRecord(in);\r
+\r
+ undoPage.storeRecord(CLRInstant, slot, false, in);\r
+ undoPage.setAuxObject(null);\r
+ }\r
+\r
+ /*\r
+ methods to support prepared log\r
+ \r
+ the following two methods should not be called during recover\r
+ */\r
+\r
+ public ByteArray getPreparedLog()\r
+ {\r
+ return (this.preparedLog);\r
+ }\r
+\r
+ /**\r
+ Write out the changed colums of new record (from the row) followed by \r
+ changed columns of the old record (from the page).\r
+\r
+ @exception StandardException Thrown by methods I call\r
+ @exception IOException Thrown by methods I call\r
+ */\r
+ private void writeOptionalDataToBuffer(\r
+ RawTransaction t, \r
+ DynamicByteArrayOutputStream logBuffer,\r
+ Object[] row, \r
+ FormatableBitSet validColumns,\r
+ int realStartColumn, \r
+ int realSpaceOnPage, \r
+ RecordHandle headRowHandle)\r
+ throws StandardException, IOException\r
+ {\r
+\r
+ if (SanityManager.DEBUG) \r
+ {\r
+ SanityManager.ASSERT(this.page != null);\r
+ }\r
+\r
+ if (realStartColumn == (-1)) \r
+ {\r
+ logBuffer = t.getLogBuffer();\r
+ }\r
+\r
+ int optionalDataStart = logBuffer.getPosition();\r
+\r
+ if (SanityManager.DEBUG) \r
+ {\r
+\r
+ SanityManager.ASSERT(\r
+ (realStartColumn != -1 || optionalDataStart == 0),\r
+ "Buffer for writing optional data should start at position 0");\r
+ }\r
+\r
+\r
+ this.nextColumn = \r
+ this.page.logRow(\r
+ doMeSlot, false, recordId, row, validColumns,\r
+ logBuffer, 0, Page.INSERT_OVERFLOW, realStartColumn,\r
+ realSpaceOnPage, 100);\r
+\r
+ FormatableBitSet loggedColumns = validColumns;\r
+\r
+ // If this update results in moving columns off the current page to\r
+ // another page, then we must log the before image values of the columns\r
+ // being moved (deleted from this page) in addition to logging the\r
+ // columns actually being changed as part of the update.\r
+\r
+ if ((nextColumn != -1) && (validColumns != null))\r
+ {\r
+ // if nextColumn is not -1, then this must be an update which moves\r
+ // columns off of the current page. If validColumns == null then\r
+ // we are logging all of the before image columns anyway.\r
+\r
+ // get total number of fields of the old record.\r
+ int numberFields = page.getHeaderAtSlot(doMeSlot).getNumberFields();\r
+\r
+ // create new bit map, copying all bits that were set in original\r
+ loggedColumns = new FormatableBitSet(validColumns);\r
+\r
+ // make sure there is room in the bit map to add the columns being\r
+ // deleted from the end of the row.\r
+ // The important thing is that endField must be at least as big as\r
+ // the number of columns in the entire record (including previous\r
+ // pages of a long row) up to the end of this page.\r
+ int endField = nextColumn + numberFields;\r
+ loggedColumns.grow(endField);\r
+ // now include all columns being deleted.\r
+ // This actually sets too many bits in this bit set but\r
+ // logRecord will just ignore the extra bits.\r
+ for (int i = nextColumn; i < endField; i++)\r
+ {\r
+ loggedColumns.set(i);\r
+ }\r
+ }\r
+\r
+ // log the old version of the changed data\r
+ this.page.logRecord(\r
+ doMeSlot, BasePage.LOG_RECORD_FOR_UPDATE,\r
+ recordId, loggedColumns, logBuffer, headRowHandle);\r
+\r
+ // get length of all the optional data.\r
+ optionalDataStart = logBuffer.getBeginPosition();\r
+ int optionalDataLength = logBuffer.getPosition() - optionalDataStart;\r
+\r
+ // set the position to the beginning of the buffer\r
+ logBuffer.setPosition(optionalDataStart);\r
+\r
+ this.preparedLog = new ByteArray(\r
+ logBuffer.getByteArray(), optionalDataStart, optionalDataLength);\r
+ }\r
+\r
+ /*\r
+ * PageBasicOperation\r
+ */\r
+\r
+ /**\r
+ * restore the before image of the page\r
+ *\r
+ * @exception StandardException Standard Derby Error Policy\r
+ * @exception IOException problem reading the complete log record from the\r
+ * input stream\r
+ */\r
+ public void restoreMe(Transaction xact, BasePage undoPage,\r
+ LogInstant CLRInstant, LimitObjectInput in)\r
+ throws StandardException, IOException \r
+ {\r
+ undoMe(xact, undoPage, CLRInstant, in);\r
+ }\r
+\r
+\r
+ public String toString()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ return super.toString() + \r
+ "Update " +\r
+ " Slot=" + doMeSlot + \r
+ " recordId=" + recordId;\r
+ }\r
+ else\r
+ return null;\r
+ }\r
+}\r