You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by kr...@apache.org on 2007/06/13 13:59:35 UTC
svn commit: r546831 - in /db/derby/code/trunk/java:
engine/org/apache/derby/impl/jdbc/
testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/
Author: kristwaa
Date: Wed Jun 13 04:59:34 2007
New Revision: 546831
URL: http://svn.apache.org/viewvc?view=rev&rev=546831
Log:
DERBY-2712: If large Clob is updated after getting a Reader, the reader continues to point to old data.
Patch file: derby-2712v3.diff
Patch contributed by Anurag Shekhar.
Modified:
db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobStreamControl.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobUpdateableReader.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedClob.java
db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobUpdateableReaderTest.java
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobStreamControl.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobStreamControl.java?view=diff&rev=546831&r1=546830&r2=546831
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobStreamControl.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobStreamControl.java Wed Jun 13 04:59:34 2007
@@ -398,7 +398,8 @@
private void copyClobContent(InternalClob clob)
throws IOException, SQLException {
try {
- this.bytes.copyData(clob.getRawByteStream(), clob.getByteLength());
+ long byteLength = clob.getByteLength();
+ this.bytes.copyData(clob.getRawByteStream(), byteLength);
} catch (StandardException se) {
throw Util.generateCsSQLException(se);
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobUpdateableReader.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobUpdateableReader.java?view=diff&rev=546831&r1=546830&r2=546831
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobUpdateableReader.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobUpdateableReader.java Wed Jun 13 04:59:34 2007
@@ -22,8 +22,14 @@
*/
package org.apache.derby.impl.jdbc;
+import java.io.EOFException;
import java.io.IOException;
+import java.io.InputStream;
import java.io.Reader;
+import java.sql.SQLException;
+import org.apache.derby.iapi.reference.SQLState;
+import org.apache.derby.iapi.services.i18n.MessageService;
+import org.apache.derby.iapi.services.sanity.SanityManager;
/**
* <code>ClobUpdateableReader</code> is used to create a <code>Reader</code>
@@ -42,9 +48,13 @@
/** Character position of this reader. */
private long pos;
/** Underlying stream of byte data. */
- private LOBInputStream stream;
+ private InputStream stream;
/** Connection object used to obtain synchronization-object. */
private ConnectionChild conChild;
+ /** flag to indicate if its associated with materialized clob */
+ private boolean materialized;
+ /** clob object this object is associated */
+ private final EmbedClob clob;
/**
* Constructs a <code>Reader</code> over a <code>LOBInputStream</code>.
@@ -54,9 +64,28 @@
*/
ClobUpdateableReader (LOBInputStream stream, ConnectionChild conChild)
throws IOException {
+ clob = null;
+ materialized = true;
this.conChild = conChild;
this.stream = stream;
- init (0);
+ init (stream, 0);
+ }
+
+ /**
+ * Constructs a <code>Reader</code> over a <code>LOBInputStream</code>.
+ * @param clob EmbedClob this Reader is associated to.
+ * @throws IOException
+ * @throws SQLException
+ */
+ ClobUpdateableReader (EmbedClob clob) throws IOException, SQLException {
+ materialized = clob.isWritable();
+ this.clob = clob;
+ this.conChild = clob;
+ //getting bytelength make some time leave exisitng streams
+ //unusable
+ long byteLength = clob.getByteLength();
+ this.stream = clob.getInternalStream ();
+ init (0, byteLength);
}
/**
@@ -68,11 +97,8 @@
* @return number of bytes read
* @throws IOException
*/
- public int read(char[] cbuf, int off, int len) throws IOException {
- if (stream.isObsolete()) {
- stream.reInitialize();
- init (pos);
- }
+ public int read(char[] cbuf, int off, int len) throws IOException {
+ updateIfRequired();
int ret = streamReader.read (cbuf, off, len);
if (ret >= 0) {
pos += ret;
@@ -93,15 +119,76 @@
* @param skip number of characters to skip to reach initial position
* @throws IOException if a streaming error occurs
*/
- private void init(long skip) throws IOException {
+ private void init(LOBInputStream stream, long skip)
+ throws IOException {
streamReader = new UTF8Reader (stream, 0, stream.length (),
conChild,
conChild.getConnectionSynchronization());
long remainToSkip = skip;
while (remainToSkip > 0) {
long skipBy = streamReader.skip(remainToSkip);
+ if (skipBy == 0) {
+ if (streamReader.read() == -1) {
+ throw new EOFException (
+ MessageService.getCompleteMessage (
+ SQLState.STREAM_EOF, new Object [0]));
+ }
+ skipBy = 1;
+ }
remainToSkip -= skipBy;
}
pos = skip;
}
+
+ private void init (long skip, long streamLength) throws IOException {
+ streamReader = new UTF8Reader (stream, 0, streamLength,
+ conChild,
+ conChild.getConnectionSynchronization());
+ long remainToSkip = skip;
+ while (remainToSkip > 0) {
+ long skipBy = streamReader.skip(remainToSkip);
+ remainToSkip -= skipBy;
+ }
+ pos = skip;
+ }
+
+ /**
+ * Updates the stream if underlying clob is modified since
+ * this reader was created.
+ * If the stream is associated with a materialized clob, it
+ * checks if the underlying clob is updated since last read and
+ * updates itself if it is. If the stream is associated with
+ * non materialized clob and clob is materialized since last read it
+ * fetches the stream again and sets the position to current position.
+ * @throws IOException
+ */
+ private void updateIfRequired () throws IOException {
+ if (materialized) {
+ LOBInputStream lobStream = (LOBInputStream) stream;
+ if (lobStream.isObsolete()) {
+ lobStream.reInitialize();
+ init (lobStream, pos);
+ }
+ }
+ else {
+ //clob won't be null if the stream wasn't materialized
+ //but still try to be safe
+ if (SanityManager.DEBUG) {
+ SanityManager.ASSERT (!(clob == null),
+ "Internal error while updating stream");
+ }
+ if (clob.isWritable ()) {
+ try {
+ stream = clob.getInternalStream();
+ }
+ catch (SQLException e) {
+ IOException ioe = new IOException (e.getMessage());
+ ioe.initCause (e);
+ throw ioe;
+ }
+ init ((LOBInputStream) stream, pos);
+ materialized = true;
+ }
+ }
+ }
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedClob.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedClob.java?view=diff&rev=546831&r1=546830&r2=546831
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedClob.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedClob.java Wed Jun 13 04:59:34 2007
@@ -253,7 +253,7 @@
//the Clob object has been freed by calling free() on it
checkValidity();
try {
- return this.clob.getReader(1L);
+ return new ClobUpdateableReader (this);
} catch (IOException ioe) {
throw Util.setStreamFailure(ioe);
}
@@ -721,5 +721,33 @@
getEmbedConnection().getDBName(),
this, toBeAbandoned, len);
toBeAbandoned.release();
+ }
+
+ /**
+ * Returns if the internal clob is a writable clob.
+ * @return true if internal clob is writable
+ */
+ boolean isWritable() {
+ return clob.isWritable();
+ }
+
+ /**
+ * Returns the internal InputStream associated with this clob.
+ * @return internal InputStream
+ * @throws IOException
+ */
+ InputStream getInternalStream ()
+ throws IOException, SQLException {
+ return clob.getRawByteStream();
+ }
+
+ /**
+ * Returns byte length of the clob
+ * @return byte length of the clob
+ * @throws IOException
+ * @throws SQLException
+ */
+ long getByteLength() throws IOException, SQLException {
+ return clob.getByteLength();
}
}
Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobUpdateableReaderTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobUpdateableReaderTest.java?view=diff&rev=546831&r1=546830&r2=546831
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobUpdateableReaderTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobUpdateableReaderTest.java Wed Jun 13 04:59:34 2007
@@ -47,6 +47,60 @@
}
/**
+ * Test updating a large clob
+ */
+ public void testUpdateableStoreReader () throws Exception {
+ Connection con = getConnection();
+ try {
+ con.setAutoCommit (false);
+ PreparedStatement ps = con.prepareStatement ("insert into updateClob " +
+ "(id , data) values (? ,?)");
+ ps.setInt (1, 2);
+ StringBuffer sb = new StringBuffer ();
+ String base = "SampleSampleSample";
+ for (int i = 0; i < 100000; i++) {
+ sb.append (base);
+ }
+ //insert a large enough data to ensure stream is created in dvd
+ ps.setCharacterStream (2, new StringReader (sb.toString()),
+ sb.length());
+ ps.execute();
+ ps.close();
+ Statement stmt = con.createStatement ();
+ ResultSet rs = stmt.executeQuery("select data from " +
+ "updateClob where id = 2");
+ rs.next();
+ Clob clob = rs.getClob (1);
+ rs.close();
+ stmt.close();
+ assertEquals (sb.length(), clob.length());
+ Reader r = clob.getCharacterStream();
+ String newString = "this is a new string";
+ //access reader before modifying the clob
+ long l = r.skip (100);
+ clob.setString (1001, newString);
+ //l chars are already skipped
+ long toSkip = 1000 - l;
+ while (toSkip > 0) {
+ long skipped = r.skip (toSkip);
+ toSkip -= skipped;
+ }
+ char [] newdata = new char [newString.length()];
+ int len = r.read(newdata);
+ assertEquals ("updated not reflected", newString,
+ new String (newdata, 0, len));
+ r.close();
+ }
+ finally {
+ if (con != null) {
+ con.commit ();
+ con.close ();
+ }
+ }
+
+ }
+
+ /**
* Tests updates on reader.
*/
public void testUpdateableReader () throws Exception {