db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From krist...@apache.org
Subject svn commit: r546831 - in /db/derby/code/trunk/java: engine/org/apache/derby/impl/jdbc/ testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/
Date Wed, 13 Jun 2007 11:59:35 GMT
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 {



Mime
View raw message