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 km...@apache.org on 2008/01/31 19:48:44 UTC

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

Author: kmarsden
Date: Thu Jan 31 10:48:30 2008
New Revision: 617186

URL: http://svn.apache.org/viewvc?rev=617186&view=rev
Log:
DERBY-3365 Network Server stores a duplicate entry in the lob hash map for every lob

Change network server to use existing lob hash map entry instead of creating a second entry.


Added:
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/EngineBlob.java   (with props)
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/EngineClob.java   (with props)
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedBlob.java
Modified:
    db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/BrokeredConnection.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/EngineConnection.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/LOBStoredProcedure.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/BlobStoredProcedureTest.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobStoredProcedureTest.java

Modified: db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java?rev=617186&r1=617185&r2=617186&view=diff
==============================================================================
--- db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java (original)
+++ db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java Thu Jan 31 10:48:30 2008
@@ -62,6 +62,8 @@
 import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
 import org.apache.derby.iapi.tools.i18n.LocalizedResource;
 import org.apache.derby.iapi.jdbc.AuthenticationService;
+import org.apache.derby.iapi.jdbc.EngineBlob;
+import org.apache.derby.iapi.jdbc.EngineClob;
 import org.apache.derby.iapi.jdbc.EngineResultSet;
 import org.apache.derby.impl.jdbc.EmbedSQLException;
 import org.apache.derby.impl.jdbc.Util;
@@ -7381,6 +7383,7 @@
    * @param stmt       Statement being processed
    *
    * @exception DRDAProtocolException  
+   * 
    * @exception SQLException
    *
    * @see FdocaConstants
@@ -7487,12 +7490,12 @@
 				case DRDAConstants.DRDA_TYPE_NLONGVARBYTE:
 						writer.writeLDBytes((byte[]) val, index);
 					break;
-                case DRDAConstants.DRDA_TYPE_NLOBLOC:
-                case DRDAConstants.DRDA_TYPE_NCLOBLOC:
-                    // Get locator for LOB
-                    int locator = database.getConnection().addLOBMapping(val);
-                    writer.writeInt(locator);
-                    break;
+				case DRDAConstants.DRDA_TYPE_NLOBLOC:
+					writer.writeInt(((EngineBlob) val).getLocator());
+					break;
+				case DRDAConstants.DRDA_TYPE_NCLOBLOC:
+					writer.writeInt(((EngineClob) val).getLocator());
+					break;
 				default:
 					if (SanityManager.DEBUG) 
 						trace("ndrdaType is: "+ndrdaType);

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/BrokeredConnection.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/BrokeredConnection.java?rev=617186&r1=617185&r2=617186&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/BrokeredConnection.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/BrokeredConnection.java Thu Jan 31 10:48:30 2008
@@ -615,21 +615,7 @@
         return holdability;
         
     }
-	/**
-	* Add the locator and the corresponding LOB object into the
-	* HashMap
-	*
-	* @param LOBReference The object which contains the LOB object that
-	*                     that is added to the HashMap.
-	* @return an integer that represents the locator that has been
-	*         allocated to this LOB.
-	*/
-	public int addLOBMapping(Object LOBReference) throws SQLException {
-            //Forward the methods implementation to the implementation in the
-            //underlying EmbedConnection object. 
-            return getRealConnection().addLOBMapping(LOBReference);
-	}
-
+    
 	/**
 	* Clear the HashMap of all entries.
 	* Called when a commit or rollback of the transaction

Added: db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/EngineBlob.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/EngineBlob.java?rev=617186&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/EngineBlob.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/EngineBlob.java Thu Jan 31 10:48:30 2008
@@ -0,0 +1,19 @@
+package org.apache.derby.iapi.jdbc;
+
+/**
+ * Additional methods the embedded engine exposes on its Blob object
+ * implementations. An internal api only, mainly for the network
+ * server.  
+ * 
+ */
+
+public interface EngineBlob {
+
+    /**
+     * Return lob locator key that can be used with 
+     * EmbedConnection.getLobMapping(int) to retrieve this Blob.
+     * 
+     * @return lob locator for this Blob
+     */
+    public int getLocator();
+}

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

Added: db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/EngineClob.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/EngineClob.java?rev=617186&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/EngineClob.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/EngineClob.java Thu Jan 31 10:48:30 2008
@@ -0,0 +1,18 @@
+package org.apache.derby.iapi.jdbc;
+
+/**
+ * Additional methods the embedded engine exposes on its Blob object
+ * implementations. An internal api only, mainly for the network
+ * server.  
+ * 
+ */
+
+public interface EngineClob {
+    /**
+     * Return lob locator key that can be used with 
+     * EmbedConnection.getLobMapping(int) to retrieve this Clob.
+     * 
+     * @return lob locator for this Clob
+     */
+    public int getLocator();
+}

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

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/EngineConnection.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/EngineConnection.java?rev=617186&r1=617185&r2=617186&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/EngineConnection.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/jdbc/EngineConnection.java Thu Jan 31 10:48:30 2008
@@ -80,16 +80,6 @@
         throws SQLException;
 
     /**
-    * Add the locator and the corresponding LOB object into the
-    * HashMap
-    * @param LOBReference The object which contains the LOB object that
-    *                     that is added to the HashMap.
-    * @return an integer that represents the locator that has been
-    *         allocated to this LOB.
-    */
-    public int addLOBMapping(Object LOBReference) throws SQLException;
-
-    /**
     * Clear the HashTable of all entries.
     * Called when a commit or rollback of the transaction
     * happens.

Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedBlob.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedBlob.java?rev=617186&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedBlob.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedBlob.java Thu Jan 31 10:48:30 2008
@@ -0,0 +1,1005 @@
+/*
+
+   Derby - Class org.apache.derby.impl.jdbc.EmbedBlob
+
+   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 org.apache.derby.iapi.reference.SQLState;
+import org.apache.derby.iapi.error.StandardException;
+import org.apache.derby.iapi.jdbc.EngineBlob;
+import org.apache.derby.iapi.services.monitor.Monitor;
+import org.apache.derby.iapi.services.sanity.SanityManager;
+import org.apache.derby.iapi.types.DataValueDescriptor;
+import org.apache.derby.iapi.types.Resetable;
+import org.apache.derby.iapi.services.io.NewByteArrayInputStream;
+import org.apache.derby.iapi.services.io.InputStreamUtil;
+import org.apache.derby.iapi.services.io.ArrayInputStream;
+
+import java.sql.SQLException;
+import java.sql.Blob;
+import java.io.InputStream;
+import java.io.EOFException;
+import java.io.IOException;
+
+/**
+    Implements java.sql.Blob (see the JDBC 2.0 spec).
+    A blob sits on top of a BINARY, VARBINARY or LONG VARBINARY column.
+    If its data is small (less than 1 page) it is a byte array taken from
+    the SQLBit class. If it is large (more than 1 page) it is a long column
+    in the database. The long column is accessed as a stream, and is implemented
+    in store as an OverflowInputStream.  The Resetable interface allows sending
+    messages to that stream to initialize itself (reopen its container and
+    lock the corresponding row) and to reset itself to the beginning. 
+
+    NOTE: In the case that the data is large, it is represented as a stream.
+    This stream is returned to the user in the getBinaryStream() method.
+    This means that we have limited control over the state of the stream,
+    since the user can read bytes from it at any time.  Thus all methods
+    here reset the stream to the beginning before doing any work.
+    CAVEAT: The methods may not behave correctly if a user sets up
+    multiple threads and sucks data from the stream (returned from
+    getBinaryStream()) at the same time as calling the Blob methods.
+
+  <P><B>Supports</B>
+   <UL>
+   <LI> JSR169 - no subsetting for java.sql.Blob
+   <LI> JDBC 2.0
+   <LI> JDBC 3.0 - no new dependencies on new JDBC 3.0 or JDK 1.4 classes,
+        new update methods can safely be added into implementation.
+   </UL>
+
+ */
+
+final class EmbedBlob extends ConnectionChild implements Blob, EngineBlob
+{
+    // blob is either materialized or still in stream
+    private boolean         materialized;
+    private InputStream     myStream;
+    
+    // locator key for lob. used by Network Server.
+    private final int             locator;
+    
+    /*
+     * Length of the BLOB if known. Set to -1 if
+     * the current length of the BLOB is not known.
+     */
+    private long myLength = -1;
+    
+    // note: cannot control position of the stream since user can do a getBinaryStream
+    private long            pos;
+    // this stream sits on top of myStream
+    private BinaryToRawStream biStream;
+
+    // buffer for reading in blobs from a stream (long column)
+    // and trashing them (to set the position of the stream etc.)
+    private static int BLOB_BUF_SIZE = 4096;
+    private byte buf[];
+    
+    //This boolean variable indicates whether the Blob object has
+    //been invalidated by calling free() on it
+    private boolean isValid = true;
+
+    private LOBStreamControl control;
+
+     /**
+     * This constructor is used to create a empty Blob object. It is used by the
+     * Connection interface method createBlob().
+     * 
+     * @param blobBytes A byte array containing the data to be stores in the 
+     *        Blob.
+     *
+     * @param con The EmbedConnection object associated with this Blob object.
+     *
+     */
+    
+     EmbedBlob(byte [] blobBytes,EmbedConnection con) throws SQLException {
+        super(con);
+         try {
+             control = new LOBStreamControl (con.getDBName(), blobBytes);
+             materialized = true;
+             //add entry in connection so it can be cleared 
+             //when transaction is not valid
+             locator = con.addLOBMapping (this);
+         }
+         catch (IOException e) {
+             throw Util.setStreamFailure (e);
+         }
+         catch (StandardException se) {
+            throw Util.generateCsSQLException (se);
+         }
+     }
+     
+    /*
+      This constructor should only be called by EmbedResultSet.getBlob
+    */
+    protected EmbedBlob(DataValueDescriptor dvd, EmbedConnection con)
+        throws StandardException
+    {
+        super(con);
+        // if the underlying column is null, ResultSet.getBlob will return null,
+        // never should get this far
+        if (SanityManager.DEBUG)
+            SanityManager.ASSERT(!dvd.isNull(), "blob is created on top of a null column");
+
+        myStream = dvd.getStream();
+        if (myStream == null)
+        {
+            materialized = true;
+            // copy bytes into memory so that blob can live after result set
+            // is closed
+            byte[] dvdBytes = dvd.getBytes();
+
+            if (SanityManager.DEBUG)
+                SanityManager.ASSERT(dvdBytes != null,"blob has a null value underneath");
+            try {
+                control = new LOBStreamControl (
+                            getEmbedConnection().getDBName(), dvdBytes);
+            }
+            catch (SQLException e) {
+                throw StandardException.newException (e.getSQLState());
+            }
+            catch (IOException e) {
+                throw StandardException.newException (
+                                        SQLState.SET_STREAM_FAILURE, e);
+            }
+        }
+        else
+        {
+            materialized = false;
+
+            /*
+             We are expecting this stream to be a FormatIdInputStream with an
+             OverflowInputStream inside. FormatIdInputStream implements
+             Resetable. This should be the case when retrieving
+             data from a long column. However, SQLBit, which is the class
+             implementing the getStream() method for dvd.getStream(), does not
+             guarantee this for us
+             */
+            if (SanityManager.DEBUG)
+                SanityManager.ASSERT(myStream instanceof Resetable);
+            //make myStream a position aware stream
+            myStream = new PositionedStoreStream (myStream);
+            try {
+                ((Resetable) myStream).initStream();
+            } catch (StandardException se) {
+                if (se.getMessageId().equals(SQLState.DATA_CONTAINER_CLOSED)) {
+                    throw StandardException
+                            .newException(SQLState.BLOB_ACCESSED_AFTER_COMMIT);
+                }
+            }
+            // set up the buffer for trashing the bytes to set the position of
+            // the
+            // stream, only need a buffer when we have a long column
+            buf = new byte[BLOB_BUF_SIZE];
+        }
+        pos = 0;
+        //add entry in connection so it can be cleared 
+        //when transaction is not valid
+        this.locator = con.addLOBMapping (this);
+    }
+
+
+    /*
+        Sets the position of the stream to position newPos, where position 0 is
+        the beginning of the stream.
+
+        @param newPos the position to set to
+        @exception StandardException (BLOB_SETPOSITION_FAILED) throws this if
+        the stream runs out before we get to newPos
+    */
+    private void setPosition(long newPos)
+        throws StandardException, IOException
+    {
+        if (SanityManager.DEBUG)
+            SanityManager.ASSERT(newPos >= 0);
+        if (materialized)
+            pos = newPos;
+        else {
+            // Always resets the stream to the beginning first, because user can
+            // influence the state of the stream without letting us know.
+            ((Resetable)myStream).resetStream();
+            // PT could try to save creating a new object each time
+            biStream = new BinaryToRawStream(myStream, this);
+            pos = 0;
+            while (pos < newPos)
+            {
+                int size = biStream.read(
+                    buf,0,(int) Math.min((newPos-pos), (long) BLOB_BUF_SIZE));
+                if (size <= 0)   // ran out of stream
+                    throw StandardException.newException(SQLState.BLOB_LENGTH_TOO_LONG);
+                pos += size;
+            }
+        }
+    }
+
+
+    /*
+        Reads one byte, either from the byte array or else from the stream.
+    */
+    private int read() throws IOException, SQLException {
+        int c;
+        if (materialized)
+        {
+            try {
+                if (pos >= control.getLength())
+                    return -1;
+                else
+                    c = control.read (pos);
+            }
+            catch (StandardException se) {
+                throw Util.generateCsSQLException (se);
+            }
+        }
+        else
+            c = biStream.read();
+        pos++;
+        return c;
+    }
+
+  /**
+   * Returns the number of bytes in the <code>BLOB</code> value
+   * designated by this <code>Blob</code> object.
+   * @return length of the <code>BLOB</code> in bytes
+   * @exception SQLException if there is an error accessing the
+   * length of the <code>BLOB</code>
+   */
+    // PT stream part may get pushed to store
+    public long length()
+        throws SQLException
+    {
+        //call checkValidity to exit by throwing a SQLException if
+        //the Blob object has been freed by calling free() on it
+        checkValidity();
+        try {
+            if (materialized)
+                return control.getLength ();
+        }
+        catch (IOException e) {
+            throw Util.setStreamFailure (e);
+        }
+        if (myLength != -1)
+            return myLength;
+        
+        boolean pushStack = false;
+        try
+        {
+           // we have a stream
+            synchronized (getConnectionSynchronization())
+            {
+                pushStack = !getEmbedConnection().isClosed();
+                if (pushStack)
+                    setupContextStack();
+
+                setPosition(0);
+                // If possible get the length from the encoded
+                // length at the front of the raw stream.
+                if ((myLength = biStream.getLength()) != -1) {
+                    biStream.close();
+                   return myLength;
+                }
+                
+                // Otherwise have to read the entire stream!
+                for (;;)
+                {
+                    int size = biStream.read(buf);
+                    if (size == -1)
+                        break;
+                    pos += size;
+                }
+                // Save for future uses.
+                myLength = pos;
+                biStream.close();
+                return pos;
+            }
+        }
+        catch (Throwable t)
+        {
+			throw handleMyExceptions(t);
+        }
+        finally
+        {
+            if (pushStack)
+                restoreContextStack();
+        }
+    }
+
+
+  /**
+   * Returns as an array of bytes part or all of the <code>BLOB</code>
+   * value that this <code>Blob</code> object designates.  The byte
+   * array contains up to <code>length</code> consecutive bytes
+   * starting at position <code>startPos</code>.
+   * The starting position must be between 1 and the length
+   * of the BLOB plus 1. This allows for zero-length BLOB values, from
+   * which only zero-length byte arrays can be returned. 
+   * If a larger length is requested than there are bytes available,
+   * characters from the start position to the end of the BLOB are returned.
+   * @param startPos the ordinal position of the first byte in the
+   * <code>BLOB</code> value to be extracted; the first byte is at
+   * position 1
+   * @param length is the number of consecutive bytes to be copied
+   * @return a byte array containing up to <code>length</code>
+   * consecutive bytes from the <code>BLOB</code> value designated
+   * by this <code>Blob</code> object, starting with the
+   * byte at position <code>startPos</code>.
+   * @exception SQLException if there is an error accessing the
+   * <code>BLOB</code>
+   * NOTE: If the starting position is the length of the BLOB plus 1,
+   * zero bytess are returned regardless of the length requested.
+   */
+    public byte[] getBytes(long startPos, int length)
+        throws SQLException
+    {
+        //call checkValidity to exit by throwing a SQLException if
+        //the Blob object has been freed by calling free() on it
+        checkValidity();
+        
+        boolean pushStack = false;
+        try
+        {
+            if (startPos < 1)
+                throw StandardException.newException(
+                    SQLState.BLOB_BAD_POSITION, new Long(startPos));
+            if (length < 0)
+                throw StandardException.newException(
+                    SQLState.BLOB_NONPOSITIVE_LENGTH, new Integer(length));
+
+            byte[] result;
+            // if the blob is materialized
+            if (materialized) {
+                 result = new byte [length];
+                 int sz = control.read (result, 0, result.length, startPos - 1);
+                 if (sz == -1)
+                     return new byte [0];
+                 if (sz < length) {
+                     byte [] tmparray = new byte [sz];
+                     System.arraycopy (result, 0, tmparray, 0, sz);
+                     result = tmparray;
+                 }
+            }
+            else // we have a stream
+            {
+                synchronized (getConnectionSynchronization())
+                {
+                    pushStack = !getEmbedConnection().isClosed();
+                    if (pushStack)
+                        setupContextStack();
+
+                    setPosition(startPos-1);
+                    // read length bytes into a string
+                    result = new byte[length];
+                    int n = InputStreamUtil.readLoop(biStream,result,0,length);
+                    pos += n;
+                    /*
+                     According to the spec, if there are only n < length bytes
+                     to return, we should just return these bytes. Rather than
+                     return them in an array of size length, where the trailing
+                     bytes are not initialized, and the user cannot tell how
+                     many bytes were actually returned, we should return an
+                     array of n bytes.
+                     */
+                    if (n < length)
+                    {
+                        byte[] result2 = new byte[n];
+                        System.arraycopy(result,0,result2,0,n);
+                        return result2;
+                    }
+                }
+            }
+            return result;
+        }
+        catch (StandardException e)
+        {  // if this is a setPosition exception then we ran out of Blob
+            if (e.getMessageId().equals(SQLState.BLOB_LENGTH_TOO_LONG))
+                e = StandardException.newException(
+                    SQLState.BLOB_POSITION_TOO_LARGE, new Long(startPos));
+            throw handleMyExceptions(e);
+        }
+        catch (Throwable t)
+        {
+			throw handleMyExceptions(t);
+        }
+        finally
+        {
+            if (pushStack)
+                restoreContextStack();
+        }
+
+    }
+
+
+  /**
+   * Retrieves the <code>BLOB</code> designated by this
+   * <code>Blob</code> instance as a stream.
+   * @return a stream containing the <code>BLOB</code> data
+   * @exception SQLException if there is an error accessing the
+   * <code>BLOB</code>
+   */
+    public java.io.InputStream getBinaryStream()
+        throws SQLException
+    {
+        //call checkValidity to exit by throwing a SQLException if
+        //the Blob object has been freed by calling free() on it
+        checkValidity();
+        
+        boolean pushStack = false;
+        try
+        {
+            // if we have byte array, not a stream
+            if (materialized)
+            {
+                return control.getInputStream(0);
+            }
+            else
+            { 
+                // have a stream
+
+                synchronized (getConnectionSynchronization())
+                {
+                    pushStack = !getEmbedConnection().isClosed();
+                    if (pushStack)
+                        setupContextStack();
+
+                    ((Resetable)myStream).resetStream();
+                    return new UpdatableBlobStream (this, 
+                            new AutoPositioningStream (this, myStream, this));
+                }
+            }
+        }
+        catch (Throwable t)
+        {
+			throw handleMyExceptions(t);
+        }
+        finally
+        {
+            if (pushStack)
+                restoreContextStack();
+        }
+    }
+
+
+  /**
+   * Determines the byte position at which the specified byte
+   * <code>pattern</code> begins within the <code>BLOB</code>
+   * value that this <code>Blob</code> object represents.  The
+   * search for <code>pattern</code. begins at position
+   * <code>start</code>
+   * @param pattern the byte array for which to search
+   * @param start the position at which to begin searching; the
+   *        first position is 1
+   * @return the position at which the pattern appears, else -1.
+   * @exception SQLException if there is an error accessing the
+   * <code>BLOB</code>
+   */
+    public long position(byte[] pattern, long start)
+        throws SQLException
+    {
+        //call checkValidity to exit by throwing a SQLException if
+        //the Blob object has been freed by calling free() on it
+        checkValidity();
+        
+        boolean pushStack = false;
+        try
+        {
+            if (start < 1)
+                throw StandardException.newException(
+                    SQLState.BLOB_BAD_POSITION, new Long(start));
+            if (pattern == null)
+                throw StandardException.newException(SQLState.BLOB_NULL_PATTERN_OR_SEARCH_STR);
+            if (pattern.length == 0)
+                return start; // match DB2's SQL LOCATE function
+
+            synchronized (getConnectionSynchronization())
+            {
+                pushStack = !getEmbedConnection().isClosed();
+                if (pushStack)
+                    setupContextStack();
+
+                setPosition(start-1);
+                // look for first character
+                int lookFor = pattern[0];
+                long curPos;
+                int c;
+                while (true)
+                {
+                    c = read();
+                    if (c == -1)  // run out of stream
+                        return -1;
+                    if (c == lookFor)
+                    {
+                        curPos = pos;
+                        if (checkMatch(pattern))
+                            return curPos;
+                        else
+                            setPosition(curPos);
+                    }
+                }
+            }
+        }
+        catch (StandardException e)
+        {  // if this is a setPosition exception then not found
+            if (e.getMessageId().equals(SQLState.BLOB_LENGTH_TOO_LONG))
+                return -1;
+            else
+                throw handleMyExceptions(e);
+        }
+        catch (Throwable t)
+        {
+			throw handleMyExceptions(t);
+        }
+        finally
+        {
+            if (pushStack)
+                restoreContextStack();
+        }
+
+    }
+
+
+    /*
+     check whether pattern (starting from the second byte) appears inside
+     posStream (at the current position)
+     @param posStream the stream to search inside
+     @param pattern the byte array passed in by the user to search with
+     @return true if match, false otherwise
+     */
+    private boolean checkMatch(byte[] pattern)
+        throws IOException, SQLException {
+       // check whether rest matches
+       // might improve performance by reading more
+        for (int i = 1; i < pattern.length; i++)
+        {
+            int b = read();
+            if ((b < 0) || (b != pattern[i]))  // mismatch or stream runs out
+                return false;
+        }
+        return true;
+    }
+
+  /**
+   * Determines the byte position in the <code>BLOB</code> value
+   * designated by this <code>Blob</code> object at which
+   * <code>pattern</code> begins.  The search begins at position
+   * <code>start</code>.
+   * @param pattern the <code>Blob</code> object designating
+   * the <code>BLOB</code> value for which to search
+   * @param start the position in the <code>BLOB</code> value
+   *        at which to begin searching; the first position is 1
+   * @return the position at which the pattern begins, else -1
+   * @exception SQLException if there is an error accessing the
+   * <code>BLOB</code>
+   */
+    public long position(Blob pattern, long start)
+        throws SQLException
+    {
+        //call checkValidity to exit by throwing a SQLException if
+        //the Blob object has been freed by calling free() on it
+        checkValidity();
+        
+        boolean pushStack = false;
+        try
+        {
+            if (start < 1)
+                throw StandardException.newException(
+                    SQLState.BLOB_BAD_POSITION, new Long(start));
+            if (pattern == null)
+                throw StandardException.newException(SQLState.BLOB_NULL_PATTERN_OR_SEARCH_STR);
+            synchronized (getConnectionSynchronization())
+            {
+                pushStack = !getEmbedConnection().isClosed();
+                if (pushStack)
+                    setupContextStack();
+
+                setPosition(start-1);
+                // look for first character
+                byte[] b;
+                try
+                { // pattern is not necessarily a Derby Blob
+                    b = pattern.getBytes(1,1);
+                }
+                catch (SQLException e)
+                {
+                    throw StandardException.newException(SQLState.BLOB_UNABLE_TO_READ_PATTERN);
+                }
+                if (b == null || b.length < 1)  // the 'empty' blob
+                    return start; // match DB2's SQL LOCATE function
+                int lookFor = b[0];
+                int c;
+                long curPos;
+                while (true)
+                {
+                    c = read();
+                    if (c == -1)  // run out of stream
+                        return -1;
+                    if (c == lookFor)
+                    {
+                        curPos = pos;
+                        if (checkMatch(pattern))
+                            return curPos;
+                        else
+                            setPosition(curPos);
+                    }
+                }
+            }
+        }
+        catch (StandardException e)
+        {  // if this is a setPosition exception then not found
+            if (e.getMessageId().equals(SQLState.BLOB_LENGTH_TOO_LONG))
+                return -1;
+            else
+                throw handleMyExceptions(e);
+        }
+        catch (Throwable t)
+        {
+			throw handleMyExceptions(t);
+        }
+        finally
+        {
+            if (pushStack)
+                restoreContextStack();
+        }
+
+    }
+
+
+    /*
+     check whether pattern (starting from the second byte) appears inside
+     posStream (at the current position)
+     @param posStream the stream to search inside
+     @param pattern the blob passed in by the user to search with
+     @return true if match, false otherwise
+     */
+    private boolean checkMatch(Blob pattern)
+        throws IOException, SQLException
+    {
+        // check whether rest matches
+        // might improve performance by reading buffer at a time
+        InputStream pStream;
+        try
+        {
+            pStream = pattern.getBinaryStream();
+        }
+        catch (SQLException e)
+        {
+            return false;
+        }
+        if (pStream == null)
+            return false;
+        // throw away first character since we already read it in the calling
+        // method
+        int b1 = pStream.read();
+        if (b1 < 0)
+            return false;
+        while (true)
+        {
+            b1 = pStream.read();
+            if (b1 < 0)  // search blob runs out
+                return true;
+            int b2 = read();
+            if ((b1 != b2) || (b2 < 0))  // mismatch or stream runs out
+                return false;
+        }
+    }
+
+    /*
+      Convert exceptions where needed before calling handleException to convert
+      them to SQLExceptions.
+    */
+	private SQLException handleMyExceptions(Throwable t)
+        throws SQLException
+    {
+        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 handleException(t);
+	}
+
+
+   /*
+    If we have a stream, release the resources associated with it.
+    */
+    protected void finalize()
+    {
+        if (!materialized)
+            ((Resetable)myStream).closeStream();
+    }
+
+	/**
+    Following methods are for the new JDBC 3.0 methods in java.sql.Blob
+    (see the JDBC 3.0 spec). We have the JDBC 3.0 methods in Local20
+    package, so we don't have to have a new class in Local30.
+    The new JDBC 3.0 methods don't make use of any new JDBC3.0 classes and
+    so this will work fine in jdbc2.0 configuration.
+	*/
+
+	/////////////////////////////////////////////////////////////////////////
+	//
+	//	JDBC 3.0	-	New public methods
+	//
+	/////////////////////////////////////////////////////////////////////////
+
+	/**
+    * JDBC 3.0
+    *
+    * Writes the given array of bytes to the BLOB value that this Blob object
+    * represents, starting at position pos, and returns the number of bytes written.
+    *
+    * @param pos - the position in the BLOB object at which to start writing
+    * @param bytes - the array of bytes to be written to the BLOB value that this
+    * Blob object represents
+    * @return the number of bytes written
+    * @exception SQLException Feature not implemented for now.
+	*/
+	public int setBytes(long pos, byte[] bytes) throws SQLException {
+            return setBytes(pos, bytes, 0, bytes.length);
+	}
+
+   /**
+    * JDBC 3.0
+    *
+    * Writes all or part of the given array of byte array to the BLOB value that
+    * this Blob object represents and returns the number of bytes written.
+    * Writing starts at position pos in the BLOB value; len bytes from the given
+    * byte array are written.
+    *
+    * @param pos - the position in the BLOB object at which to start writing
+    * @param bytes - the array of bytes to be written to the BLOB value that this
+    * Blob object represents
+    * @param offset - the offset into the array bytes at which to start reading
+    * the bytes to be set
+    * @param len - the number of bytes to be written to the BLOB value from the
+    * array of bytes bytes
+    * @return the number of bytes written
+    * @exception SQLException Feature not implemented for now.
+	*/
+    public int setBytes(long pos,
+            byte[] bytes,
+            int offset,
+            int len) throws SQLException {
+        checkValidity();
+        try {
+            if (materialized) {
+                if (pos - 1 > length())
+                    throw Util.generateCsSQLException(
+                            SQLState.BLOB_POSITION_TOO_LARGE, new Long(pos));
+                if (pos < 1)
+                    throw Util.generateCsSQLException(
+                        SQLState.BLOB_BAD_POSITION, new Long(pos));
+                len = (int) control.write (bytes, offset, len, pos - 1);
+            }
+            else {
+                control = new LOBStreamControl (getEmbedConnection().getDBName());
+                control.copyData (myStream, length());
+                len = (int) control.write(bytes, offset, len, pos - 1);
+                myStream.close();
+                materialized = true;
+            }
+            return len;
+        }
+        catch (IOException e) {
+            throw Util.setStreamFailure (e);
+        }
+        catch (StandardException se) {
+            throw Util.generateCsSQLException (se);
+        }
+    }
+
+   /**
+    * JDBC 3.0
+    *
+    * Retrieves a stream that can be used to write to the BLOB value that this
+    * Blob object represents. The stream begins at position pos. 
+    *
+    * @param pos - the position in the BLOB object at which to start writing
+    * @return a java.io.OutputStream object to which data can be written 
+    * @exception SQLException Feature not implemented for now.
+	*/
+	public java.io.OutputStream setBinaryStream (long pos)
+                                    throws SQLException {
+            checkValidity ();
+            if (pos - 1 > length())
+                throw Util.generateCsSQLException(
+                    SQLState.BLOB_POSITION_TOO_LARGE, new Long(pos));
+            if (pos < 1)
+                throw Util.generateCsSQLException(
+                    SQLState.BLOB_BAD_POSITION, new Long(pos));
+            try {
+                if (materialized) {
+                    return control.getOutputStream (pos - 1);
+                }
+                else {
+                    control = new LOBStreamControl (
+                                            getEmbedConnection().getDBName());
+                    control.copyData (myStream, pos - 1);
+                    myStream.close ();
+                    materialized = true;
+                    return control.getOutputStream(pos - 1);
+
+                }
+            }
+            catch (IOException e) {
+                throw Util.setStreamFailure (e);
+            }
+            catch (StandardException se) {
+                throw Util.generateCsSQLException (se);
+            }
+	}
+
+	/**
+    * JDBC 3.0
+    *
+    * Truncates the BLOB value that this Blob object represents to be len bytes
+    * in length.
+    *
+    * @param len - the length, in bytes, to which the BLOB value that this Blob
+    * object represents should be truncated
+    * @exception SQLException Feature not implemented for now.
+	*/
+	public void truncate(long len)
+                                        throws SQLException
+	{
+            if (len > length())
+                throw Util.generateCsSQLException(
+                    SQLState.BLOB_LENGTH_TOO_LONG, new Long(pos));
+            try {
+                if (materialized) {
+                    control.truncate (len);
+                }
+                else {
+                    control = new LOBStreamControl (getEmbedConnection().getDBName());
+                    control.copyData (myStream, len);
+                    myStream.close();
+                    materialized = true;
+                }
+            }
+            catch (IOException e) {
+                throw Util.setStreamFailure (e);
+            }
+            catch (StandardException se) {
+                throw Util.generateCsSQLException (se);
+            }
+	}
+
+    /////////////////////////////////////////////////////////////////////////
+    //
+    //	JDBC 4.0	-	New public methods
+    //
+    /////////////////////////////////////////////////////////////////////////
+    /**
+     * This method frees the <code>Blob</code> object and releases the resources that 
+     * it holds. 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 Blob's 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 Blob object is no longer
+        //valid
+        isValid = false;
+        
+        //initialialize length to default value -1
+        myLength = -1;
+        
+        //if it is a stream then close it.
+        //if a array of bytes then initialize it to null
+        //to free up space
+        if (!materialized)
+            ((Resetable)myStream).closeStream();
+        else {
+            try {
+                control.free ();
+                control = null;
+            }
+            catch (IOException e) {
+                throw Util.setStreamFailure (e);
+            }
+        }
+    }
+    
+    /**
+     * Returns an <code>InputStream</code> object that contains a partial 
+     * <code>Blob</code> value, starting with the byte specified by pos, 
+     * which is length bytes in length.
+     *
+     * @param pos the offset to the first byte of the partial value to be 
+     *      retrieved. The first byte in the <code>Blob</code> is at 
+     *      position 1
+     * @param length the length in bytes of the partial value to be retrieved
+     * @return through which the partial <code>Blob</code> value can be read. 
+     * @throws SQLException if pos is less than 1 or if pos is greater than 
+     *      the number of bytes in the <code>Blob</code> or if pos + length is
+     *      greater than the number of bytes in the <code>Blob</code>
+     */
+    public InputStream getBinaryStream(long pos, long length)
+        throws SQLException {
+        //call checkValidity to exit by throwing a SQLException if
+        //the Blob object has been freed by calling free() on it
+        checkValidity();
+        
+        if (pos <= 0) {
+            throw Util.generateCsSQLException(
+                    SQLState.BLOB_BAD_POSITION,
+                    new Long(pos));
+        }
+        if (length < 0) {
+            throw Util.generateCsSQLException(
+                    SQLState.BLOB_NONPOSITIVE_LENGTH,
+                    new Long(length));
+        }
+        if (length > (this.length() - pos)) {
+            throw Util.generateCsSQLException(
+                    SQLState.POS_AND_LENGTH_GREATER_THAN_LOB,
+                    new Long(pos), new Long(length));
+        }
+        
+        try {
+            return new UpdatableBlobStream(this,
+                                            getBinaryStream(),
+                                            pos-1,
+                                            length);
+        } catch (IOException ioe) {
+            throw Util.setStreamFailure(ioe);
+        }
+    }
+    
+    /*
+     * 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
+     *
+     * throws SQLException if isvalid is not true.
+     */
+    private void checkValidity() throws SQLException{
+        //check for connection to maintain sqlcode for closed
+        //connection
+        getEmbedConnection().checkIfClosed();
+        if(!isValid)
+            throw newSQLException(SQLState.LOB_OBJECT_INVALID);
+    }
+    
+    /**
+     * Returns if blob data is stored locally (using LOBStreamControl).
+     * @return true if materialized else false
+     */
+    boolean isMaterialized () {
+        return materialized;
+    }
+
+    /**
+     * Return locator for this lob.
+     * 
+     * @return
+     */
+    public int getLocator() {
+        return locator;
+    }
+}

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?rev=617186&r1=617185&r2=617186&view=diff
==============================================================================
--- 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 Thu Jan 31 10:48:30 2008
@@ -24,6 +24,7 @@
 
 import org.apache.derby.iapi.reference.SQLState;
 import org.apache.derby.iapi.error.StandardException;
+import org.apache.derby.iapi.jdbc.EngineClob;
 import org.apache.derby.iapi.services.sanity.SanityManager;
 import org.apache.derby.iapi.types.DataValueDescriptor;
 import org.apache.derby.iapi.types.Resetable;
@@ -66,7 +67,7 @@
         new update methods can safely be added into implementation.
    </UL>
  */
-final class EmbedClob extends ConnectionChild implements Clob
+final class EmbedClob extends ConnectionChild implements Clob, EngineClob
 {
 
     /**
@@ -78,6 +79,8 @@
     /** Tells whether the Clob has been freed or not. */
     private boolean isValid = true;
 
+    private final int locator;
+    
     /**
      * Creates an empty Clob object.
      *
@@ -88,7 +91,7 @@
     EmbedClob(EmbedConnection con) throws SQLException {
         super(con);
         this.clob = new TemporaryClob (con.getDBName(), this);
-        con.addLOBMapping (this);
+        this.locator = con.addLOBMapping (this);
     }
 
     /**
@@ -153,7 +156,7 @@
                 throw se;
             }
         }
-        con.addLOBMapping (this);
+        this.locator = con.addLOBMapping (this);
     }
 
     /**
@@ -782,5 +785,12 @@
      */
     InternalClob getInternalClob() {
         return this.clob;
+    }
+
+    /**     
+     * @return locator value for this Clob.
+     */
+    public int getLocator() {
+        return locator;
     }
 }

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBStoredProcedure.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBStoredProcedure.java?rev=617186&r1=617185&r2=617186&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBStoredProcedure.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBStoredProcedure.java Thu Jan 31 10:48:30 2008
@@ -26,6 +26,8 @@
 import java.sql.DriverManager;
 import java.sql.SQLException;
 
+import org.apache.derby.iapi.jdbc.EngineBlob;
+import org.apache.derby.iapi.jdbc.EngineClob;
 import org.apache.derby.iapi.reference.SQLState;
 
 /**
@@ -41,8 +43,8 @@
      * @throws SQLException
      */
     public static int CLOBCREATELOCATOR() throws SQLException {
-        Clob clob = getEmbedConnection().createClob();
-        return getEmbedConnection().addLOBMapping(clob);
+        EngineClob clob = (EngineClob) getEmbedConnection().createClob();
+        return clob.getLocator();
     }
 
     /**
@@ -201,8 +203,8 @@
      * @throws SQLException
      */
     public static int BLOBCREATELOCATOR() throws SQLException {
-        Blob blob = getEmbedConnection().createBlob();
-        return getEmbedConnection().addLOBMapping(blob);
+        EngineBlob blob = (EngineBlob) getEmbedConnection().createBlob();
+        return blob.getLocator();
     }
 
     /**

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?rev=617186&r1=617185&r2=617186&view=diff
==============================================================================
--- 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 Thu Jan 31 10:48:30 2008
@@ -1522,6 +1522,32 @@
     }
 
     /**
+     * Make sure we get an error attempting to access the 
+     * lob after commit.
+     */
+    public void testClobAfterCommitWithSecondClob() throws SQLException
+    {
+        getConnection().setAutoCommit(false);
+        Statement s1 = createStatement();
+        ResultSet rs1 = s1.executeQuery("values cast('first' as clob)");
+        rs1.next();
+        Clob first = rs1.getClob(1);
+        rs1.close(); 
+        commit();
+        Statement s2 = createStatement();
+        ResultSet rs2 = s2.executeQuery("values cast('second' as clob)");
+        rs2.next();
+        Clob second = rs2.getClob(1);
+        try {
+            first.getSubString(1,100);
+            fail("first.getSubString should have failed because after the commit");
+        } catch (SQLException se){
+            assertSQLState(INVALID_LOB,se);
+        }
+        assertEquals("second",second.getSubString(1, 100));        
+        rs2.close(); 
+    }
+    /**
      * Test fix for derby-1382.
      *
      * Test that the getClob() returns the correct value for the clob before and

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BlobStoredProcedureTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BlobStoredProcedureTest.java?rev=617186&r1=617185&r2=617186&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BlobStoredProcedureTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BlobStoredProcedureTest.java Thu Jan 31 10:48:30 2008
@@ -164,7 +164,7 @@
         //remember in setup a locator is already created
         //hence expected value is 2
         assertEquals("The locator values returned by " +
-            "SYSIBM.BLOBCREATELOCATOR() are incorrect", 4, locator);
+            "SYSIBM.BLOBCREATELOCATOR() are incorrect", 2, locator);
         cs.close();
     }
 

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobStoredProcedureTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobStoredProcedureTest.java?rev=617186&r1=617185&r2=617186&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobStoredProcedureTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ClobStoredProcedureTest.java Thu Jan 31 10:48:30 2008
@@ -76,7 +76,6 @@
         cs.registerOutParameter(1, java.sql.Types.INTEGER);
         cs.executeUpdate();
         locator = cs.getInt(1);
-
         cs.close();
         cs  = prepareCall("CALL SYSIBM.CLOBSETSTRING(?,?,?,?)");
         cs.setInt(1, locator);
@@ -139,7 +138,7 @@
         //remember in setup a locator is already created
         //hence expected value is 2
         assertEquals("The locator values returned by " +
-            "SYSIBM.CLOBCREATELOCATOR() are incorrect", 4, locator);
+            "SYSIBM.CLOBCREATELOCATOR() are incorrect", 2, locator);
         cs.close();
     }