db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From krist...@apache.org
Subject svn commit: r506918 - in /db/derby/code/trunk/java: engine/org/apache/derby/impl/io/ engine/org/apache/derby/impl/jdbc/ engine/org/apache/derby/io/ shared/org/apache/derby/shared/common/reference/ testing/org/apache/derbyTesting/functionTests/tests/jdb...
Date Tue, 13 Feb 2007 09:15:15 GMT
Author: kristwaa
Date: Tue Feb 13 01:15:13 2007
New Revision: 506918

URL: http://svn.apache.org/viewvc?view=rev&rev=506918
Log:
DERBY-2247: Provide set-methods for Blob in the embedded driver.

Patch contributed by Anurag Shekhar.

Added:
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBInputStream.java   (with props)
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBOutputStream.java   (with props)
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBStreamControl.java   (with props)
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/BlobSetMethodsTest.java   (with props)
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/LobStreamTest.java   (with props)
Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/impl/io/BaseStorageFactory.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedBlob.java
    db/derby/code/trunk/java/engine/org/apache/derby/io/StorageFactory.java
    db/derby/code/trunk/java/engine/org/apache/derby/io/StorageRandomAccessFile.java
    db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/_Suite.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/corruptio/CorruptBaseStorageFactory.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/corruptio/CorruptRandomAccessFile.java

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/io/BaseStorageFactory.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/io/BaseStorageFactory.java?view=diff&rev=506918&r1=506917&r2=506918
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/io/BaseStorageFactory.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/io/BaseStorageFactory.java Tue Feb 13 01:15:13 2007
@@ -45,6 +45,7 @@
     protected String uniqueName;
     protected String canonicalName;
     private static final String TEMP_DIR_PREFIX = "derbytmp_";
+    private int counter = 0;
 
     /**
      * Most of the initialization is done in the init method.
@@ -267,12 +268,37 @@
 		File temp = File.createTempFile("derby", "tmp");
 		String parent = temp.getParent();
 		temp.delete();
-
 		return parent;
 	}
 
     public int getStorageFactoryVersion()
     {
         return StorageFactory.VERSION_NUMBER;
+    }
+
+    /**
+     * Create and returns a temporary file in temporary file system of database.
+     * @param prefix String to prefix the random name generator. It can be null
+     * @param suffix String to suffix the random name generator. ".tmp" will be
+     *               used if null.
+     * @return StorageFile
+     */
+    public StorageFile createTemporaryFile (String prefix, String suffix)
+                                                            throws IOException {
+        StorageFile tmpDir = getTempDir();
+        if (prefix == null)
+            prefix = "tmp";
+        if (suffix == null)
+            suffix = ".tmp";
+        StorageFile tmpFile = null;
+        synchronized (tmpDir) {
+            do {
+                String fileName = prefix + Integer.toString (counter++)
+                            + suffix;
+                tmpFile = newStorageFile (tmpDir, fileName);
+            } while (tmpFile.exists());
+            tmpFile.createNewFile();
+        }
+        return tmpFile;
     }
 }

Modified: 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?view=diff&rev=506918&r1=506917&r2=506918
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedBlob.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedBlob.java Tue Feb 13 01:15:13 2007
@@ -24,6 +24,7 @@
 
 import org.apache.derby.iapi.reference.SQLState;
 import org.apache.derby.iapi.error.StandardException;
+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;
@@ -71,8 +72,8 @@
 
 final class EmbedBlob extends ConnectionChild implements Blob
 {
-    // blob is either bytes or stream
-    private boolean         isBytes;
+    // blob is either materialized or still in stream
+    private boolean         materialized;
     private InputStream     myStream;
     
     /*
@@ -81,7 +82,6 @@
      */
     private long myLength = -1;
     
-    private byte[]          myBytes;
     // note: cannot control position of the stream since user can do a getBinaryStream
     private long            pos;
     // this stream sits on top of myStream
@@ -96,6 +96,8 @@
     //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().
@@ -107,11 +109,16 @@
      *
      */
     
-     EmbedBlob(byte [] blobBytes,EmbedConnection con) {
-         super(con);
-         myBytes = blobBytes;
-         isBytes = true;
-         myLength = myBytes.length;
+     EmbedBlob(byte [] blobBytes,EmbedConnection con) throws SQLException {
+        super(con);
+         try {
+             control = new LOBStreamControl (con.getDBName());
+             control.write (blobBytes, 0);
+             materialized = true;
+         }
+         catch (IOException e) {
+             throw Util.setStreamFailure (e);
+         }
      }
      
     /*
@@ -129,21 +136,27 @@
         myStream = dvd.getStream();
         if (myStream == null)
         {
-            isBytes = true;
+            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");
-
-            myLength = dvdBytes.length;
-            myBytes = new byte[dvdBytes.length];
-            System.arraycopy(dvdBytes, 0, myBytes, 0, dvdBytes.length);
+            control = new LOBStreamControl (getEmbedConnection().getDBName());
+            try {
+                control.write (dvdBytes, pos);
+            }
+            catch (SQLException e) {
+                throw StandardException.newException (e.getSQLState());
+            }
+            catch (IOException e) {
+                throw StandardException.newException (null, e);
+            }
         }
         else
         {
-            isBytes = false;
+            materialized = false;
 
             /*
              We are expecting this stream to be a FormatIdInputStream with an
@@ -186,10 +199,9 @@
     {
         if (SanityManager.DEBUG)
             SanityManager.ASSERT(newPos >= 0);
-        if (isBytes)
+        if (materialized)
             pos = newPos;
-        else
-        {
+        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();
@@ -211,16 +223,14 @@
     /*
         Reads one byte, either from the byte array or else from the stream.
     */
-    private int read()
-        throws IOException
-    {
+    private int read() throws IOException, SQLException {
         int c;
-        if (isBytes)
+        if (materialized)
         {
-            if (pos >= myBytes.length)
+            if (pos >= control.getLength())
                 return -1;
             else
-                c = myBytes[(int) pos];
+                c = control.read (pos);
         }
         else
             c = biStream.read();
@@ -242,7 +252,13 @@
         //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;
         
@@ -331,18 +347,15 @@
                     SQLState.BLOB_NONPOSITIVE_LENGTH, new Integer(length));
 
             byte[] result;
-            // if we have a byte array, not a stream
-            if (isBytes)
-            {
-                // if blob length is less than pos bytes + 1, raise an exception
-                if (myBytes.length + 1 < startPos)
-                    throw StandardException.newException(
-                        SQLState.BLOB_POSITION_TOO_LARGE, new Long(startPos));
-                // cannot go over length of array
-                int lengthFromPos = myBytes.length - (int) startPos + 1;
-                int actualLength = length > lengthFromPos ? lengthFromPos : length;
-                result = new byte[actualLength];
-                System.arraycopy(myBytes, ((int) startPos) - 1, result, 0, actualLength);
+            // if the blob is materialized
+            if (materialized) {
+                 result = new byte [length];
+                 int sz = control.read (result, startPos - 1);
+                 if (sz < length) {
+                     byte [] tmparray = new byte [sz];
+                     System.arraycopy (result, 0, tmparray, 0, sz);
+                     result = tmparray;
+                 }
             }
             else // we have a stream
             {
@@ -413,9 +426,9 @@
         try
         {
             // if we have byte array, not a stream
-            if (isBytes)
+            if (materialized)
             {
-                return new NewByteArrayInputStream(myBytes);
+                return control.getInputStream(0);
             }
             else
             { 
@@ -530,8 +543,7 @@
      @return true if match, false otherwise
      */
     private boolean checkMatch(byte[] pattern)
-        throws IOException
-    {
+        throws IOException, SQLException {
        // check whether rest matches
        // might improve performance by reading more
         for (int i = 1; i < pattern.length; i++)
@@ -637,7 +649,7 @@
      @return true if match, false otherwise
      */
     private boolean checkMatch(Blob pattern)
-        throws IOException
+        throws IOException, SQLException
     {
         // check whether rest matches
         // might improve performance by reading buffer at a time
@@ -692,7 +704,7 @@
     */
     protected void finalize()
     {
-        if (!isBytes)
+        if (!materialized)
             ((Resetable)myStream).closeStream();
     }
 
@@ -722,14 +734,11 @@
     * @return the number of bytes written
     * @exception SQLException Feature not implemented for now.
 	*/
-	public int setBytes(long pos,
-					byte[] bytes)
-    throws SQLException
-	{
-		throw Util.notImplemented();
+	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
@@ -747,15 +756,36 @@
     * @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
-	{
-		throw Util.notImplemented();
-	}
+    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);
+        }
+    }
 
-	/**
+   /**
     * JDBC 3.0
     *
     * Retrieves a stream that can be used to write to the BLOB value that this
@@ -765,10 +795,35 @@
     * @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
-	{
-		throw Util.notImplemented();
+	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 {
+                    if (pos > length())
+                        throw Util.generateCsSQLException (
+                                                SQLState.BLOB_POSITION_TOO_LARGE);
+                    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);
+            }
 	}
 
 	/**
@@ -784,7 +839,23 @@
 	public void truncate(long len)
     throws SQLException
 	{
-		throw Util.notImplemented();
+            if (len > length())
+                throw Util.generateCsSQLException(
+                    SQLState.BLOB_NONPOSITIVE_LENGTH, 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);
+            }
 	}
 
     /////////////////////////////////////////////////////////////////////////
@@ -816,10 +887,17 @@
         //if it is a stream then close it.
         //if a array of bytes then initialize it to null
         //to free up space
-        if (!isBytes)
+        if (!materialized)
             ((Resetable)myStream).closeStream();
-        else
-            myBytes = null;
+        else {
+            try {
+                control.free ();
+                control = null;
+            }
+            catch (IOException e) {
+                throw Util.setStreamFailure (e);
+            }
+        }
     }
     
     /**

Added: 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=auto&rev=506918
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBInputStream.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBInputStream.java Tue Feb 13 01:15:13 2007
@@ -0,0 +1,230 @@
+/*
+ 
+   Derby - Class org.apache.derby.impl.jdbc.LOBInputStream
+ 
+   Copyright (c) 2006 The Apache Software Foundation or its licensors, where 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.InputStream;
+import java.sql.SQLException;
+import org.apache.derby.iapi.reference.SQLState;
+import org.apache.derby.iapi.services.i18n.MessageService;
+import org.apache.derby.shared.common.error.ExceptionUtil;
+
+/**
+ * This input stream is built on top of LOBStreamControl. All the read methods
+ * are routed to LOBStreamControl.
+ */
+
+public class LOBInputStream extends InputStream {
+
+    private boolean closed;
+    private LOBStreamControl control;
+    private long pos;
+
+    LOBInputStream(LOBStreamControl control, long position) {
+        closed = false;
+        this.control = control;
+        pos = position;
+
+    }
+
+    /**
+     * Reads up to <code>len</code> bytes of data from the input stream into
+     * an array of bytes.  An attempt is made to read as many as
+     * <code>len</code> bytes, but a smaller number may be read.
+     * The number of bytes actually read is returned as an integer.
+     *
+     * <p> This method blocks until input data is available, end of file is
+     * detected, or an exception is thrown.
+     *
+     * <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 <code>IndexOutOfBoundsException</code> is
+     * thrown.
+     *
+     * <p> If <code>len</code> is zero, then no bytes are read and
+     * <code>0</code> is returned; otherwise, there is an attempt to read at
+     * least one byte. If no byte is available because the stream is at end of
+     * file, the value <code>-1</code> is returned; otherwise, at least one
+     * byte is read and stored into <code>b</code>.
+     *
+     * <p> The first byte read is stored into element <code>b[off]</code>, the
+     * next one into <code>b[off+1]</code>, and so on. The number of bytes read
+     * is, at most, equal to <code>len</code>. Let <i>k</i> be the number of
+     * bytes actually read; these bytes will be stored in elements
+     * <code>b[off]</code> through <code>b[off+</code><i>k</i><code>-1]</code>,
+     * leaving elements <code>b[off+</code><i>k</i><code>]</code> through
+     * <code>b[off+len-1]</code> unaffected.
+     *
+     * <p> In every case, elements <code>b[0]</code> through
+     * <code>b[off]</code> and elements <code>b[off+len]</code> through
+     * <code>b[b.length-1]</code> are unaffected.
+     *
+     * <p> If the first byte cannot be read for any reason other than end of
+     * file, then an <code>IOException</code> is thrown. In particular, an
+     * <code>IOException</code> is thrown if the input stream has been closed.
+     *
+     * <p> The <code>read(b,</code> <code>off,</code> <code>len)</code> method
+     * for class <code>InputStream</code> simply calls the method
+     * <code>read()</code> repeatedly. If the first such call results in an
+     * <code>IOException</code>, that exception is returned from the call to
+     * the <code>read(b,</code> <code>off,</code> <code>len)</code> method.  If
+     * any subsequent call to <code>read()</code> results in a
+     * <code>IOException</code>, the exception is caught and treated as if it
+     * were end of file; the bytes read up to that point are stored into
+     * <code>b</code> and the number of bytes read before the exception
+     * occurred is returned.  Subclasses are encouraged to provide a more
+     * efficient implementation of this method.
+     *
+     * @param b     the buffer into which the data is read.
+     * @param off   the start offset in array <code>b</code>
+     *                   at which the data is written.
+     * @param len   the maximum number of bytes to read.
+     * @return the total number of bytes read into the buffer, or
+     *             <code>-1</code> if there is no more data because the end of
+     *             the stream has been reached.
+     * @exception IOException  if an I/O error occurs.
+     * @exception NullPointerException  if <code>b</code> is <code>null</code>.
+     * @see java.io.InputStream#read()
+     */
+    public int read(byte[] b, int off, int len) throws IOException {
+        if (closed)
+            throw new IOException (
+                   MessageService.getTextMessage(SQLState.LANG_STREAM_CLOSED));
+        try {
+            int ret = control.read(b, off, len, pos);
+            if (ret > 0) {
+                pos += ret;
+                return ret;
+            }
+            return -1;
+        } catch (SQLException e) {
+            return handleSQLException (e);
+        }
+    }
+
+    /**
+     * Reads some number of bytes from the input stream and stores them into
+     * the buffer array <code>b</code>. The number of bytes actually read is
+     * returned as an integer.  This method blocks until input data is
+     * available, end of file is detected, or an exception is thrown.
+     *
+     * <p> If <code>b</code> is <code>null</code>, a
+     * <code>NullPointerException</code> is thrown.  If the length of
+     * <code>b</code> is zero, then no bytes are read and <code>0</code> is
+     * returned; otherwise, there is an attempt to read at least one byte. If
+     * no byte is available because the stream is at end of file, the value
+     * <code>-1</code> is returned; otherwise, at least one byte is read and
+     * stored into <code>b</code>.
+     *
+     * <p> The first byte read is stored into element <code>b[0]</code>, the
+     * next one into <code>b[1]</code>, and so on. The number of bytes read is,
+     * at most, equal to the length of <code>b</code>. Let <i>k</i> be the
+     * number of bytes actually read; these bytes will be stored in elements
+     * <code>b[0]</code> through <code>b[</code><i>k</i><code>-1]</code>,
+     * leaving elements <code>b[</code><i>k</i><code>]</code> through
+     * <code>b[b.length-1]</code> unaffected.
+     *
+     * <p> If the first byte cannot be read for any reason other than end of
+     * file, then an <code>IOException</code> is thrown. In particular, an
+     * <code>IOException</code> is thrown if the input stream has been closed.
+     *
+     * <p> The <code>read(b)</code> method for class <code>InputStream</code>
+     * has the same effect as: <pre><code> read(b, 0, b.length) </code></pre>
+     *
+     * @param b   the buffer into which the data is read.
+     * @return the total number of bytes read into the buffer, or
+     *             <code>-1</code> is there is no more data because the end of
+     *             the stream has been reached.
+     * @exception IOException  if an I/O error occurs.
+     * @exception NullPointerException  if <code>b</code> is <code>null</code>.
+     * @see java.io.InputStream#read(byte[], int, int)
+     */
+    public int read(byte[] b) throws IOException {
+        if (closed)
+            throw new IOException (
+                   MessageService.getTextMessage(SQLState.LANG_STREAM_CLOSED));
+        try {
+            int ret = control.read(b, pos);
+            if (ret > 0) {
+                pos += ret;
+                return ret;
+            }
+            return -1;
+        } catch (SQLException e) {
+            return handleSQLException (e);
+        }
+    }
+
+    /**
+     * Closes this input stream and releases any system resources associated
+     * with the stream.
+     *
+     * <p> The <code>close</code> method of <code>InputStream</code> does
+     * nothing.
+     *
+     * @exception IOException  if an I/O error occurs.
+     */
+    public void close() throws IOException {
+        closed = true;
+    }
+
+    /**
+     * Reads the next byte of data from the input stream. The value byte is
+     * returned as an <code>int</code> in the range <code>0</code> to
+     * <code>255</code>. If no byte is available because the end of the stream
+     * has been reached, the value <code>-1</code> is returned. This method
+     * blocks until input data is available, the end of the stream is detected,
+     * or an exception is thrown.
+     *
+     * <p> A subclass must provide an implementation of this method.
+     *
+     * @return the next byte of data, or <code>-1</code> if the end of the
+     *             stream is reached.
+     * @exception IOException  if an I/O error occurs.
+     */
+    public int read() throws IOException {
+        if (closed)
+            throw new IOException (
+                   MessageService.getTextMessage (SQLState.LANG_STREAM_CLOSED));
+        try {
+            int ret = control.read(pos);
+            if (ret != -1)
+                pos += 1;
+            return ret;
+        } catch (SQLException e) {
+            throw new IOException(e.getMessage());
+        }
+    }
+
+    private int handleSQLException (SQLException e) throws IOException {
+        if (e.getSQLState().equals(
+                ExceptionUtil.getSQLStateFromIdentifier(
+                              SQLState.BLOB_POSITION_TOO_LARGE)))
+            return -1;
+        if (e.getSQLState().equals(
+                ExceptionUtil.getSQLStateFromIdentifier(
+                              SQLState.BLOB_INVALID_OFFSET)))
+                throw new ArrayIndexOutOfBoundsException (e.getMessage());
+            throw new IOException(e.getMessage());
+    }
+}

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

Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBOutputStream.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBOutputStream.java?view=auto&rev=506918
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBOutputStream.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBOutputStream.java Tue Feb 13 01:15:13 2007
@@ -0,0 +1,152 @@
+/*
+
+   Derby - Class org.apache.derby.impl.jdbc.LOBOutputStream
+
+   Copyright (c) 2006 The Apache Software Foundation or its licensors, where 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.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.sql.SQLException;
+import org.apache.derby.iapi.reference.SQLState;
+import org.apache.derby.iapi.services.i18n.MessageService;
+import org.apache.derby.shared.common.error.ExceptionUtil;
+
+/**
+ * This is an output stream built on top of LOBStreamControl.
+ * All the write methods are routed to LOBStreamControl.
+ */
+
+public class LOBOutputStream extends OutputStream {
+    private boolean closed;
+    private LOBStreamControl control;
+    private long pos;
+
+    LOBOutputStream(LOBStreamControl control, long position) {
+        closed = false;
+        this.control = control;
+        pos = position;
+    }
+
+    /**
+     * 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 {
+        if (closed)
+            throw new IOException (
+                    MessageService.getTextMessage(
+                        SQLState.LANG_STREAM_CLOSED));
+        try {
+            pos = control.write(b, pos);
+        } catch (SQLException e) {
+            throw  new IOException(e.getMessage());
+        }
+    }
+
+    /**
+     * 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 {
+        if (closed)
+            throw new IOException (
+                    MessageService.getTextMessage(
+                        SQLState.LANG_STREAM_CLOSED));
+        try {
+            pos = control.write(b, off, len, pos);
+        } catch (SQLException e) {
+             if (e.getSQLState().equals(
+                    ExceptionUtil.getSQLStateFromIdentifier(
+                                  SQLState.BLOB_INVALID_OFFSET)))
+                    throw new ArrayIndexOutOfBoundsException (e.getMessage());
+            throw new IOException(e.getMessage());
+        }
+    }
+
+    /**
+     * Writes <code>b.length</code> bytes from the specified byte array
+     * to this output stream. The general contract for <code>write(b)</code>
+     * is that it should have exactly the same effect as the call
+     * <code>write(b, 0, b.length)</code>.
+     *
+     * @param b   the data.
+     * @exception IOException  if an I/O error occurs.
+     * @see java.io.OutputStream#write(byte[], int, int)
+     */
+    public void write(byte[] b) throws IOException {
+        if (closed)
+            throw new IOException (
+                    MessageService.getTextMessage(
+                        SQLState.LANG_STREAM_CLOSED));
+        try {
+            pos = control.write(b, pos);
+        } catch (SQLException e) {
+            throw new IOException(e.getMessage());
+        }
+    }
+
+    /**
+     * Closes this output stream and releases any system resources
+     * associated with this stream. The general contract of <code>close</code>
+     * is that it closes the output stream. A closed stream cannot perform
+     * output operations and cannot be reopened.
+     * <p>
+     * The <code>close</code> method of <code>OutputStream</code> does nothing.
+     *
+     * @exception IOException  if an I/O error occurs.
+     */
+    public void close() throws IOException {
+        closed = true;
+    }
+}

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

Added: 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=auto&rev=506918
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBStreamControl.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/LOBStreamControl.java Tue Feb 13 01:15:13 2007
@@ -0,0 +1,414 @@
+/*
+
+   Derby - Class org.apache.derby.impl.jdbc.LOBStreamControl
+
+   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.EOFException;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.sql.SQLException;
+import org.apache.derby.iapi.error.StandardException;
+import org.apache.derby.iapi.reference.Property;
+import org.apache.derby.iapi.reference.SQLState;
+import org.apache.derby.iapi.services.monitor.Monitor;
+import org.apache.derby.iapi.store.raw.data.DataFactory;
+import org.apache.derby.io.StorageFile;
+import org.apache.derby.io.StorageRandomAccessFile;
+import org.apache.derby.shared.common.error.ExceptionUtil;
+
+/**
+ * This class acts as a layer of blob/clob repository (in memory or file).
+ * The max bytes of data stored in memory is MAX_BUF_SIZE. When write
+ * increases the data beyond this value a temporary file is created and data
+ * is moved into that. If truncate reduces the size of the file below
+ * MAX_BUF_SIZE the data moved into memory.
+ *
+ * This class also creates Input- and OutputStream which can be used to access
+ * blob data irrespective of if its in memory or in file.
+ */
+
+class LOBStreamControl {
+    //private RandomAccessFile tmpFile;
+    private StorageRandomAccessFile tmpFile;
+    private StorageFile lobFile;
+    private byte [] dataBytes = new byte [0];
+    private boolean isBytes = true;
+    //keeping max 4k bytes in memory
+    //randomly selected value
+    private final int MAX_BUF_SIZE = 4096;
+    private String dbName;
+
+    public LOBStreamControl (String dbName) {
+        this.dbName = dbName;
+    }
+
+    private void init(byte [] b, long len) throws IOException, SQLException {
+        try {
+            AccessController.doPrivileged (new PrivilegedExceptionAction() {
+                public Object run() throws IOException, StandardException {
+                    Object monitor = Monitor.findService(
+                            Property.DATABASE_MODULE, dbName);
+                    DataFactory df =  (DataFactory) Monitor.findServiceModule(
+                            monitor, DataFactory.MODULE);
+                    lobFile =
+                        df.getStorageFactory().createTemporaryFile("lob", null);
+                    tmpFile = lobFile.getRandomAccessFile ("rw");
+                    return null;
+                }
+            });
+            //create a temporary file
+        }
+        catch (PrivilegedActionException pae) {
+            Exception e = pae.getException();
+            if (e instanceof StandardException)
+                throw Util.generateCsSQLException ((StandardException) e);
+            if (e instanceof IOException)
+                throw (IOException) e;
+            if (e instanceof RuntimeException)
+                throw (RuntimeException) e;
+        }
+        isBytes = false;
+        //now this call will write into the file
+        if (len != 0)
+            write(b, 0, (int) len, 0);
+    }
+
+    private long updateData(byte[] bytes, int offset, int len, long pos)
+    throws SQLException {
+        if (dataBytes == null) {
+            if ((int) pos == 0) {
+                dataBytes = new byte [len];
+                System.arraycopy(bytes, offset, dataBytes, (int) pos, len);
+                return len;
+            }
+            else {
+                //invalid postion
+                throw Util.generateCsSQLException(
+                        SQLState.BLOB_POSITION_TOO_LARGE, new Long(pos));
+            }
+        }
+        else {
+            if (pos > dataBytes.length) {
+                //invalid postion
+                throw Util.generateCsSQLException(
+                        SQLState.BLOB_POSITION_TOO_LARGE, new Long(pos));
+            }
+            else {
+                if (pos + len < dataBytes.length) {
+                    System.arraycopy(bytes, offset, dataBytes, (int) pos, len);
+                }
+                else {
+                    byte [] tmpBytes = new byte [len + (int) pos];
+                    System.arraycopy(dataBytes, 0 , tmpBytes, 0, (int) pos);
+                    System.arraycopy(bytes, offset, tmpBytes, (int) pos, len);
+                    dataBytes = tmpBytes;
+                }
+            }
+            return pos + len;
+        }
+    }
+
+    private void isValidPostion(long pos) throws SQLException, IOException {
+        if (pos < 0)
+            throw Util.generateCsSQLException(
+                    SQLState.BLOB_NONPOSITIVE_LENGTH, new Long(pos + 1));
+        if (pos > Integer.MAX_VALUE)
+            throw Util.generateCsSQLException(
+                    SQLState.BLOB_POSITION_TOO_LARGE, new Long(pos + 1));
+
+        if (isBytes) {
+            if (dataBytes == null) {
+                if (pos != 0)
+                    throw Util.generateCsSQLException(
+                            SQLState.BLOB_POSITION_TOO_LARGE, new Long(pos + 1));
+            } else if (dataBytes.length < pos)
+                throw Util.generateCsSQLException(
+                        SQLState.BLOB_POSITION_TOO_LARGE, new Long(pos + 1));
+        } else {
+            if (pos > tmpFile.length())
+                throw Util.generateCsSQLException(
+                        SQLState.BLOB_POSITION_TOO_LARGE, new Long(pos + 1));
+        }
+    }
+
+    private void isValidOffset(int off, int length) throws SQLException, IOException {
+        if (off < 0 || off > length)
+            throw Util.generateCsSQLException(
+                    SQLState.BLOB_INVALID_OFFSET, new Integer(off));
+    }
+
+    /**
+     * Writes one byte.
+     * @param b byte
+     * @param pos
+     * @return new postion
+     * @throws IOException, SQLException
+     */
+    synchronized long write(int b, long pos) throws IOException, SQLException {
+        isValidPostion(pos);
+        if (isBytes) {
+            if (pos + 1 < MAX_BUF_SIZE) {
+                byte [] bytes = {(byte) b};
+                updateData(bytes, 0, 1, pos);
+                return pos + 1;
+            } else {
+                init(dataBytes, pos);
+            }
+        }
+        if (tmpFile.getFilePointer() != pos)
+            tmpFile.seek(pos);
+        tmpFile.write(b);
+        return tmpFile.getFilePointer();
+    }
+
+    /**
+     * Writes part of the byte array.
+     * @param b byte array
+     * @param off offset from where to read from the byte array
+     * @param len number of bytes to be copied
+     * @param pos starting postion
+     * @return new postion
+     * @throws IOException, SQLException
+     */
+    synchronized long write(byte[] b, int off, int len, long pos)
+    throws IOException, SQLException {
+        try {
+            isValidPostion(pos);
+            isValidOffset(off, b.length);
+        }
+        catch (SQLException e) {
+            if (e.getSQLState().equals(
+                    ExceptionUtil.getSQLStateFromIdentifier(
+                                  SQLState.BLOB_INVALID_OFFSET)))
+                    throw new ArrayIndexOutOfBoundsException (e.getMessage());
+        }
+        if (isBytes) {
+            long finalLen = (dataBytes != null) ? dataBytes.length + b.length
+                    : b.length;
+            if (finalLen < MAX_BUF_SIZE)
+                return updateData(b, off, len, pos);
+            else {
+                init(dataBytes, pos);
+            }
+        }
+        if (tmpFile.getFilePointer() != pos)
+            tmpFile.seek(pos);
+        tmpFile.write(b, off, len);
+        return tmpFile.getFilePointer();
+    }
+
+    /**
+     * Writes byte array starting from pos.
+     * @param b bytes array
+     * @param pos starting postion
+     * @return new position
+     * @throws IOException, SQLException
+     */
+    synchronized long write(byte[] b, long pos)
+    throws IOException, SQLException {
+        isValidPostion(pos);
+        if (isBytes) {
+            long len = (dataBytes != null) ? dataBytes.length + b.length
+                    : b.length;
+            if (len < MAX_BUF_SIZE)
+                return updateData(b, 0, b.length, pos);
+            else {
+                init(dataBytes, pos);
+            }
+        }
+        if (tmpFile.getFilePointer() != pos)
+            tmpFile.seek(pos);
+        tmpFile.write(b);
+        return tmpFile.getFilePointer();
+    }
+
+    /**
+     * Reads one byte.
+     * @param pos postion from where to read
+     * @return byte
+     * @throws IOException, SQLException
+     */
+    synchronized int read(long pos) throws IOException, SQLException {
+        isValidPostion(pos);
+        if (isBytes) {
+            if (dataBytes.length == pos)
+                return -1;
+            return dataBytes [(int) pos] & 0xff;
+        }
+        if (tmpFile.getFilePointer() != pos)
+            tmpFile.seek(pos);
+        try {
+            return tmpFile.readByte() & 0xff;
+        }
+        catch (EOFException eof) {
+            return -1;
+        }
+    }
+
+    private int readBytes(byte [] b, int off, int len, long pos) {
+        int lengthFromPos = dataBytes.length - (int) pos;
+        int actualLength = len > lengthFromPos ? lengthFromPos : len;
+        byte [] result = new byte[actualLength];
+        System.arraycopy(dataBytes, (int) pos, b, off, actualLength);
+        return actualLength;
+    }
+
+    /**
+     * Copies bytes into byte array starting from pos.
+     * @param b bytes array to copy data
+     * @param pos starting postion
+     * @return new postion
+     * @throws IOException, SQLException
+     */
+    synchronized int read(byte[] b, long pos)
+    throws IOException, SQLException {
+        return read (b, 0, b.length, pos);
+    }
+
+    /**
+     * Reads bytes starting from 'position' into bytes array.
+     * starting from 'offset'
+     * @param b array into the bytes will be copied
+     * @param off offset from where the array has to be populated
+     * @param len number of bytes to read
+     * @param pos initial postion before reading
+     * @return number new postion
+     * @throws IOException, SQLException
+     */
+    synchronized int read(byte[] buff, int off, int len, long pos)
+    throws IOException, SQLException {
+        isValidPostion(pos);
+        isValidOffset(off, buff.length);
+        if (isBytes) {
+            return readBytes(buff, off, len, pos);
+        }
+        if (tmpFile.getFilePointer() != pos)
+            tmpFile.seek(pos);
+        return tmpFile.read (buff, off, len);
+    }
+
+    /**
+     * returns input stream linked with this object.
+     * @param pos initial postion
+     * @return InputStream
+     */
+    InputStream getInputStream(long pos) {
+        return new LOBInputStream(this, pos);
+    }
+
+    /**
+     * returns output stream linked with this object
+     * @param pos initial postion
+     * @return OutputStream
+     */
+    OutputStream getOutputStream(long pos) {
+        return new LOBOutputStream(this, pos);
+    }
+
+    /**
+     * Returns length of data.
+     * @return length
+     * @throws IOException
+     */
+    long getLength() throws IOException {
+        if (isBytes)
+            return dataBytes.length;
+        return tmpFile.length();
+    }
+
+    /**
+     * Resets the size.
+     * @param size new size should be smaller than exisiting size
+     * @throws IOException, SQLException
+     */
+    synchronized void truncate(long size) throws IOException, SQLException {
+        isValidPostion(size);
+        if (isBytes) {
+            byte [] tmpByte = new byte [(int) size];
+            System.arraycopy(dataBytes, 0, tmpByte, 0, (int) size);
+        } else {
+            if (size < Integer.MAX_VALUE && size < MAX_BUF_SIZE) {
+                dataBytes = new byte [(int) size];
+                read(dataBytes, 0);
+                isBytes = true;
+                tmpFile.close();
+                tmpFile = null;
+            } else
+                tmpFile.setLength(size);
+        }
+    }
+
+    /**
+     * Copies bytes from stream to local storage.
+     * @param inStream
+     * @param pos length to be copied
+     * @throws IOException, SQLException
+     */
+    synchronized void copyData(InputStream inStream,
+            long length) throws IOException, SQLException {
+        byte [] data = new byte [MAX_BUF_SIZE];
+        long sz = 0;
+        while (sz < length) {
+            int len = (int) (((length - sz) >= MAX_BUF_SIZE) ? MAX_BUF_SIZE
+                    : length - sz);
+            inStream.read(data, 0, len);
+            write(data, 0, len, sz);
+            sz += len;
+        }
+    }
+
+    protected void finalize() throws Throwable {
+        free();
+    }
+
+    /**
+     * Invalidates all the variables and closes file handle if open.
+     * @throws IOexception
+     */
+    void free() throws IOException {
+        dataBytes = null;
+        if (tmpFile != null) {
+            tmpFile.close();
+            try {
+                AccessController.doPrivileged (new PrivilegedExceptionAction() {
+                    public Object run() throws IOException {
+                        lobFile.delete();
+                        return null;
+                    }
+                });
+            }
+            catch (PrivilegedActionException pae) {
+                Exception e = pae.getException();
+                if (e instanceof IOException)
+                    throw (IOException) e;
+                if (e instanceof RuntimeException)
+                    throw (RuntimeException) e;
+            }
+        }
+    }
+}

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

Modified: db/derby/code/trunk/java/engine/org/apache/derby/io/StorageFactory.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/io/StorageFactory.java?view=diff&rev=506918&r1=506917&r2=506918
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/io/StorageFactory.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/io/StorageFactory.java Tue Feb 13 01:15:13 2007
@@ -221,4 +221,14 @@
      * @return the StorageFactory version supported by this implementation
      */
     public int getStorageFactoryVersion();
+
+    /**
+     * Create and returns a temporary file in temporary file system of database
+     * @param prefix String to prefix the random name generator. It can be null
+     * @param suffix String to suffix the random name generator. ".tmp" will be
+     *               used if null.
+     * @return StorageFile
+     */
+    public StorageFile createTemporaryFile (String prefix, String suffix)
+                                                            throws IOException;
 }

Modified: db/derby/code/trunk/java/engine/org/apache/derby/io/StorageRandomAccessFile.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/io/StorageRandomAccessFile.java?view=diff&rev=506918&r1=506917&r2=506918
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/io/StorageRandomAccessFile.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/io/StorageRandomAccessFile.java Tue Feb 13 01:15:13 2007
@@ -107,4 +107,27 @@
      * @exception IOException If an IO error occurs.
      */
     public void sync( boolean metaData) throws IOException;
+
+    /**
+     * Reads up to <code>len</code> bytes of data from this file into an
+     * array of bytes. This method blocks until at least one byte of input
+     * is available.
+     * <p>
+     *
+     * @param b     the buffer into which the data is read.
+     * @param off   the start offset in array <code>b</code>
+     *                   at which the data is written.
+     * @param len   the maximum number of bytes read.
+     * @return the total number of bytes read into the buffer, or
+     *             <code>-1</code> if there is no more data because the end of
+     *             the file has been reached.
+     * @exception IOException If the first byte cannot be read for any reason
+     * other than end of file, or if the random access file has been closed, or
+     * if some other I/O error occurs.
+     * @exception NullPointerException If <code>b</code> is <code>null</code>.
+     * @exception IndexOutOfBoundsException If <code>off</code> is negative,
+     * <code>len</code> is negative, or <code>len</code> is greater than
+     * <code>b.length - off</code>
+     */
+    public int read(byte[] b, int off, int len) throws IOException;
 }

Modified: db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java?view=diff&rev=506918&r1=506917&r2=506918
==============================================================================
--- db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java (original)
+++ db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java Tue Feb 13 01:15:13 2007
@@ -936,6 +936,7 @@
 	// MORE GENERIC LANGUAGE STUFF
 	String LANG_COLUMN_DEFAULT										   = "42Z09.U";
 	String LANG_STREAM												   = "42Z11.U";
+    String LANG_STREAM_CLOSED                                          = "42Z12";
 
 	// String LANG_UPDATABLE_VTI_BAD_GETMETADATA						   = "42Z14";
 

Added: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/BlobSetMethodsTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/BlobSetMethodsTest.java?view=auto&rev=506918
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/BlobSetMethodsTest.java (added)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/BlobSetMethodsTest.java Tue Feb 13 01:15:13 2007
@@ -0,0 +1,163 @@
+/*
+ *
+ * Derby - Class BlobSetMethodsTest
+ *
+ * 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.derbyTesting.functionTests.tests.jdbc4;
+
+import java.sql.Blob;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+import org.apache.derbyTesting.functionTests.harness.RunSuite;
+import org.apache.derbyTesting.functionTests.harness.RunTest;
+import org.apache.derbyTesting.junit.BaseJDBCTestCase;
+import org.apache.derbyTesting.junit.TestConfiguration;
+
+/**
+ * tests set methods of blob
+ */
+public class BlobSetMethodsTest extends BaseJDBCTestCase {
+    private static int BUFFER_SIZE = 1024;
+    private static int UPDATE_SIZE = 100;
+
+    public BlobSetMethodsTest (String name) {
+        super (name);
+    }
+
+    protected void setUp() throws Exception {
+        Connection con = getConnection();
+        Statement stmt = con.createStatement();
+        stmt.execute ("create table blobtest (id integer, data Blob)");
+        stmt.close();
+        con.close();
+    }
+
+    /**
+     * Create test suite.
+     */
+    public static Test suite() {
+        return TestConfiguration.embeddedSuite(BlobSetMethodsTest.class);
+    }
+
+    /**
+     * tests set bytes method of blob.
+     */
+    public void testSetBytes () throws SQLException {
+        Connection con = getConnection();
+        try {
+            con.setAutoCommit (false);
+            PreparedStatement pstmt = con.prepareStatement("insert into " +
+                    "blobtest (id, data) values (?,?)");
+            pstmt.setInt (1,1);
+            Blob blob = con.createBlob();
+            //add 1024 bytes
+            byte [] data = new byte [BUFFER_SIZE];
+            for (int i = 0; i < BUFFER_SIZE; i++) {
+                data [i] = (byte) (i % 255);
+            }
+            blob.setBytes (1, data);
+            assertEquals (BUFFER_SIZE, blob.length());
+            pstmt.setBlob (2, blob);
+            pstmt.executeUpdate();
+            Statement stmt = con.createStatement();
+            ResultSet rs = stmt.executeQuery(
+                    "select data from blobtest where id = 1");
+            assertEquals(true, rs.next());
+            blob = rs.getBlob (1);
+            assertEquals (BUFFER_SIZE, blob.length());
+            //update blob in the middle
+            byte [] data1 = new byte [UPDATE_SIZE];
+            for (int i = 0; i < UPDATE_SIZE; i++)
+                data1 [i] = 120;//just any value
+            blob.setBytes (UPDATE_SIZE, data1);
+            byte [] data2 = blob.getBytes (100, UPDATE_SIZE);
+            for (int i = 0; i < UPDATE_SIZE; i++)
+                assertEquals (data1 [i], data2 [i]);
+            //update it at the end
+            blob.setBytes (BUFFER_SIZE + 1, data1);
+            assertEquals (BUFFER_SIZE + UPDATE_SIZE, blob.length());
+            data2 = blob.getBytes (BUFFER_SIZE + 1, UPDATE_SIZE);
+            for (int i = 0; i < UPDATE_SIZE; i++)
+                assertEquals (data1 [i], data2 [i]);
+            //insert the blob and test again
+            pstmt.setInt (1, 2);
+            pstmt.setBlob (2, blob);
+            pstmt.executeUpdate();
+            rs = stmt.executeQuery("select data from blobtest where " +
+                    "id = 2");
+            assertEquals(true, rs.next());
+            blob = rs.getBlob (1);
+            assertEquals (BUFFER_SIZE + UPDATE_SIZE, blob.length());
+            data2 = blob.getBytes (100, UPDATE_SIZE);
+            for (int i = 0; i < UPDATE_SIZE; i++)
+                assertEquals (data1 [i], data2 [i]);
+            data2 = blob.getBytes (BUFFER_SIZE + 1, UPDATE_SIZE);
+            for (int i = 0; i < UPDATE_SIZE; i++)
+                assertEquals (data1 [i], data2 [i]);
+
+            //now add more than 4k so file get in use
+            for (int i = 0; i < 5; i++)
+                blob.setBytes (i * BUFFER_SIZE + 1, data);
+            assertEquals (BUFFER_SIZE * 5 , blob.length());
+            blob.setBytes (BUFFER_SIZE + 1, data1);
+            blob.setBytes (BUFFER_SIZE * 5 + 1, data1);
+            assertEquals (5 * BUFFER_SIZE + UPDATE_SIZE, blob.length());
+            //insert it into table
+            pstmt.setInt (1, 3);
+            pstmt.setBlob (2, blob);
+            pstmt.executeUpdate ();
+            rs = stmt.executeQuery("select data from blobtest where " +
+                                    "id = 3");
+            assertEquals(true, rs.next());
+            blob = rs.getBlob (1);
+            data2 = blob.getBytes (BUFFER_SIZE + 1, UPDATE_SIZE);
+            assertEquals (5 * BUFFER_SIZE + UPDATE_SIZE, blob.length());
+            for (int i = 0; i < UPDATE_SIZE; i++)
+                assertEquals (data1 [i], data2 [i]);
+            data2 = blob.getBytes (5 * BUFFER_SIZE + 1, UPDATE_SIZE);
+            for (int i = 0; i < UPDATE_SIZE; i++)
+                assertEquals (data1 [i], data2 [i]);
+            //test truncate
+            blob.truncate (BUFFER_SIZE);
+            assertEquals (BUFFER_SIZE, blob.length());
+            con.commit();
+            stmt.close();
+            pstmt.close();
+        }
+        finally {
+            if (con != null) {
+                con.commit();
+                con.close();
+            }
+        }
+    }
+
+    protected void tearDown() throws SQLException {
+        Connection con = getConnection();
+        Statement stmt = con.createStatement();
+        stmt.execute ("drop table blobtest");
+        stmt.close();
+        con.close();
+    }
+}

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

Added: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/LobStreamTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/LobStreamTest.java?view=auto&rev=506918
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/LobStreamTest.java (added)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/LobStreamTest.java Tue Feb 13 01:15:13 2007
@@ -0,0 +1,441 @@
+/*
+ *
+ * Derby - Class LobStreamTest
+ *
+ * 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.derbyTesting.functionTests.tests.jdbc4;
+
+import java.io.IOException;
+import junit.framework.*;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.File;
+import java.sql.Blob;
+import java.sql.Connection;
+import org.apache.derbyTesting.junit.BaseJDBCTestCase;
+import org.apache.derbyTesting.junit.TestConfiguration;
+
+
+
+public class LobStreamTest extends BaseJDBCTestCase {
+
+    private static final String dbName = "LobStreamTest";
+    private static final boolean useLOBStreamControl = true;
+
+    private Connection conn = null;
+    File f = null;
+    private InputStream in = null;
+    private OutputStream out = null;
+    private Blob blob;
+
+    public LobStreamTest(String testName) {
+        super(testName);
+    }
+
+    protected void setUp() throws Exception {
+        conn = getConnection();
+        blob = conn.createBlob();
+        in = blob.getBinaryStream();
+        out = blob.setBinaryStream (1);
+    }
+
+    protected void tearDown() throws Exception {
+        blob.free();
+        conn.rollback();
+        conn.close();
+    }
+
+    /**
+     * Test cases
+     */
+
+    /**
+     * Test read and write methods with one parameter.
+     */
+    public void testReadWriteNoParameters() throws IOException {
+
+        for (int i=0; i<8000; i++) {
+            out.write((i%255));
+        }
+
+        for (int i=0; i<8000; i++) {
+            int value = in.read();
+            assertEquals("Read value is equal to i", i%255, value);
+        }
+
+        in.close();
+        in = null;
+        out.close();
+        out = null;
+    }
+
+    /**
+     * test read method with no parameters and write method with one parameter
+     */
+    public void testReadNoParameterWriteOneParameter() throws IOException {
+        byte[] b = new byte[100];
+        for (int i=0; i<8000; i++) {
+            b[i%100] = (byte) (((byte)(i%255)) & 0xFF);
+            if (i%100 == 99) {
+                out.write(b);
+            }
+        }
+
+        for (int i=0; i<8000; i++) {
+            int value = in.read();
+            assertEquals("Read value is equal to i", i%255, value);
+        }
+
+        in.close();
+        in = null;
+        out.close();
+        out = null;
+    }
+
+    /**
+     * test read and write method with one parameter
+     */
+    public void testReadWriteOneParameter() throws IOException {
+
+        byte[] b = new byte[100];
+        for (int i=0; i<8000; i++) {
+            b[i%100] = (byte) (((byte)(i%255)) & 0xFF);
+            if (i%100 == 99) {
+                out.write(b);
+            }
+        }
+
+        for (int i=0; i<80; i++) {
+            int count = in.read(b);
+            for (int j=0; j<count; j++) {
+                int value = b[j] & 0xFF;
+                assertEquals("Read value is equal to i",
+                                        (((i * 100) + j) % 255), value);
+            }
+        }
+
+        in.close();
+        in = null;
+        out.close();
+        out = null;
+    }
+
+    /**
+     * test read and write method with three parameter
+     */
+    public void testReadWriteThreeParameter() throws IOException {
+
+        byte[] b = new byte[200];
+        int offset = 0;
+        for (int i=0; i<8000; i++) {
+            b[(i%100) + offset] = (byte) (((byte)(i%255)) & 0xFF);
+            if (i%100 == 99) {
+                out.write(b, offset, 100);
+                offset += 1;
+            }
+        }
+
+        offset = 0;
+        for (int i=0; i<80; i++) {
+            int count = in.read(b, offset, 100);
+            for (int j=0; j<count; j++) {
+                int value = b[j + offset] & 0xFF;
+                assertEquals("Read value is equal to i",
+                                        (((i * 100) + j) % 255), value);
+            }
+            offset += 1;
+        }
+
+        in.close();
+        in = null;
+        out.close();
+        out = null;
+
+    }
+
+    /**
+     * Test that stream returns -1 on end of stream when reading a byte at
+     * a time.
+     */
+    public void testEndOfStreamValue() throws IOException {
+
+        for (int i=0; i<8000; i++) {
+            out.write((i%255));
+        }
+
+        int count = 0;
+        while (in.read() != -1) {
+            count++;
+        }
+        assertEquals("All values have been read", 8000, count);
+
+        in.close();
+        in = null;
+        out.close();
+        out = null;
+
+    }
+
+    /**
+     * Test that read with one parameter returns the correct count
+     * at end of stream.
+     */
+    public void testEndOfStreamOnReadOneParameter() throws IOException {
+
+        for (int i=0; i<8050; i++) {
+            out.write((i%255));
+        }
+
+        int count = 0, totalCount = 0;
+        byte[] b = new byte[100];
+        assertTrue("b.length should not be = 0", (b.length != 0));
+        while ((count = in.read(b)) != -1) {
+            assertTrue("Number of bytes read can not be = 0", (count != 0));
+            totalCount += count;
+        }
+        assertEquals("All values have been read", 8050, totalCount);
+
+        in.close();
+        in = null;
+        out.close();
+        out = null;
+
+    }
+
+    /**
+     * Test that read with three parameter returns the correct count
+     * at end of stream.
+     */
+    public void testEndOfStreamOnReadThreeParameters() throws IOException {
+
+        for (int i=0; i<8050; i++) {
+            out.write((i%255));
+        }
+
+        int count = 0, totalCount = 0, offset = 0;
+        byte[] b = new byte[200];
+        assertTrue("b.length should not be = 0", (b.length != 0));
+        while ((count = in.read(b, offset, 100)) != -1) {
+            assertTrue("Number of bytes read can not be = 0", (count != 0));
+            totalCount += count;
+            offset++;
+        }
+        assertEquals("All values have been read", 8050, totalCount);
+
+        in.close();
+        in = null;
+        out.close();
+        out = null;
+    }
+
+    /**
+     * Test the skip method of the input stream
+     */
+    public void testSkip() throws IOException {
+
+        for (int i=0; i<8000; i++) {
+            out.write((i%255));
+        }
+
+        int i = 0;
+        while (i < 8000) {
+            if ((i%255) < 100) {
+                int value = in.read();
+                assertEquals("Read value is equal to i", i%255, value);
+                i++;
+            } else {
+                long count = in.skip(155);
+                i += count;
+            }
+        }
+        in.close();
+        out.close();
+        in = null;
+        out = null;
+    }
+
+    /**
+     * Test write method with three parameters with invalid parameter
+     * values.
+     */
+     public void testWriteWithInvalidParameterValues() throws IOException {
+
+        for (int i=0; i<8000; i++) {
+            out.write((i%255));
+        }
+
+        // b is null
+        byte[] b = null;
+        try {
+            out.write(b, 100, 20);
+            assertTrue("byte[] = null should cause exception", false);
+        } catch (Exception e) {
+            assertTrue("Expected NullPointerException",
+                    e instanceof NullPointerException);
+        }
+        // length > b.length
+        b = new byte[100];
+        try {
+            out.write(b, 0, 200);
+            assertTrue("length > b.length should cause exception", false);
+        } catch (Exception e) {
+            assertTrue("Expected IndexOutOfBoundException",
+                    e instanceof IndexOutOfBoundsException);
+        }
+
+        // offset > b.length
+        try {
+            out.write(b, 150, 0);
+            assertTrue("offset > b.length should cause exception", false);
+        } catch (Exception e) {
+            assertTrue("Expected IndexOutOfBoundException",
+                                    e instanceof IndexOutOfBoundsException);
+        }
+
+        // offset + length > b.length
+        try {
+            out.write(b, 50, 100);
+            assertTrue("length + offset > b.length should cause exception",
+                    false);
+        } catch (Exception e) {
+            assertTrue("Expected IndexOutOfBoundException",
+                                    e instanceof IndexOutOfBoundsException);
+        }
+
+        // offset is negative
+        try {
+            out.write(b, -50, 100);
+            assertTrue("negative offset should cause exception", false);
+        } catch (Exception e) {
+            assertTrue("Expected IndexOutOfBoundException",
+                                        e instanceof IndexOutOfBoundsException);
+        }
+
+        //length is negative
+        try {
+            out.write(b, 0, -100);
+            assertTrue("negative length should cause exception", false);
+        } catch (Exception e) {
+            assertTrue("Expected IndexOutOfBoundException",
+                                e instanceof IndexOutOfBoundsException);
+        }
+
+        // stream is closed
+        out.close();
+        try {
+            out.write(b, 0, 100);
+            assertTrue("Stream should be closed", false);
+        } catch (Exception e) {
+            assertTrue("Expected IOException", e instanceof IOException);
+        }
+
+        out = null;
+        in.close();
+        in = null;
+     }
+
+    /**
+     * Test write method with three parameters with invalid parameter
+     * values.
+     */
+     public void testReadWithInvalidParameterValues() throws IOException {
+
+        for (int i=0; i<8000; i++) {
+            out.write((i%255));
+        }
+        out.close();
+        out = null;
+
+        // b is null
+        byte[] b = null;
+        try {
+            in.read(b, 100, 20);
+            assertTrue("byte[] = null should cause exception", false);
+        } catch (Exception e) {
+            assertTrue("Expected NullPointerException",
+                                        e instanceof NullPointerException);
+        }
+
+        // length > b.length
+        b = new byte[100];
+        try {
+            int count = in.read(b, 0, 200);
+            assertTrue("length > b.length should cause exception", false);
+        } catch (Exception e) {
+            assertTrue("Expected IndexOutOfBoundException",
+                                    e instanceof IndexOutOfBoundsException);
+        }
+
+        // offset > b.length
+        try {
+            in.read(b, 150, 0);
+            assertTrue("offset > b.length should cause exception", false);
+        } catch (Exception e) {
+            assertTrue("Expected IndexOutOfBoundException",
+                                        e instanceof IndexOutOfBoundsException);
+        }
+
+        // offset + length > b.length
+        try {
+            int count = in.read(b, 50, 100);
+            assertTrue("offset + length > b.length should cause exception",
+                                                                        false);
+        } catch (Exception e) {
+            assertTrue("Expected IndexOutOfBoundException",
+                                    e instanceof IndexOutOfBoundsException);
+        }
+
+        // offset is negative
+        try {
+            in.read(b, -50, 100);
+            assertTrue("negative offset should cause exception", false);
+        } catch (Exception e) {
+            assertTrue("Expected IndexOutOfBoundException",
+                                        e instanceof IndexOutOfBoundsException);
+        }
+
+        //length is negative
+        try {
+            in.read(b, 0, -100);
+            assertTrue("negative length should cause exception", false);
+        } catch (Exception e) {
+            assertTrue("Expected IndexOutOfBoundException",
+                                    e instanceof IndexOutOfBoundsException);
+        }
+
+        // stream is closed
+        in.close();
+        try {
+            in.read(b, 0, 100);
+            assertTrue("Stream should be closed", false);
+        } catch (Exception e) {
+            assertTrue("Expected IOException", e instanceof IOException);
+        }
+
+        in = null;
+     }
+
+     /**
+     * Suite method automatically generated by JUnit module.
+     */
+    public static Test suite() {
+        return TestConfiguration.embeddedSuite(LobStreamTest.class);
+    }
+
+}

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

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/_Suite.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/_Suite.java?view=diff&rev=506918&r1=506917&r2=506918
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/_Suite.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbc4/_Suite.java Tue Feb 13 01:15:13 2007
@@ -70,6 +70,8 @@
 		suite.addTest(UnsupportedVetter.suite());
 		suite.addTest(XA40Test.suite());
         suite.addTest(VerifySignatures.suite());
+        suite.addTest (LobStreamTest.suite());
+        suite.addTest (BlobSetMethodsTest.suite());
 		
 		return suite;
 	}

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/corruptio/CorruptBaseStorageFactory.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/corruptio/CorruptBaseStorageFactory.java?view=diff&rev=506918&r1=506917&r2=506918
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/corruptio/CorruptBaseStorageFactory.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/corruptio/CorruptBaseStorageFactory.java Tue Feb 13 01:15:13 2007
@@ -263,4 +263,18 @@
      */
 	abstract WritableStorageFactory getRealStorageFactory();
 
+    /**
+     * Create and returns a temporary file in temporary file system of database.
+     *
+     * @param prefix String to prefix the random name generator. It can be null
+     * @param suffix String to suffix the random name generator. ".tmp" will be
+     *               used if null.
+     * @return StorageFile
+     */
+    public StorageFile createTemporaryFile(String prefix, String suffix)
+                                                            throws IOException {
+        return new CorruptFile(realStorageFactory.createTemporaryFile(
+                prefix, suffix));
+    }
+
 }

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/corruptio/CorruptRandomAccessFile.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/corruptio/CorruptRandomAccessFile.java?view=diff&rev=506918&r1=506917&r2=506918
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/corruptio/CorruptRandomAccessFile.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/corruptio/CorruptRandomAccessFile.java Tue Feb 13 01:15:13 2007
@@ -357,4 +357,29 @@
 		realRaf.writeUTF(str);
 	}
 
+    /**
+     * Reads up to <code>len</code> bytes of data from this file into an
+     * array of bytes. This method blocks until at least one byte of input
+     * is available.
+     * <p>
+     *
+     *
+     * @param b     the buffer into which the data is read.
+     * @param off   the start offset in array <code>b</code>
+     *                   at which the data is written.
+     * @param len   the maximum number of bytes read.
+     * @return the total number of bytes read into the buffer, or
+     *             <code>-1</code> if there is no more data because the end of
+     *             the file has been reached.
+     * @exception IOException If the first byte cannot be read for any reason
+     * other than end of file, or if the random access file has been closed, or
+     * if some other I/O error occurs.
+     * @exception NullPointerException If <code>b</code> is <code>null</code>.
+     * @exception IndexOutOfBoundsException If <code>off</code> is negative,
+     * <code>len</code> is negative, or <code>len</code> is greater than
+     * <code>b.length - off</code>
+     */
+    public int read(byte[] b, int off, int len) throws IOException {
+        return realRaf.read (b, off, len);
+    }
 }



Mime
View raw message