--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.store.raw.data.OverflowInputStream\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.error.StandardException;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.iapi.store.raw.RecordHandle;\r
+\r
+import org.apache.derby.iapi.types.Resetable;\r
+import org.apache.derby.iapi.store.raw.LockingPolicy;\r
+import org.apache.derby.iapi.store.access.TransactionController;\r
+\r
+import java.io.IOException;\r
+\r
+/**\r
+ A OverflowInputStream is used by store to turn a long column\r
+ into an InputStream.\r
+*/\r
+public class OverflowInputStream\r
+extends BufferedByteHolderInputStream\r
+implements Resetable\r
+{\r
+ protected BaseContainerHandle owner;\r
+ protected long overflowPage;\r
+ protected int overflowId;\r
+ // remember first page and id for reset\r
+ protected long firstOverflowPage;\r
+ protected int firstOverflowId;\r
+ // the row to lock for Blobs/Clobs\r
+ protected RecordHandle recordToLock;\r
+ \r
+ // Make sure record is only locked once.\r
+ private boolean initialized = false;\r
+\r
+ public OverflowInputStream(ByteHolder bh, BaseContainerHandle owner,\r
+ long overflowPage, int overflowId, RecordHandle recordToLock)\r
+ throws IOException, StandardException\r
+ {\r
+ super(bh);\r
+ this.owner = owner;\r
+ this.overflowPage = overflowPage;\r
+ this.overflowId = overflowId;\r
+ this.firstOverflowPage = overflowPage;\r
+ this.firstOverflowId = overflowId;\r
+ this.recordToLock = recordToLock;\r
+ fillByteHolder();\r
+ }\r
+\r
+\r
+ public void fillByteHolder() throws IOException\r
+ {\r
+ if ((this.bh.available() == 0) && (this.overflowPage != -1))\r
+ {\r
+ this.bh.clear();\r
+\r
+ try\r
+ {\r
+ // fill the byte holder with data from the page.\r
+ BasePage columnOverflowPage = \r
+ ((BasePage) this.owner.getPage(overflowPage));\r
+\r
+ if (columnOverflowPage != null)\r
+ {\r
+ columnOverflowPage.restorePortionLongColumn(this);\r
+ columnOverflowPage.unlatch();\r
+ columnOverflowPage = null;\r
+ }\r
+ }\r
+ catch (StandardException se)\r
+ {\r
+ throw new IOException( se.toString() );\r
+ }\r
+ this.bh.startReading();\r
+ }\r
+ }\r
+\r
+\r
+ public long getOverflowPage() {\r
+ return this.overflowPage;\r
+ }\r
+\r
+ public int getOverflowId() {\r
+ return this.overflowId;\r
+ }\r
+\r
+ public void setOverflowPage(long overflowPage) {\r
+ this.overflowPage = overflowPage;\r
+ }\r
+\r
+ public void setOverflowId(int overflowId) {\r
+ this.overflowId = overflowId;\r
+ }\r
+\r
+\r
+ /*\r
+ Methods of Resetable interface.\r
+ */\r
+\r
+ /*\r
+ Resets the stream to the beginning.\r
+ */\r
+ public void resetStream() throws IOException, StandardException\r
+ {\r
+ // check the container is open, this is needed to make sure the\r
+ // container closed exception is thrown as a StandardException and not\r
+ // as an IOException\r
+ owner.checkOpen();\r
+ // return to the original overflow page and id\r
+ this.overflowPage = firstOverflowPage;\r
+ this.overflowId = firstOverflowId;\r
+ // completely clear the byte holder\r
+ this.bh.clear();\r
+ this.bh.startReading();\r
+ // fill the byte holder\r
+ fillByteHolder();\r
+ }\r
+\r
+ /**\r
+ * Initialize. Reopen the container. This will have the effect of\r
+ * getting an intent shared lock on the table, which will stay around until\r
+ * the enclosing blob/clob object is closed, or until the end of the \r
+ * transaction. Also get a read lock on the appropriate row.\r
+ * \r
+ * @throws org.apache.derby.iapi.error.StandardException\r
+ */\r
+ public void initStream() throws StandardException\r
+ {\r
+ if (initialized) return;\r
+ \r
+ // it is possible that the transaction in which the stream was\r
+ // created is committed and no longer valid\r
+ // dont want to get NPE but instead throw error that\r
+ // container was not opened\r
+ if (owner.getTransaction() == null)\r
+ throw StandardException.newException(SQLState.DATA_CONTAINER_CLOSED);\r
+ \r
+ /*\r
+ We use isolation level READ_COMMITTED and reopen the container to \r
+ get a new container handle to use for locking. This way, the lock will\r
+ be freed when we the container handle is closed. This will happen in\r
+ closeStream() or when the transaction commits. \r
+ Hence, locks will be released before the end of transaction if \r
+ blobs/clobs are explicitly released.\r
+ */\r
+ LockingPolicy lp = \r
+ owner.getTransaction().newLockingPolicy(\r
+ LockingPolicy.MODE_RECORD, \r
+ TransactionController.ISOLATION_READ_COMMITTED, true);\r
+\r
+ // reopen the container\r
+ owner = (BaseContainerHandle) owner.getTransaction().openContainer(\r
+ owner.getId(), lp, owner.getMode());\r
+\r
+ // get a read lock on the appropriate row\r
+ // this will wait until either the lock is granted or an exception is\r
+ // thrown\r
+ owner.getLockingPolicy().lockRecordForRead(\r
+ owner.getTransaction(), owner, recordToLock, true, false);\r
+ \r
+ initialized = true;\r
+ }\r
+\r
+\r
+ /*\r
+ Close the container associated with this stream. (This will also free the \r
+ associated IS table lock and the associated S row lock.)\r
+ */\r
+ public void closeStream()\r
+ {\r
+ owner.close();\r
+ initialized = false;\r
+ }\r
+\r
+}\r