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/04/18 18:17:05 UTC

svn commit: r530085 - in /db/derby/code/trunk/java: engine/org/apache/derby/impl/jdbc/ testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/

Author: kristwaa
Date: Wed Apr 18 09:17:01 2007
New Revision: 530085

URL: http://svn.apache.org/viewvc?view=rev&rev=530085
Log:
DERBY-2346: Provide set methods for clob for embedded driver.

Patch contributed by Anurag Shekar.

Added:
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobAsciiStream.java   (with props)
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobStreamControl.java   (with props)
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobUpdateableReader.java   (with props)
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobUtf8Writer.java   (with props)
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobUpdateableReaderTest.java   (with props)
Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedClob.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBInputStream.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBStreamControl.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/UTF8Reader.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BlobClob4BlobTest.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/LobStreamsTest.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/_Suite.java

Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobAsciiStream.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobAsciiStream.java?view=auto&rev=530085
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobAsciiStream.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobAsciiStream.java Wed Apr 18 09:17:01 2007
@@ -0,0 +1,87 @@
+/* 
+   Derby - Class org.apache.derby.impl.jdbc.ClobAsciiStream
+ 
+   Copyright 2006 The Apache Software Foundation or its licensors, as applicable.
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+      http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+ 
+ */
+
+package org.apache.derby.impl.jdbc;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+
+final class ClobAsciiStream extends OutputStream {
+    private Writer writer;
+    
+    ClobAsciiStream (Writer writer){
+        this.writer = writer;
+    }
+
+    /**
+     * Writes the specified byte to this output stream. The general 
+     * contract for <code>write</code> is that one byte is written 
+     * to the output stream. The byte to be written is the eight 
+     * low-order bits of the argument <code>b</code>. The 24 
+     * high-order bits of <code>b</code> are ignored.
+     * <p>
+     * Subclasses of <code>OutputStream</code> must provide an 
+     * implementation for this method. 
+     * 
+     * @param b   the <code>byte</code>.
+     * @exception IOException  if an I/O error occurs. In particular, 
+     *             an <code>IOException</code> may be thrown if the 
+     *             output stream has been closed.
+     */
+    public void write(int b) throws IOException {
+        writer.write (b);
+    }
+
+    /**
+     * Writes <code>len</code> bytes from the specified byte array 
+     * starting at offset <code>off</code> to this output stream. 
+     * The general contract for <code>write(b, off, len)</code> is that 
+     * some of the bytes in the array <code>b</code> are written to the 
+     * output stream in order; element <code>b[off]</code> is the first 
+     * byte written and <code>b[off+len-1]</code> is the last byte written 
+     * by this operation.
+     * <p>
+     * The <code>write</code> method of <code>OutputStream</code> calls 
+     * the write method of one argument on each of the bytes to be 
+     * written out. Subclasses are encouraged to override this method and 
+     * provide a more efficient implementation. 
+     * <p>
+     * If <code>b</code> is <code>null</code>, a 
+     * <code>NullPointerException</code> is thrown.
+     * <p>
+     * If <code>off</code> is negative, or <code>len</code> is negative, or 
+     * <code>off+len</code> is greater than the length of the array 
+     * <code>b</code>, then an <tt>IndexOutOfBoundsException</tt> is thrown.
+     * 
+     * @param b     the data.
+     * @param off   the start offset in the data.
+     * @param len   the number of bytes to write.
+     * @exception IOException  if an I/O error occurs. In particular, 
+     *             an <code>IOException</code> is thrown if the output 
+     *             stream is closed.
+     */
+    public void write(byte[] b, int off, int len) throws IOException {
+        char [] c = new char [len];
+        for (int i = off; i < len; i++) {
+            c [i - off] = (char) b [i];
+        }
+        writer.write(c);
+    }    
+}

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobAsciiStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 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=auto&rev=530085
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobStreamControl.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobStreamControl.java Wed Apr 18 09:17:01 2007
@@ -0,0 +1,219 @@
+/* 
+   Derby - Class org.apache.derby.impl.jdbc.ClobStreamControl
+ 
+   Copyright 2006 The Apache Software Foundation or its licensors, as applicable.
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+      http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+ 
+ */
+package org.apache.derby.impl.jdbc;
+
+import java.io.BufferedInputStream;
+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.reference.SQLState;
+import org.apache.derby.iapi.util.ByteArray;
+
+final class ClobStreamControl extends LOBStreamControl {
+    
+    private ConnectionChild conChild;
+    /**
+     * Constructs ClobStreamControl
+     * @param dbName 
+     * @param conChild
+     */
+    ClobStreamControl (String dbName, ConnectionChild conChild) {
+        super (dbName);
+        this.conChild = conChild;
+    }        
+    
+    /**
+     * Finds the positon in bytes for a utf8 char postion starting 
+     * from startPos (bytes).
+     * @param startPos in bytes
+     * @param charPos in chars
+     * @return stream position for the given char position
+     * @throws IOException
+     */
+    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 >= 0x0001) && (c <= 0x007F)) {
+                //found char of one byte width
+                streamLength++;
+            }
+            else if (c > 0x07FF) {
+                //found char of three byte width
+                if (in.skip (2) < 2) {
+                    //no second and third byte present
+                    throw new UTFDataFormatException();
+                }
+                streamLength += 3;
+                break;
+            }
+            else {
+                //found char of three byte width
+                if (in.skip (1) != 1) {
+                    //no second and third byte present
+                    throw new UTFDataFormatException();
+                }
+                streamLength += 2;
+            }
+        }
+        
+        in.close();
+        return streamLength;        
+    }
+    
+    /**
+     * constructs and returns a writer.
+     * @param pos 
+     * @return Writer
+     * @throws IOException, SQLException
+     */
+    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));
+    }
+    
+    /**
+     * constructs and reurns a reader.
+     * @param pos 
+     * @return 
+     * @throws IOException, SQLException
+     */
+    Reader getReader (long pos) throws IOException, SQLException {
+        Reader isr = new ClobUpdateableReader (
+                (LOBInputStream) getInputStream (0), conChild);
+
+        long leftToSkip = pos;
+        while (leftToSkip > 0) {
+            leftToSkip -= isr.skip (leftToSkip);
+        }
+        return isr;
+    }    
+    
+    /**
+     * returns a substring.
+     * @param bIndex 
+     * @param eIndex 
+     * @return 
+     * @throws IOException, SQLException
+     */
+    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;
+    }
+    
+    /**
+     * returns the size of clob in bytes.
+     * @return 
+     * @throws IOException
+     */
+    long getByteLength () throws IOException {
+        return super.getLength();
+    }
+    
+    /**
+     * inserts a string at a given postion.
+     * @param str 
+     * @param pos byte postion
+     * @return current byte postion
+     * @throws IOException
+     */
+    synchronized long insertString (String str, long pos) 
+                                            throws IOException, SQLException {
+        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);
+        return str.length();
+    }
+    
+    /**
+     * Converts a string into utf8 byte array.
+     * @param str 
+     * @return utf8 bytes array
+     */
+    private byte[] getByteFromString (String str) {
+        //create a buffer with max size possible
+        byte [] buffer = new byte [3 * str.length()];
+        int len = 0;
+        //start decoding
+        for (int i = 0; i < str.length(); i++) {
+            int c = str.charAt (i);
+            if ((c >= 0x0001) && (c <= 0x007F)) {
+                buffer[len++] = (byte) c;
+            }
+            else if (c > 0x07FF) {
+                buffer[len++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
+                buffer[len++] = (byte) (0x80 | ((c >>  6) & 0x3F));
+                buffer[len++] = (byte) (0x80 | ((c >>  0) & 0x3F));
+            }
+            else {
+                buffer[len++] = (byte) (0xC0 | ((c >>  6) & 0x1F));
+                buffer[len++] = (byte) (0x80 | ((c >>  0) & 0x3F));
+            }
+        }
+        byte [] buff = new byte [len];
+        System.arraycopy (buffer, 0, buff, 0, len);
+        return buff;
+    } 
+}

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobStreamControl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 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=auto&rev=530085
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobUpdateableReader.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobUpdateableReader.java Wed Apr 18 09:17:01 2007
@@ -0,0 +1,96 @@
+/*
+ *
+ * Derby - Class ClobUpdateableReader
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
+ */
+package org.apache.derby.impl.jdbc;
+
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * ClobUpdateableReader is used to create Reader over InputStream. This class is
+ * aware that underlying stream can be modified and reinitializes itsef if it 
+ * detects any change in stream hence invalidating the cache so the changes are 
+ * reflected immidiatly.
+ */
+
+final class ClobUpdateableReader extends Reader {
+    
+    private Reader streamReader;
+    private long pos;
+    private LOBInputStream stream;
+    private ConnectionChild conChild;
+    
+    /**
+     * Constructs a Reader over a LOBInputStream.
+     * @param stream 
+     * @throws IOException
+     */
+    ClobUpdateableReader (LOBInputStream stream, ConnectionChild conChild)
+                                                        throws IOException {
+        this.conChild = conChild;
+        this.stream = stream;
+        init (0);
+    }
+        
+    /**
+     * Reads chars into the cbuf char array. Changes made in uderlying storage 
+     * will be reflected immidiatly from the corrent position.
+     * @param cbuf buffer to read into
+     * @param off offet of the cbuf array to start writing read chars
+     * @param len number of chars to be read
+     * @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);
+        }
+        int ret = streamReader.read (cbuf, off, len);
+        if (ret >= 0) {
+            pos += ret;
+        }
+        return ret;
+    }
+
+    /**
+     * Closes the reader.
+     * @throws IOException
+     */
+    public void close() throws IOException {
+        streamReader.close();
+    }
+    
+    /**
+     * Initializes the streamReader and skips to the given position.
+     * @param skip 
+     * @throws IOException
+     */
+    private void init(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);
+            remainToSkip -= skipBy;
+        }
+        pos = skip;
+    }    
+}

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobUpdateableReader.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 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=auto&rev=530085
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobUtf8Writer.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobUtf8Writer.java Wed Apr 18 09:17:01 2007
@@ -0,0 +1,111 @@
+/* 
+   Derby - Class org.apache.derby.impl.jdbc.ClobUtf8Writer
+ 
+   Copyright 2006 The Apache Software Foundation or its licensors, as applicable.
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+      http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+ 
+ */
+
+package org.apache.derby.impl.jdbc;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.sql.SQLException;
+import org.apache.derby.iapi.reference.SQLState;
+import org.apache.derby.iapi.services.i18n.MessageService;
+
+/**
+ * Writer implementation for Clob.
+ */
+final class ClobUtf8Writer extends Writer {
+    private ClobStreamControl control;    
+    private long pos; //postion in bytes
+    private boolean closed;
+    
+    /**
+     * Constructor.
+     * @param control 
+     * @param pos 
+     * @return 
+     */
+    ClobUtf8Writer(ClobStreamControl control, long pos) {
+        this.control = control;
+        this.pos = pos;
+        closed = false;
+    }    
+
+    /**
+     * Flushes the stream.  If the stream has saved any characters from the
+     * various write() methods in a buffer, write them immediately to their
+     * intended destination.  Then, if that destination is another character or
+     * byte stream, flush it.  Thus one flush() invocation will flush all the
+     * buffers in a chain of Writers and OutputStreams.
+     * 
+     * <p> If the intended destination of this stream is an abstraction provided
+     * by the underlying operating system, for example a file, then flushing the
+     * stream guarantees only that bytes previously written to the stream are
+     * passed to the operating system for writing; it does not guarantee that
+     * they are actually written to a physical device such as a disk drive.
+     * 
+     * @throws IOException
+     *          If an I/O error occurs
+     */
+    public void flush() throws IOException {
+        if (closed)
+            throw new IOException (
+                MessageService.getTextMessage (SQLState.LANG_STREAM_CLOSED));
+        //no op
+    }
+
+    /**
+     * Closes the stream, flushing it first. Once the stream has been closed,
+     * further write() or flush() invocations will cause an IOException to be
+     * thrown. Closing a previously closed stream has no effect.
+     */
+    public void close() {
+        closed = true;
+    }
+
+    /**
+     * Writes a portion of an array of characters.
+     * 
+     * @param cbuf
+     *         Array of characters
+     * @param off
+     *         Offset from which to start writing characters
+     * @param len
+     *         Number of characters to write
+     * @throws IOException
+     *          If an I/O error occurs
+     */
+    public void write(char[] cbuf, int off, int len) throws IOException {
+        if (closed)
+            throw new IOException (
+                MessageService.getTextMessage (SQLState.LANG_STREAM_CLOSED));
+        try {
+            long ret = control.insertString (String.copyValueOf (
+                                                    cbuf, off, len), 
+                                              pos);
+            if (ret > 0)
+                pos += ret;
+        }
+        catch (SQLException e) {
+            IOException ioe = new IOException (e.getMessage());
+            ioe.initCause (e);
+            throw ioe;
+        }
+    }
+}

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/ClobUtf8Writer.java
------------------------------------------------------------------------------
    svn:eol-style = native

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=530085&r1=530084&r2=530085
==============================================================================
--- 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 Apr 18 09:17:01 2007
@@ -72,9 +72,9 @@
 final class EmbedClob extends ConnectionChild implements Clob
 {
     // clob is either a string or stream
-    private boolean         isString;
+    private boolean         materialized;
     private InputStream     myStream;
-    private String          myString;
+    private ClobStreamControl control;
     
     //This boolean variable indicates whether the Clob object has
     //been invalidated by calling free() on it
@@ -88,18 +88,29 @@
      *        Clob.
      *
      * @param con The Connection object associated with this EmbedClob object.
+     * @throws SQLException
      *
      */
     
-    EmbedClob(String clobString,EmbedConnection con) {
+    EmbedClob(String clobString,EmbedConnection con) throws SQLException {
         super(con);
-        myString = clobString;
-        isString = true;
+        materialized = true;
+        control = new ClobStreamControl (con.getDBName(), this);
+        try {
+            control.insertString (clobString, 0);
+        }
+       
+        catch (IOException e) {
+            throw Util.setStreamFailure (e);
+        }
     }
     
-    /*
-    This constructor should only be called by EmbedResultSet.getClob
-    */
+    /**
+     * This constructor should only be called by EmbedResultSet.getClob
+     * @param dvd 
+     * @param con 
+     * @throws StandardException
+     */
     protected EmbedClob(DataValueDescriptor dvd, EmbedConnection con)
         throws StandardException
     {
@@ -112,10 +123,19 @@
         myStream = dvd.getStream();
         if (myStream == null)
         {
-            isString = true;
-           myString = dvd.getString();
-            if (SanityManager.DEBUG)
-                SanityManager.ASSERT(myString != null,"clob has a null value underneath");
+            control = new ClobStreamControl (con.getDBName(), this);
+            materialized = true;
+            try {
+                String str = dvd.getString();
+                control.insertString (dvd.getString(), 0);
+            }
+            catch (SQLException sqle) {
+                throw StandardException.newException (sqle.getSQLState(), sqle);
+            }
+            catch (IOException e) {
+                throw StandardException.newException (
+                                        SQLState.SET_STREAM_FAILURE, e);
+            }
         }
         else
         {
@@ -162,8 +182,13 @@
         //the Clob object has been freed by calling free() on it
         checkValidity();
         // if we have a string, not a stream
-        if (isString)
-            return myString.length();
+        try {
+            if (materialized)
+                return control.getCharLength ();
+        }
+        catch (IOException e) {
+            throw Util.setStreamFailure (e);
+        }
 
 
 		Object synchronization = getConnectionSynchronization();
@@ -240,15 +265,21 @@
                 SQLState.BLOB_NONPOSITIVE_LENGTH, new Integer(length));
 
         // if we have a string, not a stream
-        if (isString)
+        if (materialized)
         {
-            int sLength = myString.length();
-            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 myString.substring(((int) pos) - 1, (sLength > endIndex ? endIndex : sLength));
+            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();
@@ -305,20 +336,18 @@
         //the Clob object has been freed by calling free() on it
         checkValidity();
 
-        // if we have a string, not a stream
-        if (isString)
-        {
-            return new StringReader(myString);
-        }
-
 
-		Object synchronization = getConnectionSynchronization();
+        Object synchronization = getConnectionSynchronization();
         synchronized (synchronization)
         {
             setupContextStack();
 
 			try {
-				return getCharacterStreamAtPos(1, synchronization);
+                            if (materialized) {
+                                return control.getReader (0);
+                            }
+                            
+                            return getCharacterStreamAtPos(1, synchronization);
 			}
 			catch (Throwable t)
 			{
@@ -348,24 +377,30 @@
 		return new ReaderToAscii(getCharacterStream());
     }
 
-	private UTF8Reader getCharacterStreamAtPos(long position, Object synchronization)
-		throws IOException, StandardException
-	{
-        ((Resetable)myStream).resetStream();
-		UTF8Reader 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 == -1)
-				return null;
-
-			remainToSkip -= skipBy;
-		}
-
-		return clobReader;
-	}
+    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 == -1)
+                return null;
+            
+            remainToSkip -= skipBy;
+        }
+        
+        return clobReader;
+    }
 
 
   /**
@@ -422,83 +457,75 @@
             if (searchStr == "")
                 return start; // match DB2's SQL LOCATE function
 
-            // if we have a string, not a stream
-            if (isString)
+            Object synchronization = getConnectionSynchronization();
+            synchronized (synchronization)
             {
-				// avoid truncation errors in the cast of start to an int.
-				if (start > myString.length())
-					return -1;
-
-                int result = myString.indexOf(searchStr, (int) start-1);
-                return result < 0 ? -1 : result + 1;
-            }
-            else // we have a stream
-            {
-                Object synchronization = getConnectionSynchronization();
-                synchronized (synchronization)
-                {
-                    pushStack = !getEmbedConnection().isClosed();
-                    if (pushStack)
-                        setupContextStack();
-                    int matchCount = 0;
-                    long pos = start - 1;
-                    long newStart = -1;
-                    Reader reader = getCharacterStreamAtPos (start, this);
-                    char [] tmpClob = new char [256];
-                    boolean reset;
-                    for (;;) {
-                        reset = false;
-                        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)) {
-                                //find the new starting position in 
-                                // case this match is unsuccessful
-                                if (matchCount != 0 && newStart == -1 
-                                        && 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;
-                                }
+                pushStack = !getEmbedConnection().isClosed();
+                if (pushStack)
+                    setupContextStack();
+                int matchCount = 0;
+                long pos = start - 1;
+                long newStart = -1;
+                Reader reader = getCharacterStreamAtPos (start, this);
+                char [] tmpClob = new char [4096];
+                boolean reset;
+                for (;;) {
+                    reset = false;
+                    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)) {
+                            //find the new starting position in 
+                            // case this match is unsuccessful
+                            if (matchCount != 0 && newStart == -1 
+                                    && 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;
                             }
-                            else {
-                                if (matchCount > 0) {
-                                    matchCount = 0;
-                                    if (newStart == -1) {
-                                        continue;
+                        }
+                        else {
+                            if (matchCount > 0) {
+                                if (newStart == -1) {
+                                    if (matchCount > 1) {
+                                        //compensate for increment in the "for"
+                                        clobOffset--;
                                     }
-                                    if (newStart < pos) {
-                                        pos = newStart;
-                                        reader.close();
-                                        reader = getCharacterStreamAtPos 
-                                                    (newStart + 1, this);
-                                        newStart = -1;
-                                        reset = true;
-                                        break;
-                                    }                        
-                                    clobOffset = (int) (newStart - pos) - 1;
-                                    newStart = -1;
+                                    matchCount = 0;                                    
                                     continue;
                                 }
+                                matchCount = 0;
+                                if (newStart < pos) {
+                                    pos = newStart;
+                                    reader.close();
+                                    reader = getCharacterStreamAtPos 
+                                                (newStart + 1, this);
+                                    newStart = -1;
+                                    reset = true;
+                                    break;
+                                }                        
+                                clobOffset = (int) (newStart - pos) - 1;
+                                newStart = -1;
+                                continue;
                             }
                         }
-                        if (!reset) {
-                            pos += readCount;
-                        }
                     }
-
+                    if (!reset) {
+                        pos += readCount;
+                    }
                 }
+
             }
         }
         catch (Throwable t)
@@ -546,7 +573,7 @@
 
             synchronized (getConnectionSynchronization())
             {
-				char[] subPatternChar = new char[256];
+				char[] subPatternChar = new char[1024];
 
 				boolean seenOneCharacter = false;
 
@@ -629,7 +656,7 @@
     protected void finalize()
     {
         // System.out.println("finalizer called");
-        if (!isString)
+        if (!materialized)
             ((Resetable)myStream).closeStream();
     }
 
@@ -659,13 +686,11 @@
     * @return the number of characters written 
     * @exception SQLException Feature not implemented for now.
 	*/
-	public int setString(long pos, String parameterName)
-    throws SQLException
-	{
-		throw Util.notImplemented();
+	public int setString(long pos, String str) throws SQLException {
+            return setString (pos, str, 0, str.length());
 	}
 
-	/**
+   /**
     * JDBC 3.0
     *
     * Writes len characters of str, starting at character offset, to the CLOB value
@@ -678,11 +703,23 @@
     * @return the number of characters written
     * @exception SQLException Feature not implemented for now.
 	*/
-	public int setString(long pos, String str, int offset, int len)
-    throws SQLException
-	{
-		throw Util.notImplemented();
-	}
+        public int setString(long pos, String str, int offset, int len)
+        throws SQLException {
+            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);
+            } catch (IOException e) {
+                throw Util.setStreamFailure(e);
+            }
+        }
 
 	/**
     * JDBC 3.0
@@ -694,11 +731,13 @@
     * @return the stream to which ASCII encoded characters can be written 
     * @exception SQLException Feature not implemented for now.
 	*/
-	public java.io.OutputStream setAsciiStream(long pos)
-    throws SQLException
-	{
-                throw Util.notImplemented();
-	}
+    public java.io.OutputStream setAsciiStream(long pos) throws SQLException {
+        try {
+            return new ClobAsciiStream (control.getWriter(pos - 1));
+        } catch (IOException e) {
+            throw Util.setStreamFailure(e);
+        }
+    }
 
 	/**
     * JDBC 3.0
@@ -710,11 +749,17 @@
     * @return the stream to which Unicode encoded characters can be written 
     * @exception SQLException Feature not implemented for now.
 	*/
-	public java.io.Writer setCharacterStream(long pos)
-    throws SQLException
-	{
-		throw Util.notImplemented();
-	}
+    public java.io.Writer setCharacterStream(long pos) throws SQLException {
+        try {
+            if (!materialized) {
+                control.copyData(myStream, length());
+                materialized = true;
+            }
+            return control.getWriter(pos - 1);
+        } catch (IOException e) {
+            throw Util.setStreamFailure(e);
+        }
+    }
 
   	/**
     * JDBC 3.0
@@ -725,8 +770,7 @@
     * value should be truncated
     * @exception SQLException Feature not implemented for now.
 	*/
-	public void truncate(long len)
-    throws SQLException
+	public void truncate(long len) throws SQLException
 	{
 		throw Util.notImplemented();
 	}
@@ -754,10 +798,17 @@
         //valid
         isValid = false;
         
-        if (!isString)
+        if (!materialized) {
             ((Resetable)myStream).closeStream();
-        else
-            myString = null;
+        }
+        else {
+            try {
+                control.free();
+            }    
+            catch (IOException e) {
+                throw Util.setStreamFailure(e);
+            }
+        }
     }
 
     public java.io.Reader getCharacterStream(long pos, long length)
@@ -791,5 +842,6 @@
         private void checkValidity() throws SQLException{
             if(!isValid)
                 throw newSQLException(SQLState.LOB_OBJECT_INVALID);
+            localConn.checkIfClosed();
         }
 }

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBInputStream.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBInputStream.java?view=diff&rev=530085&r1=530084&r2=530085
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBInputStream.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBInputStream.java Wed Apr 18 09:17:01 2007
@@ -36,12 +36,13 @@
     private boolean closed;
     private LOBStreamControl control;
     private long pos;
+    private long updateCount;
 
     LOBInputStream(LOBStreamControl control, long position) {
         closed = false;
         this.control = control;
         pos = position;
-
+        updateCount = control.getUpdateCount ();
     }
 
     /**
@@ -173,5 +174,29 @@
                               SQLState.BLOB_INVALID_OFFSET)))
                 throw new ArrayIndexOutOfBoundsException (e.getMessage());
             throw new IOException(e.getMessage());
+    }
+    
+    /**
+     * Checks if underlying StreamControl has been updated.
+     * @return if stream is modified since created
+     */
+    boolean isObsolete () {
+        return updateCount != control.getUpdateCount();
+    }
+    
+    /**
+     * Reinitializes the stream and sets the current pointer to zero.
+     */
+    void reInitialize () {
+        updateCount = control.getUpdateCount();
+        pos = 0;
+    }
+    
+    /**
+     * Returns size of stream in bytes.
+     * @return size of stream.
+     */
+    long length () throws IOException {
+        return control.getLength();
     }
 }

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBStreamControl.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBStreamControl.java?view=diff&rev=530085&r1=530084&r2=530085
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBStreamControl.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBStreamControl.java Wed Apr 18 09:17:01 2007
@@ -61,9 +61,11 @@
     //randomly selected value
     private final int MAX_BUF_SIZE = 4096;
     private String dbName;
+    private long updateCount;
 
     public LOBStreamControl (String dbName) {
         this.dbName = dbName;
+        updateCount = 0;
     }
 
     private void init(byte [] b, long len) throws IOException, SQLException {
@@ -170,6 +172,7 @@
      */
     synchronized long write(int b, long pos) throws IOException, SQLException {
         isValidPostion(pos);
+        updateCount++;
         if (isBytes) {
             if (pos + 1 < MAX_BUF_SIZE) {
                 byte [] bytes = {(byte) b};
@@ -205,6 +208,7 @@
                                   SQLState.BLOB_INVALID_OFFSET)))
                     throw new ArrayIndexOutOfBoundsException (e.getMessage());
         }
+        updateCount++;
         if (isBytes) {
             if (pos + b.length < MAX_BUF_SIZE)
                 return updateData(b, off, len, pos);
@@ -368,5 +372,80 @@
                     throw (RuntimeException) e;
             }
         }
+    }
+    
+    /**
+     * Replaces bytes in the middle of the lob.The new byte array may not be 
+     * be of same length as the original bytes, so it may result in resizing 
+     * the total length.
+     * @param buf byte array which will be written inplace of old block
+     * @param stPos starting pisition of old block
+     * @param endPos end position of old block
+     * @return newposition new write position 
+     * @throws IOExcepton, SQLException
+     */
+    synchronized long replaceBytes (byte [] buf, long stPos, long endPos) 
+                                            throws IOException, SQLException {
+        long length = getLength();
+        long finalLength = length - endPos + stPos + buf.length;
+        if (isBytes) {
+            if (finalLength > MAX_BUF_SIZE) {
+                init (dataBytes, stPos);
+                write (buf, 0, buf.length, getLength());
+                if (endPos < length)
+                    write (dataBytes, (int) endPos, 
+                            (int) (length - endPos), getLength());
+            }
+            else {
+                byte [] tmpByte = new byte [(int) finalLength];
+                System.arraycopy (dataBytes, 0, tmpByte, 0, (int) stPos);
+                System.arraycopy (buf, 0, tmpByte, (int) stPos, (int) buf.length);
+                if (endPos < length)
+                    System.arraycopy (dataBytes, (int) endPos, tmpByte, 
+                            (int) (stPos + buf.length), (int) (length - endPos));
+                dataBytes = tmpByte;            
+            }
+        }
+        else {
+            //save over file handle and 
+            //create new file with 0 size
+            
+            byte tmp [] = new byte [0];
+            StorageRandomAccessFile oldFile = tmpFile;
+            init (tmp, 0);
+            byte [] tmpByte = new byte [1024];
+            long sz = stPos;
+            oldFile.seek(0);
+            while (sz != 0) {
+                int readLen = (int) Math.min (1024, sz);                
+                int actualLength = oldFile.read (tmpByte, 0, readLen);
+                if (actualLength == -1)
+                    break;
+                tmpFile.write (tmpByte, 0, actualLength);
+                sz -= actualLength;
+            }
+            tmpFile.write (buf);
+            oldFile.seek (endPos);
+            int rdLen;
+            if (endPos < length) {
+                do {
+                    rdLen = oldFile.read (tmpByte, 0, 1024);
+                    if (rdLen == -1)
+                        break;
+                    tmpFile.write (tmpByte, 0, rdLen);
+                }while (true);
+            }            
+        }
+        updateCount++;
+        return stPos + buf.length;
+    }
+
+    /**
+     * Returns the running secquence number to check if the lob is updated since
+     * last access.
+     * @return newcount
+     */
+    long getUpdateCount() {
+        return updateCount;
     }
 }

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/UTF8Reader.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/UTF8Reader.java?view=diff&rev=530085&r1=530084&r2=530085
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/UTF8Reader.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/UTF8Reader.java Wed Apr 18 09:17:01 2007
@@ -66,6 +66,32 @@
 			this.utfLen = readUnsignedShort();
 		}
 	}
+        
+    /**
+     * Constructs a UTF8Reader using a stream. This consturctor accepts 
+     * the stream size as paramater and doesn't attempts to read the lenght 
+     * from the stream.
+     * @param in InputStream
+     * @param maxFieldSize 
+     * @param streamSize size of the stream
+     * @param parent connectionChild this stream is associated with
+     * @param synchronization object to synchronize on
+     * @throws IOException
+     */
+        public UTF8Reader(
+                InputStream in,
+                long maxFieldSize,
+                long streamSize,
+                ConnectionChild      parent,                
+                Object synchronization)
+                throws IOException {
+            super(synchronization);
+            
+            this.in     = in;
+            this.maxFieldSize = maxFieldSize;
+            this.parent = parent;
+            this.utfLen = streamSize;
+        }
 
 	/*
 	** Reader implemention.

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BlobClob4BlobTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BlobClob4BlobTest.java?view=diff&rev=530085&r1=530084&r2=530085
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BlobClob4BlobTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BlobClob4BlobTest.java Wed Apr 18 09:17:01 2007
@@ -1227,7 +1227,6 @@
         rs.close();
         stmt.close();
         commit();
-
         // no problem accessing this after commit since it is in memory
         assertEquals("FAIL - can not access short clob after commit",
                 26, shortClob.length());
@@ -1313,9 +1312,19 @@
         commit();
         getConnection().close();
 
-        // no problem accessing this after commit since it is in memory
-        assertEquals("FAIL - erorr accessing short lob after closing the " +
-                "connection", 26,  shortClob.length());
+        try {
+            long len = shortClob.length();
+            if (usingEmbedded()) {
+                fail("FAIL - should not be able to access large log " +
+                        "after commit");
+            }
+            assertEquals ("FAIL length mismatch", 26, len);
+        }
+        catch (SQLException e) {
+            checkException(NO_CURRENT_CONNECTION, e);
+            assertTrue("FAIL - Derby Client and JCC should not get an " +
+                    "exception", usingEmbedded());
+        }
 
         // these should all give blob/clob data unavailable exceptions
         try {
@@ -1350,11 +1359,7 @@
                         "after the connection is closed");
             }
         } catch (SQLException e) {
-            if (usingEmbedded()) {
-                checkException(BLOB_ACCESSED_AFTER_COMMIT, e);
-            } else {
-                checkException(NO_CURRENT_CONNECTION, e);
-            }
+            checkException(NO_CURRENT_CONNECTION, e);
         }
         try {
             clob.position(clob,2);

Added: 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=auto&rev=530085
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobUpdateableReaderTest.java (added)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobUpdateableReaderTest.java Wed Apr 18 09:17:01 2007
@@ -0,0 +1,166 @@
+/*
+ *
+ * Derby - Class ClobUpdateableReaderTest
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
+ */
+
+package org.apache.derbyTesting.functionTests.tests.jdbcapi;
+
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.Writer;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import junit.framework.Test;
+import org.apache.derbyTesting.junit.BaseJDBCTestCase;
+import org.apache.derbyTesting.junit.TestConfiguration;
+
+/**
+ * Test class to test Updateable Reader for Clob in embedder driver.
+ */
+public class ClobUpdateableReaderTest extends BaseJDBCTestCase {
+    
+    private final String dummy = "This is a new String";
+        
+    public ClobUpdateableReaderTest (String name) {
+        super (name);
+    }
+    
+    /**
+     * Tests updates on reader.
+     */
+    public void testUpdateableReader () throws Exception {
+        Connection con = getConnection();
+        try {
+            con.setAutoCommit (false);
+            PreparedStatement ps = con.prepareStatement ("insert into updateClob " +
+                    "(id , data) values (? ,?)");
+            ps.setInt (1, 1);
+            StringBuffer sb = new StringBuffer ();
+            String base = "SampleSampleSample";
+            for (int i = 0; i < 100; i++) {
+                sb.append (base);
+            }
+            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 = 1");
+            rs.next();
+            Clob clob = rs.getClob (1);
+            rs.close();
+            stmt.close();
+            assertEquals (sb.length(), clob.length());
+            Reader r = clob.getCharacterStream();
+            char [] clobData = new char [sb.length()];
+            r.read (clobData);
+            assertEquals ("mismatch from inserted string", 
+                                String.valueOf (clobData), sb.toString());
+            r.close();
+            //update before gettting the reader
+            clob.setString (50, dummy);        
+            r = clob.getCharacterStream();
+            r.skip (49);
+            char [] newChars = new char [dummy.length()];
+            r.read (newChars);
+            assertEquals ("update not reflected", dummy,
+                                        String.valueOf (newChars));
+            //update again and see if stream is refreshed
+            clob.setString (75, dummy);
+            r.skip (75 - 50 - dummy.length());
+            char [] testChars = new char [dummy.length()];
+            r.read (testChars);
+            assertEquals ("update not reflected", dummy,
+                                        String.valueOf (newChars));
+            r.close();
+            //try inserting some unicode string
+            String unicodeStr = getUnicodeString();
+            clob.setString (50, unicodeStr);
+            char [] utf16Chars = new char [unicodeStr.length()];
+            r = clob.getCharacterStream();
+            r.skip(49);
+            r.read(utf16Chars);
+            assertEquals ("update not reflected",  unicodeStr,
+                                        String.valueOf (utf16Chars));
+            r.close();
+            Writer w = clob.setCharacterStream (1);
+            //write enough data to switch the data to file
+            r = clob.getCharacterStream ();
+            for (int i = 0; i < 10000; i++) {
+                w.write (dummy);
+            }
+            w.close();            
+            clob.setString (500, unicodeStr);
+            r.skip (499);
+            char [] unicodeChars = new char [unicodeStr.length()];
+            r.read (unicodeChars);
+            assertEquals ("update not reflected",  unicodeStr,
+                                        String.valueOf (unicodeChars));            
+        }
+        finally {
+            if (con != null) {
+                con.commit ();
+                con.close();
+            }
+        }
+    }   
+    
+    private String getUnicodeString () {
+        char[] fill = new char[4];
+        fill[0] = 'd';          // 1 byte UTF8 character (ASCII)
+        fill[1] = '\u03a9';     // 2 byte UTF8 character (Greek)
+        fill[2] = '\u0e14';     // 3 byte UTF8 character (Thai)
+        fill[3] = 'j';          // 1 byte UTF8 character (ASCII)
+        StringBuffer sb = new StringBuffer ();
+        for (int i = 0; i < 4; i++) {
+            sb.append (fill);
+        }
+        return sb.toString();        
+    }
+    
+    /**
+     * Setup the test.
+     * @throws a SQLException.
+     */
+    public void setUp() throws Exception {
+        Connection con = getConnection ();
+        Statement stmt = con.createStatement ();
+        stmt.execute ("create table updateClob " +
+                "(id integer primary key, data clob)");
+        stmt.close();
+        con.commit();
+        con.close();
+    }
+    
+    public static Test suite() {
+        return TestConfiguration.embeddedSuite(
+                    ClobUpdateableReaderTest.class);
+    }        
+
+    protected void tearDown() throws java.lang.Exception {
+        Connection con = getConnection ();
+        Statement stmt = con.createStatement ();
+        stmt.execute ("drop table updateClob");
+        stmt.close();
+        con.close();
+    }
+}

Propchange: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobUpdateableReaderTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/LobStreamsTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/LobStreamsTest.java?view=diff&rev=530085&r1=530084&r2=530085
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/LobStreamsTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/LobStreamsTest.java Wed Apr 18 09:17:01 2007
@@ -19,6 +19,7 @@
  */
 package org.apache.derbyTesting.functionTests.tests.jdbcapi;
 
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.Reader;
@@ -442,9 +443,27 @@
      */
     public static Test suite() {
                 
-        return TestConfiguration.clientServerSuite(LobStreamsTest.class);
+        return TestConfiguration.defaultSuite (LobStreamsTest.class);
     }
 
+    //method to ensure that buffer is filled if there is any data in stream
+    private int readBytesFromStream (byte [] b, InputStream is) 
+                                                          throws IOException {
+        int read = 0;
+        while (read < b.length) {
+            int ret = is.read (b, read, b.length - read);
+            if (ret < 0) {
+                if (read == 0) {
+                    return ret;
+                }
+                else {
+                    break;
+                }
+            }
+            read += ret;
+        }
+        return read;
+    }
 
     private boolean compareLob2File(
             InputStream fStream,
@@ -456,8 +475,8 @@
         String fString, lString;
 
         do {
-            fLength = fStream.read(fByte, 0, 1024);
-            lLength = lStream.read(lByte, 0, 1024);
+            fLength = readBytesFromStream (fByte, fStream);
+            lLength = readBytesFromStream (lByte, lStream);
             if (!java.util.Arrays.equals(fByte, lByte))
                 return false;
         } while (fLength > 0 && lLength > 0);

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=530085&r1=530084&r2=530085
==============================================================================
--- 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 Apr 18 09:17:01 2007
@@ -108,6 +108,9 @@
             // Test uses DriverManager, Pooled and XADataSources, and
             // an inner class implements ConnectionEventListener.
             suite.addTest(DataSourceTest.suite());
+
+            //suite to test updateable reader for clob in embedded driver
+            suite.addTest (ClobUpdateableReaderTest.suite());
         }
 		
         return suite;