db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From krist...@apache.org
Subject svn commit: r544814 - in /db/derby/code/trunk/java: engine/org/apache/derby/impl/jdbc/ testing/org/apache/derbyTesting/functionTests/tests/jdbc4/ testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/
Date Wed, 06 Jun 2007 11:46:44 GMT
Author: kristwaa
Date: Wed Jun  6 04:46:43 2007
New Revision: 544814

URL: http://svn.apache.org/viewvc?view=rev&rev=544814
Log:
DERBY-2646: Cleanup of Clob control/support structures. Added new versions of EmbedClob and ClobStreamControl. This commit enables the code committed earlier for this issue. This is the last major patch, and bugs/remaining work will be handled under separate Jiras.
Patch file: derby-2646-06b-embedclob_clobstreamcontrol.diff

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/ClobUtf8Writer.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedClob.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/ClobTest.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobTest.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/_Suite.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=544814&r1=544813&r2=544814
==============================================================================
--- 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  6 04:46:43 2007
@@ -23,21 +23,60 @@
 package org.apache.derby.impl.jdbc;
 
 import java.io.BufferedInputStream;
+import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
 import java.io.Reader;
-import java.io.UTFDataFormatException;
 import java.io.Writer;
 import java.sql.SQLException;
 import org.apache.derby.iapi.error.StandardException;
-import org.apache.derby.iapi.reference.SQLState;
-import org.apache.derby.iapi.util.ByteArray;
+import org.apache.derby.iapi.util.UTF8Util;
 
-final class ClobStreamControl extends LOBStreamControl {
+/**
+ * A Clob representation where the Clob is stored either in memory or on disk.
+ * <p>
+ * Character positions given as input to methods in this class are always
+ * 1-based. Byte positions are always 0-based.
+ */
+final class ClobStreamControl implements InternalClob {
 
+    /**
+     * Connection child assoicated with this Clob.
+     * <p>
+     * Currently only used for synchronization in *some* streams associated
+     * with the Clob. This suggests something is off wrt. synchronization.
+     */
     private ConnectionChild conChild;
+    /** Underlying structure holding this Clobs raw bytes. */
+    //@GuardedBy("this")
+    private final LOBStreamControl bytes;
+    /** Tells whether this Clob has been released or not. */
+    // GuardedBy("this")
+    private boolean released = false;
+
+    /** Simple one-entry cache for character-byte position. */
+    // @GuardedBy("this")
+    private final CharToBytePositionCache posCache =
+        new CharToBytePositionCache();
+
+    /**
+     * Clones the content of another internal Clob.
+     *
+     * @param dbName name of the assoicated database
+     * @param conChild assoiated connection child
+     * @param clob the Clob whose content to clone
+     * @return A read-write Clob.
+     * @throws IOException if accessing the I/O resources fail (read or write)
+     * @throws SQLException if accessing underlying resources fail
+     */
+    static InternalClob cloneClobContent(String dbName,
+                                         ConnectionChild conChild,
+                                         InternalClob clob)
+            throws IOException, SQLException {
+        ClobStreamControl newClob = new ClobStreamControl(dbName, conChild);
+        newClob.copyClobContent(clob);
+        return newClob;
+    }
 
     /**
      * Constructs a <code>ClobStreamControl</code> object used to perform
@@ -45,10 +84,44 @@
      *
      * @param dbName name of the database the CLOB value belongs to
      * @param conChild connection object used to obtain synchronization object
+     * @throws NullPointerException if <code>conChild</code> is
+     *      <code>null</code>
      */
     ClobStreamControl (String dbName, ConnectionChild conChild) {
-        super (dbName);
+        if (conChild == null) {
+            throw new NullPointerException("conChild cannot be <null>");
+        }
         this.conChild = conChild;
+        this.bytes = new LOBStreamControl(dbName);
+    }
+
+    /**
+     * Releases this Clob by freeing assoicated resources.
+     *
+     * @throws IOException if accessing underlying I/O resources fail
+     */
+    public synchronized void release()
+            throws IOException {
+        if (!this.released) {
+            this.released = true;
+            this.bytes.free();
+        }
+    }
+
+    /**
+     * Returns a stream serving the raw bytes of this Clob.
+     * <p>
+     * The stream is managed by the underlying byte store, and can serve bytes
+     * both from memory and from a file on disk.
+     *
+     * @return A stream serving the raw bytes of the stream, initialized at
+     *      byte position <code>0</code>.
+     * @throws IOException if obtaining the stream fails
+     */
+    public synchronized InputStream getRawByteStream()
+            throws IOException {
+        checkIfValid();
+        return this.bytes.getInputStream(0L);
     }
 
     /**
@@ -57,51 +130,35 @@
      * See comments in SQLChar.readExternal for more notes on
      * processing the UTF8 format.
      *
-     * @param startPos start position in number of bytes
      * @param charPos character position
      * @return Stream position in bytes for the given character position.
-     * @throws IOException
+     * @throws EOFException if the character position specified is greater than
+     *      the Clob length +1
+     * @throws IOException if accessing underlying I/O resources fail
      */
-    synchronized long getStreamPosition (long startPos, long charPos) throws IOException {
-        InputStream in = new BufferedInputStream (getInputStream (startPos));
-        long charLength = 0;
-        long streamLength = 0;
-        //utf decoding routine
-        while (charLength < charPos) {
-            int c = in.read();
-            if (c < 0)
-                return -1;
-            charLength ++;
-            if ((c & 0x80) == 0x00) {
-                //found char of one byte width
-                streamLength++;
-            }
-            else if ((c & 0x60) == 0x40) // we know the top bit is set here
-            {
-                //found char of two byte width
-                if (in.skip (1) != 1) {
-                    //no second and third byte present
-                    throw new UTFDataFormatException();
-                }
-                streamLength += 2;
-            }
-            else if ((c & 0x70) == 0x60) // we know the top bit is set here
-            {
-                //found char of three byte width
-                if (in.skip (2) != 2) {
-                    //no second and third byte present
-                    throw new UTFDataFormatException();
-                }
-                streamLength += 3;
-            }
-            else
-            {
-                throw new UTFDataFormatException();
+    public synchronized long getBytePosition (final long charPos)
+            throws IOException {
+        checkIfValid();
+        long bytePos;
+        if (charPos == this.posCache.getCharPos()) {
+            // We already know the position.
+            bytePos = this.posCache.getBytePos();
+        } else {
+            long startingBytePosition = 0L; // Default to start at position 0.
+            long charsToSkip = charPos -1; // Subtract one to get number to skip.
+            if (charPos > this.posCache.getCharPos()) {
+                // Exploit the last known character position.
+                startingBytePosition = this.posCache.getBytePos();
+                charsToSkip -= (this.posCache.getCharPos() -1);
             }
+            InputStream utf8Bytes =
+                this.bytes.getInputStream(startingBytePosition);
+            bytePos = startingBytePosition +
+                UTF8Util.skipFully(new BufferedInputStream(utf8Bytes),
+                                   charsToSkip);
+            this.posCache.updateCachedPos(charPos, bytePos);
         }
-
-        in.close();
-        return streamLength;
+        return bytePos;
     }
 
     /**
@@ -112,109 +169,158 @@
      * @throws IOException
      * @throws SQLException if the specified position is invalid
      */
-    synchronized Writer getWriter (long pos) throws IOException, SQLException {
-        long charPos = getStreamPosition (0, pos);
-        if (charPos == -1)
-            throw Util.generateCsSQLException (SQLState.BLOB_POSITION_TOO_LARGE,
-                                "" + (pos + 1));
-        return new ClobUtf8Writer (this, getStreamPosition (0, charPos));
+    public synchronized Writer getWriter (long pos) throws IOException, SQLException {
+        checkIfValid();
+        // If pos is too large, an error will first be thrown when the writer
+        // is written to. Is this okay behavior, is does it break the spec?
+        if (pos < this.posCache.getCharPos()) {
+            this.posCache.reset();
+        }
+        return new ClobUtf8Writer (this, pos);
     }
 
     /**
      * Constructs and returns a <code>Reader</code>.
      * @param pos initial position of the returned <code>Reader</code> in
-     *      number of characters
+     *      number of characters. Expected to be non-negative. The first
+     *      character is at position <code>0</code>.
      * @return A <code>Reader</code> with the underlying <code>CLOB</code>
      *      value as source.
      * @throws IOException
-     * @throws SQLException if the specified position is invalid
+     * @throws SQLException if the specified position is too big
      */
-    Reader getReader (long pos) throws IOException, SQLException {
+    public synchronized Reader getReader (long pos)
+            throws IOException, SQLException {
+        checkIfValid();
+        if (pos < 1) {
+            throw new IllegalArgumentException(
+                "Position must be positive: " + pos);
+        }
         Reader isr = new ClobUpdateableReader (
-                (LOBInputStream) getInputStream (0), conChild);
+                (LOBInputStream) getRawByteStream(), conChild);
 
-        long leftToSkip = pos;
+        long leftToSkip = pos -1;
+        long skipped;
         while (leftToSkip > 0) {
-            leftToSkip -= isr.skip (leftToSkip);
+            skipped = isr.skip(leftToSkip);
+            // Since Reader.skip block until some characters are available,
+            // a return value of 0 must mean EOF.
+            if (skipped <= 0) {
+                throw new EOFException("Reached end-of-stream prematurely");
+            }
+            leftToSkip -= skipped;
         }
         return isr;
     }
 
     /**
-     * Returns a substring.
-     * @param bIndex 
-     * @param eIndex 
-     * @return A substring of the <code>CLOB</code> value.
-     * @throws IOException
-     * @throws SQLException
+     * Returns number of characters in the Clob.
+     *
+     * @return The length of the Clob in number of characters.
+     * @throws IOException if accessing the underlying I/O resources fail
      */
-    synchronized String getSubstring (long bIndex, long eIndex)
-                                            throws IOException, SQLException {
-        Reader r = getReader(bIndex);
-        char [] buff = new char [(int) (eIndex - bIndex)];
-        int length = 0;
-        do {
-            int ret = r.read (buff, length, (int) (eIndex - bIndex) - length);
-            if (ret == -1 )
-                break;
-            length += ret;
-        } while (length < eIndex - bIndex);
-        return new String (buff, 0, length);
-    }
-
-    /**
-     * returns number of charecter in the clob.
-     * @return char length
-     * @throws IOException, SQLException
-     */
-    synchronized long getCharLength () throws IOException, SQLException {
-        Reader reader = getReader(0);
-        char [] dummy = new char [4 * 1024];
-        int length = 0;
-        do {
-            long ret = reader.read (dummy);
-            if (ret == -1) break;
-            length += ret;
-        }while (true);
-        return length;
+    public synchronized long getCharLength() throws IOException {
+        checkIfValid();
+        return
+            UTF8Util.skipUntilEOF(new BufferedInputStream(getRawByteStream()));
     }
 
     /**
      * Returns the size of the Clob in bytes.
+     *
      * @return Number of bytes in the <code>CLOB</code> value.
-     * @throws IOException
+     * @throws IOException if accessing the underlying I/O resources fail
      */
-    long getByteLength () throws IOException {
-        return super.getLength();
+    public synchronized long getByteLength () throws IOException {
+        checkIfValid();
+        return this.bytes.getLength();
     }
 
     /**
-     * inserts a string at a given postion.
-     * @param str 
-     * @param pos byte postion
-     * @return current byte postion
-     * @throws IOException
-     * @throws StandardException
-     * @throws SQLException
+     * Inserts a string at the given position.
+     *
+     * @param str the string to insert
+     * @param insertionPoint the character position to insert the string at
+     * @return Number of characters inserted.
+     * @throws EOFException if the position is larger than the Clob length +1
+     * @throws IOException if accessing the underlying I/O resources fail
+     * @throws SQLException if accessing the underlying resources fail
      */
-    synchronized long insertString (String str, long pos)
-                 throws IOException, SQLException, StandardException {
-        int len = str.length();
-        if (pos == super.getLength()) {
-            byte b [] = getByteFromString (str);
-            long l = write (b, 0, b.length, pos);
-            return str.length();
-        }
-        long endPos = getStreamPosition (pos, len);
-        endPos = (endPos < 0) ? getLength() : pos + endPos;
-        replaceBytes (getByteFromString (str), pos, endPos);
+    public synchronized long insertString (String str, long insertionPoint)
+                 throws IOException, SQLException {
+        checkIfValid();
+        if (insertionPoint < 1) {
+            throw new IllegalArgumentException(
+                "Position must be positive: " + insertionPoint);
+        }
+        long byteInsertionPoint = getBytePosition(insertionPoint);
+        long curByteLength = this.bytes.getLength();
+        byte[] newBytes = getByteFromString(str);
+        // See if we are appending or replacing bytes.
+        if (byteInsertionPoint == curByteLength) {
+            try {
+                this.bytes.write(newBytes, 0, newBytes.length,
+                    byteInsertionPoint);
+            } catch (StandardException se) {
+                throw Util.generateCsSQLException(se);
+            }
+        } else {
+            // Calculate end position of the byte block to replace.
+            // Either we only replace bytes, or we replace and append bytes.
+            long endPos;
+            try {
+                endPos = getBytePosition(insertionPoint + str.length());
+                // Must reset the position cache here, as the last obtained
+                // one may be invalid after we replace the bytes (because of
+                // the variable number of bytes per char).
+                this.posCache.updateCachedPos(
+                    insertionPoint, byteInsertionPoint);
+            } catch (EOFException eofe) {
+                endPos = curByteLength; // We replace and append.
+            }
+            try {
+                this.bytes.replaceBytes(newBytes, byteInsertionPoint, endPos);
+            } catch (StandardException se) {
+                throw Util.generateCsSQLException(se);
+            }
+        }
         return str.length();
     }
 
     /**
-     * Converts a string into utf8 byte array.
-     * @param str 
-     * @return utf8 bytes array
+     * Tells if this Clob is intended to be writable.
+     *
+     * @return <code>true</code>
+     */
+    public boolean isWritable() {
+        return true;
+    }
+
+    /**
+     * Truncate the Clob to the specifiec size.
+     *
+     * @param newLength the new length, in characters, of the Clob
+     * @throws IOException if accessing the underlying I/O resources fails
+     */
+    public synchronized void truncate(long newLength)
+            throws IOException, SQLException {
+        checkIfValid();
+        try {
+            this.bytes.truncate(newLength);
+            if (newLength <= this.posCache.getCharPos()) {
+                // Reset the cache if last cached position has been cut away.
+                this.posCache.reset();
+            }
+        } catch (StandardException se) {
+            throw Util.generateCsSQLException(se);
+        }
+    }
+
+    /**
+     * Converts a string into the modified UTF-8 byte encoding.
+     *
+     * @param str string to represent with modified UTF-8 encoding
+     * @return Byte array representing the string in modified UTF-8 encoding.
      */
     private byte[] getByteFromString (String str) {
         //create a buffer with max size possible
@@ -240,4 +346,100 @@
         System.arraycopy (buffer, 0, buff, 0, len);
         return buff;
     }
+
+    /**
+     * Copies the content of another Clob into this one.
+     *
+     * @param clob the Clob to copy from
+     * @throws IOException if accessing I/O resources fail (both read and write)
+     * @throws SQLException if accessing underlying resources fail
+     */
+    private void copyClobContent(InternalClob clob)
+            throws IOException, SQLException {
+        try {
+            this.bytes.copyData(clob.getRawByteStream(), clob.getByteLength());
+        } catch (StandardException se) {
+            throw Util.generateCsSQLException(se);
+        }
+    }
+
+    /**
+     * Makes sure the Clob has not been released.
+     * <p>
+     * All operations are invalid on a released Clob.
+     *
+     * @throws IllegalStateException if the Clob has been released
+     */
+    private final void checkIfValid() {
+        if (this.released) {
+            throw new IllegalStateException(
+                "The Clob has been released and is not valid");
+        }
+    }
+
+    /**
+     * A simple class to hold the byte position for a character position.
+     * <p>
+     * The implementation is very simple and is basically intended to speed up
+     * writing a sequence of consequtive characters one character at a time.
+     * Even though this should be avoided if possible, the penalty of updating a
+     * large Clob this way and finding the correct byte position by navigating
+     * from the start of the byte stream each time is so severe that a simple
+     * caching mechanism should be in place. Note that for other encodings than
+     * UTF-8, this might not be a problem if the mapping between character
+     * position and byte position is one-to-one.
+     * <p>
+     * Note that to ensure consistency between character and byte positions,
+     * access to this class must be synchronized externally to avoid caller 1
+     * getting the character position, then caller 2 updates the cached values
+     * and then caller 1 gets the updated byte position.
+     */
+    //@NotThreadSafe
+    private static class CharToBytePositionCache {
+        private long charPos = 1L;
+        private long bytePos = 0L;
+
+        CharToBytePositionCache() {}
+
+        /**
+         * Returns the last cached byte position.
+         *
+         * @return The byte position for the last cached character position.
+         */
+        long getBytePos() {
+            return this.bytePos;
+        }
+
+        /**
+         * Returns the last cached character position.
+         *
+         * @return The last cached character position.
+         */
+        long getCharPos() {
+            return this.charPos;
+        }
+
+        /**
+         * Updates the position cache.
+         *
+         * @param charPos the character position to cache the byte position for
+         * @param bytePos byte position for the specified character position
+         */
+        void updateCachedPos(long charPos, long bytePos) {
+            if (charPos -1 > bytePos) {
+                throw new IllegalArgumentException("(charPos -1) cannot be " +
+                    "greater than bytePos; " + (charPos -1) + " > " + bytePos);
+            }
+            this.charPos = charPos;
+            this.bytePos = bytePos;
+        }
+
+        /**
+         * Resets the position cache.
+         */
+        void reset() {
+            this.charPos = 1L;
+            this.bytePos = 0L;
+        }
+    } // End internal class CharToBytePositionCache
 }

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobUtf8Writer.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobUtf8Writer.java?view=diff&rev=544814&r1=544813&r2=544814
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobUtf8Writer.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobUtf8Writer.java Wed Jun  6 04:46:43 2007
@@ -36,7 +36,7 @@
  */
 final class ClobUtf8Writer extends Writer {
     private ClobStreamControl control;    
-    private long pos; // Position in bytes.
+    private long pos; // Position in characters.
     private boolean closed;
     
     /**
@@ -107,9 +107,6 @@
             IOException ioe = new IOException (e.getMessage());
             ioe.initCause (e);
             throw ioe;
-        }
-        catch (StandardException se) {
-            throw new IOException (se.getMessage());
         }
     }
 }

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=544814&r1=544813&r2=544814
==============================================================================
--- 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  6 04:46:43 2007
@@ -30,12 +30,9 @@
 import org.apache.derby.impl.jdbc.ConnectionChild;
 import org.apache.derby.impl.jdbc.EmbedConnection;
 import org.apache.derby.impl.jdbc.Util;
-import org.apache.derby.impl.jdbc.UTF8Reader;
 import org.apache.derby.impl.jdbc.ReaderToAscii;
 
 import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.StringReader;
 import java.io.Reader;
 import java.io.IOException;
 import java.io.EOFException;
@@ -71,49 +68,40 @@
  */
 final class EmbedClob extends ConnectionChild implements Clob
 {
-    // clob is either a string or stream
-    private boolean         materialized;
-    private InputStream     myStream;
-    private ClobStreamControl control;
 
-    //This boolean variable indicates whether the Clob object has
-    //been invalidated by calling free() on it
+    /**
+     * The underlying Clob object, which may change depending on what the user
+     * does with the Clob.
+     */
+    private InternalClob clob;
+
+    /** Tells whether the Clob has been freed or not. */
     private boolean isValid = true;
 
     /**
-     * This constructor is used to create a empty Clob object. It is used by the
-     * Connection interface method createClob().
-     *
-     * @param clobString A String object containing the data to be stores in the
-     *        Clob.
+     * Creates an empty Clob object.
      *
      * @param con The Connection object associated with this EmbedClob object.
      * @throws SQLException
      *
      */
-    EmbedClob(String clobString,EmbedConnection con) throws SQLException {
+    EmbedClob(EmbedConnection con) throws SQLException {
         super(con);
-        materialized = true;
-        control = new ClobStreamControl (con.getDBName(), this);
-        try {
-            control.insertString (clobString, 0);
-        }
-        catch (IOException e) {
-            throw Util.setStreamFailure (e);
-        }
-        catch (StandardException se) {
-            throw Util.generateCsSQLException (se);
-        }
+        this.clob = new ClobStreamControl (con.getDBName(), this);
     }
 
     /**
+     * Creates a Clob on top of a data value descriptor.
+     * <p>
      * This constructor should only be called by {@link EmbedResultSet#getClob}.
+     * The data value descriptor may provide a <code>String</code> or a stream
+     * as the source of the Clob.
      *
-     * @param dvd 
-     * @param con 
+     * @param dvd data value descriptor providing the Clob source
+     * @param con associated connection for the Clob
      * @throws StandardException
      */
-    protected EmbedClob(DataValueDescriptor dvd, EmbedConnection con)
+    protected EmbedClob(EmbedConnection con, DataValueDescriptor dvd)
         throws StandardException
     {
         super(con);
@@ -122,14 +110,12 @@
         if (SanityManager.DEBUG)
             SanityManager.ASSERT(!dvd.isNull(), "clob is created on top of a null column");
 
-        myStream = dvd.getStream();
-        if (myStream == null)
-        {
-            control = new ClobStreamControl (con.getDBName(), this);
-            materialized = true;
+        InputStream storeStream = dvd.getStream();
+        // See if a String or a stream will be the source of the Clob.
+        if (storeStream == null) {
+            this.clob = new ClobStreamControl(con.getDBName(), this);
             try {
-                String str = dvd.getString();
-                control.insertString (dvd.getString(), 0);
+                this.clob.insertString (dvd.getString(), 1L);
             }
             catch (SQLException sqle) {
                 throw StandardException.newException (sqle.getSQLState(), sqle);
@@ -138,9 +124,7 @@
                 throw StandardException.newException (
                                         SQLState.SET_STREAM_FAILURE, e);
             }
-        }
-        else
-        {
+        } else {
             /*
              We are expecting this stream to be a FormatIdInputStream with an
              OverflowInputStream inside. FormatIdInputStream implements
@@ -155,102 +139,61 @@
              should not break the ASSERT below.
              */
             if (SanityManager.DEBUG)
-                SanityManager.ASSERT(myStream instanceof Resetable);
+                SanityManager.ASSERT(storeStream instanceof Resetable);
 
             try {
-                ((Resetable) myStream).initStream();
+                ((Resetable) storeStream).initStream();
             } catch (StandardException se) {
                 if (se.getMessageId().equals(SQLState.DATA_CONTAINER_CLOSED)) {
                     throw StandardException
                             .newException(SQLState.BLOB_ACCESSED_AFTER_COMMIT);
                 }
+                throw se;
             }
+            this.clob = new StoreStreamClob(storeStream, this);
         }
     }
 
-
     /**
-     * Returns the number of characters
-     * in the <code>CLOB</code> value
+     * Returns the number of characters in the <code>CLOB</code> value
      * designated by this <code>Clob</code> object.
-     * @return length of the <code>CLOB</code> in characters
-     * @exception SQLException if there is an error accessing the
-     * length of the <code>CLOB</code>
+     *
+     * @return The length of the <code>CLOB</code> in number of characters.
+     * @exception SQLException if obtaining the length fails
      */
     public long length() throws SQLException
     {
         //call checkValidity to exit by throwing a SQLException if
         //the Clob object has been freed by calling free() on it
         checkValidity();
-        // if we have a string, not a stream
         try {
-            if (materialized)
-                return control.getCharLength ();
-        }
-        catch (IOException e) {
-            throw Util.setStreamFailure (e);
-        }
-
-
-        Object synchronization = getConnectionSynchronization();
-        synchronized (synchronization)
-        {
-            Reader clobReader = null;
-            setupContextStack();
-            try {
-
-                clobReader = getCharacterStream();
-                long clobLength = 0;
-                for (;;)
-                {
-                    long size = clobReader.skip(32 * 1024);
-                    if (size == 0L)
-                        break;
-                    clobLength += size;
-                }
-                clobReader.close();
-                clobReader = null;
-
-                return clobLength;
-            }
-            catch (Throwable t)
-            {
-                throw noStateChangeLOB(t);
-            }
-            finally
-            {
-                if (clobReader != null) {
-                    try {
-                        clobReader.close();
-                    } catch (IOException ioe) {
-                    }
-                }
-                restoreContextStack();
-            }
+            return this.clob.getCharLength();
+        } catch (IOException e) {
+            throw Util.setStreamFailure(e);
         }
     }
 
     /**
-     * Returns a copy of the specified substring
-     * in the <code>CLOB</code> value
+     * Returns a copy of the specified substring in the <code>CLOB</code> value
      * designated by this <code>Clob</code> object.
-     * The substring begins at position
-     * <code>pos</code> and has up to <code>length</code> consecutive
-     * characters. The starting position must be between 1 and the length
-     * of the CLOB plus 1. This allows for zero-length CLOB values, from
-     * which only zero-length substrings can be returned.
+     * <p>
+     * The substring begins at position <code>pos</code> and has up to
+     * * <code>length</code> consecutive characters. The starting position must be
+     * between 1 and the length of the CLOB plus 1. This allows for zero-length
+     * CLOB values, from which only zero-length substrings can be returned.
      * If a larger length is requested than there are characters available,
      * characters from the start position to the end of the CLOB are returned.
+     * <p>
+     * <em>NOTE</em>: If the starting position is the length of the CLOB plus 1,
+     * zero characters are returned regardless of the length requested.
+     *
      * @param pos the first character of the substring to be extracted.
-     *            The first character is at position 1.
+     *    The first character is at position 1.
      * @param length the number of consecutive characters to be copied
      * @return a <code>String</code> that is the specified substring in
-     *         the <code>CLOB</code> value designated by this <code>Clob</code> object
-     * @exception SQLException if there is an error accessing the
-     * <code>CLOB</code>
-
-     * NOTE: If the starting position is the length of the CLOB plus 1,
-     * zero characters are returned regardless of the length requested.
+     *    the <code>CLOB</code> value designated by this <code>Clob</code> object
+     * @exception SQLException if there is an error accessing the 
+     *    <code>CLOB</code>
      */
     public String getSubString(long pos, int length) throws SQLException
     {
@@ -265,141 +208,68 @@
             throw Util.generateCsSQLException(
                 SQLState.BLOB_NONPOSITIVE_LENGTH, new Integer(length));
 
-        // if we have a string, not a stream
-        if (materialized)
-        {
-            try {
-                long sLength = control.getCharLength ();
-                if (sLength + 1 < pos)
-                    throw Util.generateCsSQLException(
-                        SQLState.BLOB_POSITION_TOO_LARGE, new Long(pos));
-                int endIndex = ((int) pos) + length - 1;
-                // cannot go over length of string
-                    return control.getSubstring (pos - 1,
-                            (sLength > endIndex ? endIndex : sLength));
-            }
-            catch (IOException e) {
-                throw Util.setStreamFailure (e);
-            }
-        }
-
-        Object synchronization = getConnectionSynchronization();
-        synchronized (synchronization)
-        {
-            setupContextStack();
-
-            UTF8Reader clobReader = null;
-            try {
-
-                clobReader = getCharacterStreamAtPos(pos, synchronization);
-                if (clobReader == null)
-                    throw StandardException.newException(SQLState.BLOB_POSITION_TOO_LARGE, new Long(pos));
-
-                StringBuffer sb = new StringBuffer(length);
-                int remainToRead = length;
-                while (remainToRead > 0) {
-
-                    int read = clobReader.readInto(sb, remainToRead);
-                    if (read == -1)
-                        break;
-
-                    remainToRead -= read;
+        String result;
+        // An exception will be thrown if the position is larger than the Clob.
+        try {
+            Reader reader = this.clob.getReader(pos);
+            char[] chars = new char[length];
+            int charsRead = 0;
+            // Read all the characters requested, or until EOF is reached.
+            while (charsRead < length) {
+                int read = reader.read(chars, charsRead, length - charsRead);
+                if (read == -1) {
+                    break;
                 }
-                clobReader.close();
-                clobReader = null;
-
-                return sb.toString();
-            }
-            catch (Throwable t)
-            {
-                throw noStateChangeLOB(t);
+                charsRead += read;
             }
-            finally
-            {
-                if (clobReader != null)
-                    clobReader.close();
-                restoreContextStack();
+            reader.close();
+            // If we have an empty Clob or requested length is zero, return "".
+            if (charsRead == 0) {
+                result = "";
+            } else {
+                result = String.copyValueOf(chars, 0, charsRead);
             }
+        } catch (EOFException eofe) {
+            throw Util.generateCsSQLException(
+                                        SQLState.BLOB_POSITION_TOO_LARGE, eofe);
+        } catch (IOException ioe) {
+            throw Util.setStreamFailure(ioe);
         }
+        return result;
     }
 
     /**
-     * Gets the <code>Clob</code> contents as a Unicode stream.
-     * @return a Unicode stream containing the <code>CLOB</code> data
+     * Gets the <code>Clob</code> contents as a stream of characters.
+     * @return A character stream containing the <code>CLOB</code> data.
      * @exception SQLException if there is an error accessing the
-     * <code>CLOB</code>
+     *    <code>CLOB</code>
      */
     public java.io.Reader getCharacterStream() throws SQLException
     {
         //call checkValidity to exit by throwing a SQLException if
         //the Clob object has been freed by calling free() on it
         checkValidity();
-
-
-        Object synchronization = getConnectionSynchronization();
-        synchronized (synchronization)
-        {
-            setupContextStack();
-
-            try {
-                            if (materialized) {
-                                return control.getReader (0);
-                            }
-
-                            return getCharacterStreamAtPos(1, synchronization);
-            }
-            catch (Throwable t)
-            {
-                throw noStateChangeLOB(t);
-            }
-            finally
-            {
-                restoreContextStack();
-            }
+        try {
+            return this.clob.getReader(1L);
+        } catch (IOException ioe) {
+            throw Util.setStreamFailure(ioe);
         }
     }
 
     /**
      * Gets the <code>CLOB</code> value designated by this <code>Clob</code>
      * object as a stream of Ascii bytes.
-     * @return an ascii stream containing the <code>CLOB</code> data
+     * @return An Ascii stream containing the <code>CLOB</code> data. Valid values
+     *    in the stream are 0 - 255.
      * @exception SQLException if there is an error accessing the
-     * <code>CLOB</code> value
+     *    <code>CLOB</code> value
      */
     public java.io.InputStream getAsciiStream() throws SQLException
     {
-                //call checkValidity to exit by throwing a SQLException if
-                //the Clob object has been freed by calling free() on it
-                checkValidity();
+        // Validity is checked in getCharacterStream().
         return new ReaderToAscii(getCharacterStream());
     }
 
-    private UTF8Reader getCharacterStreamAtPos(long position, Object synchronization)
-    throws IOException, StandardException {
-        UTF8Reader clobReader = null;
-        if (materialized)
-            clobReader = new UTF8Reader (control.getInputStream (0), 0,
-                                            control.getByteLength(),
-                                    this, control);
-        else {
-            ((Resetable)myStream).resetStream();
-            clobReader = new UTF8Reader(myStream, 0, this, synchronization);
-        }
-
-        // skip to the correct position (pos is one based)
-        long remainToSkip = position - 1;
-        while (remainToSkip > 0) {
-            long skipBy = clobReader.skip(remainToSkip);
-            if (skipBy == 0L)
-                return null;
-
-            remainToSkip -= skipBy;
-        }
-
-        return clobReader;
-    }
-
-
     /**
      * Determines the character position at which the specified substring
      * <code>searchStr</code> appears in the <code>CLOB</code> value.  The search
@@ -442,17 +312,18 @@
         //call checkValidity to exit by throwing a SQLException if
         //the Clob object has been freed by calling free() on it
         checkValidity();
+        if (start < 1)
+            throw Util.generateCsSQLException(
+                            SQLState.BLOB_BAD_POSITION, new Long(start));
+        if (searchStr == null)
+            throw Util.generateCsSQLException(
+                            SQLState.BLOB_NULL_PATTERN_OR_SEARCH_STR);
+        if (searchStr == "")
+            return start; // match DB2's SQL LOCATE function
 
         boolean pushStack = false;
         try
         {
-            if (start < 1)
-                throw StandardException.newException(
-                    SQLState.BLOB_BAD_POSITION, new Long(start));
-            if (searchStr == null)
-                throw StandardException.newException(SQLState.BLOB_NULL_PATTERN_OR_SEARCH_STR);
-            if (searchStr == "")
-                return start; // match DB2's SQL LOCATE function
 
             Object synchronization = getConnectionSynchronization();
             synchronized (synchronization)
@@ -461,9 +332,9 @@
                 if (pushStack)
                     setupContextStack();
                 int matchCount = 0;
-                long pos = start - 1;
+                long pos = start;
                 long newStart = -1;
-                Reader reader = getCharacterStreamAtPos (start, this);
+                Reader reader = this.clob.getReader(start);
                 char [] tmpClob = new char [4096];
                 boolean reset;
                 for (;;) {
@@ -471,25 +342,21 @@
                     int readCount = reader.read (tmpClob);
                     if (readCount == -1)
                         return -1;
-                    if (readCount == 0)
-                        continue;
                     for (int clobOffset = 0;
                                 clobOffset < readCount; clobOffset++) {
-                        if (tmpClob [clobOffset]
-                                        == searchStr.charAt (matchCount)) {
+                        if (tmpClob[clobOffset]
+                                        == searchStr.charAt(matchCount)) {
                             //find the new starting position in
                             // case this match is unsuccessful
                             if (matchCount != 0 && newStart == -1
-                                    && tmpClob [clobOffset]
-                                    == searchStr.charAt (0)) {
+                                    && tmpClob[clobOffset]
+                                    == searchStr.charAt(0)) {
                                 newStart = pos + clobOffset + 1;
                             }
                             matchCount ++;
                             if (matchCount == searchStr.length()) {
-                                //return after converting the position
-                                //to 1 based index
                                 return pos + clobOffset
-                                        - searchStr.length() + 1 + 1;
+                                        - searchStr.length() + 1;
                             }
                         }
                         else {
@@ -506,8 +373,7 @@
                                 if (newStart < pos) {
                                     pos = newStart;
                                     reader.close();
-                                    reader = getCharacterStreamAtPos
-                                                (newStart + 1, this);
+                                    reader = this.clob.getReader(newStart);
                                     newStart = -1;
                                     reset = true;
                                     break;
@@ -524,20 +390,15 @@
                 }
 
             }
-        }
-        catch (Throwable t)
-        {
-            throw noStateChangeLOB(t);
-        }
-        finally
-        {
-            if (pushStack)
+        } catch (IOException ioe) {
+            throw Util.setStreamFailure(ioe);
+        } finally {
+            if (pushStack) {
                 restoreContextStack();
+            }
         }
-
     }
 
-
     /**
      * Determines the character position at which the specified
      * <code>Clob</code> object <code>searchstr</code> appears in this
@@ -557,35 +418,26 @@
         //call checkValidity to exit by throwing a SQLException if
         //the Clob object has been freed by calling free() on it
         checkValidity();
+        if (start < 1)
+            throw Util.generateCsSQLException(
+                                SQLState.BLOB_BAD_POSITION, new Long(start));
+        if (searchClob == null)
+            throw Util.generateCsSQLException(
+                                SQLState.BLOB_NULL_PATTERN_OR_SEARCH_STR);
 
         boolean pushStack = false;
         try
         {
-            if (start < 1)
-                throw StandardException.newException(
-                    SQLState.BLOB_BAD_POSITION, new Long(start));
-            if (searchClob == null)
-                throw StandardException.newException(SQLState.BLOB_NULL_PATTERN_OR_SEARCH_STR);
-
             synchronized (getConnectionSynchronization())
             {
                 char[] subPatternChar = new char[1024];
 
                 boolean seenOneCharacter = false;
 
-                //System.out.println("BEGIN CLOB SEARCH @ " + start);
-
 restartScan:
                 for (;;) {
-
                     long firstPosition = -1;
-
                     Reader patternReader = searchClob.getCharacterStream();
-
-                    //System.out.println("RESTART CLOB SEARCH @ " + start);
-
-                    try {
-
                         for (;;) {
 
                             int read = patternReader.read(subPatternChar, 0, subPatternChar.length);
@@ -593,20 +445,15 @@
                                 //empty pattern
                                 if (!seenOneCharacter)
                                     return start; // matches DB2 SQL LOCATE function
-
                                 return firstPosition;
                             }
                             if (read == 0) {
-                                //System.out.println("STUCK IN READ 0 HELL");
                                 continue;
                             }
-
                             seenOneCharacter = true;
 
                             String subPattern = new String(subPatternChar, 0, read);
-                    //System.out.println("START CLOB SEARCH @ " + start + " -- " + subPattern);
                             long position = position(subPattern, start);
-                    //System.out.println("DONE SUB CLOB SEARCH @ " + start + " -- " + position);
                             if (position == -1) {
                                 // never seen any match
                                 if (firstPosition == -1)
@@ -626,34 +473,16 @@
 
                             // read is the length of the subPattern string
                             start = position + read;
-                    }
-                    } finally {
-                        patternReader.close();
-                    }
-                }
-            }
-        }
-        catch (Throwable t)
-        {
-            throw noStateChangeLOB(t);
-        }
-        finally
-        {
-            if (pushStack)
+                    } // End inner for loop
+            } // End outer for loop
+        } // End synchronized block
+        } catch (IOException ioe) {
+            throw Util.setStreamFailure(ioe);
+        } finally {
+            if (pushStack) {
                 restoreContextStack();
+            }
         }
-
-    }
-
-
-    /*
-     If we have a stream, release the resources associated with it.
-     */
-    protected void finalize()
-    {
-        // System.out.println("finalizer called");
-        if (!materialized)
-            ((Resetable)myStream).closeStream();
     }
 
     /**
@@ -699,24 +528,28 @@
      * @exception SQLException Feature not implemented for now.
      */
     public int setString(long pos, String str, int offset, int len)
-    throws SQLException {
+            throws SQLException {
+        checkValidity();
+        if (pos < 1)
+            throw Util.generateCsSQLException(
+                SQLState.BLOB_BAD_POSITION, new Long(pos));
         try {
-            if (!materialized) {
-                control.copyData(myStream, length());
-                materialized = true;
-            }
-            long charPos = control.getStreamPosition(0, pos - 1);
-            if (charPos == -1)
-                throw Util.generateCsSQLException(
-                        SQLState.BLOB_POSITION_TOO_LARGE, "" + pos);
-            return (int) control.insertString(str.substring (offset,
-                    (offset + len)), charPos);
+            if (!this.clob.isWritable()) {
+                makeWritableClobClone();
+            }
+            // Note that Clob.length() +1 is a valid position for setString.
+            // If the position is larger than this, an EOFException will be
+            // thrown. This is cheaper then getting the length up front.
+            this.clob.insertString(str.substring(offset, (offset + len)),
+                                   pos);
+        } catch (EOFException eofe) {
+            throw Util.generateCsSQLException(
+                        SQLState.BLOB_POSITION_TOO_LARGE,
+                        new Long(pos));
         } catch (IOException e) {
             throw Util.setStreamFailure(e);
         }
-        catch (StandardException se) {
-            throw Util.generateCsSQLException (se);
-        }
+        return str.length();
     }
 
     /**
@@ -730,8 +563,9 @@
      * @exception SQLException Feature not implemented for now.
      */
     public java.io.OutputStream setAsciiStream(long pos) throws SQLException {
+        checkValidity();
         try {
-            return new ClobAsciiStream (control.getWriter(pos - 1));
+            return new ClobAsciiStream (this.clob.getWriter(pos));
         } catch (IOException e) {
             throw Util.setStreamFailure(e);
         }
@@ -748,16 +582,14 @@
      * @exception SQLException Feature not implemented for now.
      */
     public java.io.Writer setCharacterStream(long pos) throws SQLException {
+        checkValidity();
         try {
-            if (!materialized) {
-                control.copyData(myStream, length());
-                materialized = true;
+            if (!this.clob.isWritable()) {
+                makeWritableClobClone();
             }
-            return control.getWriter(pos - 1);
-        } catch (IOException e) {
-            throw Util.setStreamFailure(e);
-        } catch (StandardException se) {
-            throw Util.generateCsSQLException (se);
+            return this.clob.getWriter(pos);
+        } catch (IOException ioe) {
+            throw Util.setStreamFailure(ioe);
         }
     }
 
@@ -781,32 +613,25 @@
     //
     /////////////////////////////////////////////////////////////////////////
     /**
-     * This method frees the <code>Clob</code> object and releases the resources the resources
-     * that it holds.  The object is invalid once the <code>free</code> method
+     * Frees the <code>Clob</code> and releases the resources that it holds.
+     * <p>
+     * The object is invalid once the <code>free</code> method
      * is called. If <code>free</code> is called multiple times, the
      * subsequent calls to <code>free</code> are treated as a no-op.
      *
-     * @throws SQLException if an error occurs releasing
-     * the Clob's resources
+     * @throws SQLException if an error occurs releasing the Clobs resources
      */
     public void free()
         throws SQLException {
-        //calling free() on a already freed object is treated as a no-op
-        if (!isValid) return;
-
-        //now that free has been called the Clob object is no longer
-        //valid
-        isValid = false;
-
-        if (!materialized) {
-            ((Resetable)myStream).closeStream();
-        }
-        else {
+        if (this.isValid) {
+            this.isValid = false;
+            // Release and nullify the internal Clob.
             try {
-                control.free();
-            }
-            catch (IOException e) {
+                this.clob.release();
+            } catch (IOException e) {
                 throw Util.setStreamFailure(e);
+            } finally {
+                this.clob = null;
             }
         }
     }
@@ -817,31 +642,35 @@
     }
 
     /*
-    **
-    */
-
-    static SQLException noStateChangeLOB(Throwable t) {
-        if (t instanceof StandardException)
-        {
-            // container closed means the blob or clob was accessed after commit
-            if (((StandardException) t).getMessageId().equals(SQLState.DATA_CONTAINER_CLOSED))
-            {
-                t = StandardException.newException(SQLState.BLOB_ACCESSED_AFTER_COMMIT);
-            }
-        }
-        return org.apache.derby.impl.jdbc.EmbedResultSet.noStateChangeException(t);
-    }
-
-    /*
-     * Checks is isValid is true. If it is not true throws
-     * a SQLException stating that a method has been called on
-     * an invalid LOB object
+     * Checks if the Clob is valid.
+     * <p>
+     * A Clob is invalidated when {@link #free} is called or if the parent
+     * connection is closed.
      *
-     * throws SQLException if isValid is not true.
+     * @throws SQLException if the Clob is not valid
      */
     private void checkValidity() throws SQLException{
         if(!isValid)
             throw newSQLException(SQLState.LOB_OBJECT_INVALID);
         localConn.checkIfClosed();
+    }
+
+    /**
+     * Makes a writable clone of the current Clob.
+     * <p>
+     * This is called when we have a {@link StoreStreamClob} and the user calls
+     * a method updating the content of the Clob. A temporary Clob will then be
+     * created to hold the updated content.
+     *
+     * @throws IOException if accessing underlying I/O resources fail
+     * @throws SQLException if accessing underlying resources fail
+     */
+    private void makeWritableClobClone()
+            throws IOException, SQLException {
+        InternalClob toBeAbandoned = this.clob;
+        this.clob = ClobStreamControl.cloneClobContent(
+                        getEmbedConnection().getDBName(),
+                        this, toBeAbandoned);
+        toBeAbandoned.release();
     }
 }

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java?view=diff&rev=544814&r1=544813&r2=544814
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java Wed Jun  6 04:46:43 2007
@@ -2256,7 +2256,7 @@
 	*/
 	public Clob createClob() throws SQLException {
 		checkIfClosed();
-		return new EmbedClob("", this);
+		return new EmbedClob(this);
 	}
 
 	/**

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java?view=diff&rev=544814&r1=544813&r2=544814
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedResultSet.java Wed Jun  6 04:46:43 2007
@@ -4062,7 +4062,7 @@
 				if (pushStack)
 					setupContextStack();
 
-				return new EmbedClob(dvd, getEmbedConnection());
+				return new EmbedClob(getEmbedConnection(), dvd);
 			} catch (Throwable t) {
 				throw handleException(t);
 			} finally {

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/ClobTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/ClobTest.java?view=diff&rev=544814&r1=544813&r2=544814
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/ClobTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/ClobTest.java Wed Jun  6 04:46:43 2007
@@ -128,10 +128,7 @@
     
     private static final ExemptClobMD [] emd = new ExemptClobMD [] {
         new ExemptClobMD( "getCharacterStream", new Class[] { long.class, long.class } ,true,true),
-        new ExemptClobMD( "setAsciiStream",     new Class[] { long.class } ,false,true),
-	new ExemptClobMD( "setCharacterStream", new Class[] { long.class } ,true,true),
 	new ExemptClobMD( "setString",          new Class[] { long.class, String.class } ,false,true),
-	new ExemptClobMD( "setString",          new Class[] { long.class, String.class, int.class, int.class},false,true),
 	new ExemptClobMD( "truncate",           new Class[] { long.class },false,true),
         new ExemptClobMD( "free",               null,true,true)
     };
@@ -201,7 +198,7 @@
      *
      */
     public void testFreeandMethodsAfterCallingFree()
-        throws SQLException {
+        throws IllegalAccessException, InvocationTargetException, SQLException {
             InputStream asciiStream = clob.getAsciiStream();
             Reader charStream  = clob.getCharacterStream();
             clob.free();
@@ -224,7 +221,8 @@
      * get the list of methods present in the interface
      * @param LOB an instance of the Clob interface implementation
      */
-    void buildMethodList(Object LOB) {
+    void buildMethodList(Object LOB)
+            throws IllegalAccessException, InvocationTargetException {
         //If the given method throws the correct exception
         //set this to true and add it to the 
         boolean valid = true;
@@ -271,30 +269,27 @@
     }
     
     /**
-     *Checks if the method throws a SQLFeatureNotSupportedException
-     *@param m The method object that needs to be verified to see if it 
-     *         is exempted
-     *@return true if the given method does not throw the required SQLException
+     * Checks if the method is to be exempted from testing or not.
      *
+     * @param m the method to check for exemption
+     * @return <code>false</code> if the method shall be tested,
+     *      <code>true</code> if the method is exempted and shall not be tested.
      */
     boolean checkIfExempted(Method m) {
         ExemptClobMD md = excludedMethodSet.get(m);
-        
-        if(md != null && usingDerbyNetClient()) { 
-            if(md.getIfClientFramework()) 
-                return true;
-            else
-                return false;
-        } 
-        if(md != null && usingEmbedded()) {
-            if(md.getIfEmbeddedFramework())
-                return true;
-            else
-                return false;
+        boolean isExempted = false;
+        if (md != null) {
+            if (usingDerbyNetClient()) {
+                isExempted = md.getIfClientFramework();
+            } else if (usingEmbedded()) {
+                isExempted = md.getIfEmbeddedFramework();
+            } else {
+                fail("Unknown test environment/framework");
+            }
         }
-        return false;
+        return isExempted;
     }
-    
+
     /*
      * Checks if the invocation of the method throws a SQLExceptio
      * as expected.
@@ -306,27 +301,20 @@
      *               LOB object
      *
      */
-    boolean checkIfMethodThrowsSQLException(Object LOB,Method method) {
+    boolean checkIfMethodThrowsSQLException(Object LOB,Method method)
+            throws IllegalAccessException, InvocationTargetException {
         try {
             method.invoke(LOB,getNullValues(method.getParameterTypes()));
-        } catch(Throwable e) {
-            if(e instanceof InvocationTargetException) {
-                Throwable cause = e.getCause();
-                if (cause instanceof SQLException ) {
-                    SQLException sqle = (SQLException)cause;
-                    if(sqle.getSQLState().equals("XJ215"))
-                        return true;
-                    else
-                        return false;
-                } else {
-                    return false;
-                }
-                
+        } catch (InvocationTargetException ite) {
+            Throwable cause = ite.getCause();
+            if (cause instanceof SQLException ) {
+                return ((SQLException)cause).getSQLState().equals("XJ215");
             }
+            throw ite;
         }
         return false;
     }
-    
+
     /*
      * Return a array of objects containing the default values for
      * the objects passed in as parameters

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobTest.java?view=diff&rev=544814&r1=544813&r2=544814
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobTest.java Wed Jun  6 04:46:43 2007
@@ -200,16 +200,8 @@
         assertEquals(1, this.clob.setString(9, "c"));
         assertEquals(1, this.clob.setString(5, "a"));
         assertEquals(1, this.clob.setString(6, "b"));
-        if (BaseJDBCTestCase.usingEmbedded()) {
-            assertEquals("Clob content is incorrect",
-                newContent, this.clob.getSubString(1, newContent.length()));
-        } else {
-            // Client currently truncates the Clob when inserting strings.
-            // See DERBY-1286 and DERBY-2652.
-            assertEquals("Clob content is incorrect",
-                newContent.substring(0, 6),
-                this.clob.getSubString(1, newContent.length()));
-        }
+	    assertEquals("Clob content is incorrect",
+            newContent, this.clob.getSubString(1, newContent.length()));
     }
 
     public void testPositionWithString_ASCII_SimplePartialRecurringPattern()
@@ -244,7 +236,7 @@
         final long prefix = 11L;
         final long postfix = 90L;
         char[] tmpChar = new char[1];
-        LoopingAlphabetReader tokenSrc = 
+        LoopingAlphabetReader tokenSrc =
             new LoopingAlphabetReader(1L, CharAlphabet.cjkSubset());
         tokenSrc.read(tmpChar);
         String token = String.copyValueOf(tmpChar);
@@ -253,7 +245,7 @@
         executeTestPositionWithStringToken(token, prefix);
     }
 
-    /* Test ideas
+    /* Test ideas for more tests
      *
      * truncate:
      *      truncate both on in store and from createClob
@@ -262,6 +254,11 @@
      *      truncate with too big size
      *      truncate to 0
      *      truncate to current length
+     *
+     * setString:
+     *      test with null string
+     *      test with offset out of range
+     *      test with length of string to insert out of range
      */
 
     /**

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/_Suite.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/_Suite.java?view=diff&rev=544814&r1=544813&r2=544814
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/_Suite.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/_Suite.java Wed Jun  6 04:46:43 2007
@@ -75,7 +75,8 @@
 		suite.addTest(SetTransactionIsolationTest.suite());
 		suite.addTest(AuthenticationTest.suite());
 		suite.addTest(DriverTest.suite());
-        
+        suite.addTest(ClobTest.suite());
+
         // Old harness .java tests that run using the HarnessJavaTest
         // adapter and continue to use a single master file.
         suite.addTest(JDBCHarnessJavaTest.suite());



Mime
View raw message