[ 
http://issues.apache.org/jira/browse/DERBY-1486?page=comments#action_12420190 ] 

David Heath commented on DERBY-1486:
------------------------------------

I have taken a look at the JDBC 3.0 specification and also the JavaDoc and 
noticed they did not quite agree on when an auto-commit causes the JDBC driver 
to do a transaction commit. As mentioned, in the Specification it says:

For Select statements, the statement is complete when the associated result set 
is
closed. The result set is closed as soon as one of the following occurs: 
•       all of the rows have been retrieved
•       the associated Statement object is re-executed
•       another Statement object is executed on the same connection

But in the JavaDoc for Connection.setAutoCommit we have:

The commit occurs when the statement completes or the next execute occurs, 
whichever comes first. In the case of statements returning a ResultSet object, 
the statement completes when the last row of the ResultSet object has been 
retrieved or the ResultSet object has been closed.

The JavaDoc seems to imply the statement does not complete until one calls 
close(), but I may be wrong as the above text is ambiguous.


So, as we know we can interleave reading ResultsSets if we use different 
Statements (From the JavaDoc for Statement), so please consider the following 
scenario:

Table1
Column1 BLOB
Column2 INTEGER  

Table2
Column1 INTEGER
Column2 INTEGER

Where Column2 in Table.1 is a Foreign Key referencing Column1 in Table2.

Now, if one wanted to read these tables, you would hope you could iterate over 
a ResultSet containing all the rows from Table1, then for each row, read the 
Blob and foreign key, then recover the information from Table2 using the 
foreign key (using a separate Statement and ResultSet).

However, the above fails, which can be shown by modifying by example as follows:

Adding a second row to the table:
      addRows(con, 10000, 1);
      addRows(con, 10000, 2);

In readRows change the loop from:
    
   if (rs.next()) {
      rs.getInt(1);
      
      readTable1(con, id);
      
      InputStream stream = rs.getBinaryStream(2);

      ObjectInputStream objStream = new ObjectInputStream(stream);

      Object obj = objStream.readObject();

      double[] array = (double[]) obj;

      System.out.println(array.length);
    }

To:
    while (rs.next()) {
      rs.getInt(1);

      InputStream stream = rs.getBinaryStream(2);

      ObjectInputStream objStream = new ObjectInputStream(stream);

      Object obj = objStream.readObject();

      double[] array = (double[]) obj;

      System.out.println(array.length);
      readTable1(con, id);
    }

The example, will read the first Blob, foreign key and results from the 
corresponding row in the second table, however at which point Blobs are no 
longer available from the first result set, hence it fails with the same 
Exception (ERROR 40XD0) when it tries to read the Blob for row 2.

Thus, I would conclude that derby behavior is wrong. BTW, this example works 
fine under MySQL.

So, I would really appreciate it if someone could advise me on whether you 
think this is a bug in derby or whether it is abiding by the standards? If the 
former, would you have any idea when a fix may be available, as we are trying 
to port from MySQL to derby and as far as we can tell this is the last 
remaining problem.


>  ERROR 40XD0 - When exracting Blob from a database
> --------------------------------------------------
>
>          Key: DERBY-1486
>          URL: http://issues.apache.org/jira/browse/DERBY-1486
>      Project: Derby
>         Type: Bug

>   Components: Unknown
>     Versions: 10.1.2.1
>  Environment: Windows XP
>     Reporter: David Heath

>
> An exception occurs when extracting a Blob from a database. 
> The following code, will ALWAYS fail with the Exception:
> java.io.IOException: ERROR 40XD0: Container has been closed
>         at 
> org.apache.derby.impl.store.raw.data.OverflowInputStream.fillByteHolder(Unknown
>  Source)
>         at 
> org.apache.derby.impl.store.raw.data.BufferedByteHolderInputStream.read(Unknown
>  Source)
>         at java.io.DataInputStream.read(Unknown Source)
>         at java.io.FilterInputStream.read(Unknown Source)
>         at java.io.ObjectInputStream$PeekInputStream.read(Unknown Source)
>         at java.io.ObjectInputStream$PeekInputStream.readFully(Unknown Source)
>         at java.io.ObjectInputStream$BlockDataInputStream.readDoubles(Unknown 
> Source)
>         at java.io.ObjectInputStream.readArray(Unknown Source)
>         at java.io.ObjectInputStream.readObject0(Unknown Source)
>         at java.io.ObjectInputStream.readObject(Unknown Source)
>         at BlobTest.readRows(BlobTest.java:81)
>         at BlobTest.main(BlobTest.java:23)
> CODE:
> import java.io.*;
> import java.sql.*;
> import java.util.*;
> public class BlobTest
> {
>   private static final String TABLE1 = "CREATE TABLE TABLE_1 ( "
>                                      + "ID INTEGER NOT NULL, "
>                                      + "COL_2 INTEGER NOT NULL, "
>                                      + "PRIMARY KEY (ID) )";
>   private static final String TABLE2 = "CREATE TABLE TABLE_2 ( "
>                                      + "ID INTEGER NOT NULL, "
>                                      + "COL_BLOB BLOB, "
>                                      + "PRIMARY KEY (ID) )";
>   public static void main(String... args) {
>     try {
>       createDBandTables();
>       Connection con = getConnection();
>       addRows(con, 10000, 1);
>       readRows(con, 1);
>       con.close();
>     }
>     catch(Exception exp) {
>       exp.printStackTrace();
>     }
>   }
>   private static void addRows(Connection con, int size, int id) 
>                                                          throws Exception
>   {
>     String sql = "INSERT INTO TABLE_1 VALUES(?, ?)";
>     PreparedStatement pstmt = con.prepareStatement(sql);
>     pstmt.setInt(1, id);
>     pstmt.setInt(2, 2);
>     pstmt.executeUpdate();
>     pstmt.close();
>     double[] array = new double[size];
>     array[size-1] = 1.23;
>     sql = "INSERT INTO TABLE_2 VALUES(?, ?)";
>     pstmt = con.prepareStatement(sql);
>     pstmt.setInt(1, id);
>     ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
>     ObjectOutputStream objStream = new ObjectOutputStream(byteStream);
>     objStream.writeObject(array);         // Convert object to byte stream 
>     byte[] bytes = byteStream.toByteArray();
>     ByteArrayInputStream inStream = new ByteArrayInputStream(bytes);
>     pstmt.setBinaryStream(2, inStream, bytes.length);
>     pstmt.executeUpdate();
>     pstmt.close();
>   }
>   private static void readRows(Connection con, int id) throws Exception
>   {
>     String sql = "SELECT * FROM TABLE_2";
>     Statement stmt = con.createStatement();
>     ResultSet rs = stmt.executeQuery(sql);
>     if (rs.next()) {
>       rs.getInt(1);
>       readTable1(con, id);
>       InputStream stream = rs.getBinaryStream(2);
>       ObjectInputStream objStream = new ObjectInputStream(stream);
>       Object obj = objStream.readObject();   // FAILS HERE
>       double[] array = (double[]) obj;
>       System.out.println(array.length);
>     }
>     rs.close();
>     stmt.close();
>   }
>   private static void readTable1(Connection con, int id) throws Exception {
>     String sql = "SELECT ID FROM TABLE_1 WHERE ID=" + id;    
>     Statement stmt = con.createStatement();
>     ResultSet rs = stmt.executeQuery(sql);
>     if (rs.next()) {
>     }
>     rs.close();
>     stmt.close();
>   }
>   
>   private static Connection getConnection() throws Exception {
>     String driver="org.apache.derby.jdbc.EmbeddedDriver";
>     Properties p = System.getProperties();
>     p.put("derby.system.home", "C:\\databases\\sample");
>     
>     Class.forName(driver);
>     String url = "jdbc:derby:derbyBlob";
>     Connection con = DriverManager.getConnection(url);
>     return con;
>   }
>   private static void createDBandTables() throws Exception {
>     String driver="org.apache.derby.jdbc.EmbeddedDriver";
>     Properties p = System.getProperties();
>     p.put("derby.system.home", "C:\\databases\\sample");
>     
>     Class.forName(driver);
>     String url = "jdbc:derby:derbyBlob;create=true";
>     Connection con = DriverManager.getConnection(url);
>     Statement stmt = con.createStatement();
>     stmt.execute(TABLE1);
>     stmt.execute(TABLE2);
>     stmt.close();
>     con.close();
>   }
> }
> The Exception DOES NOT occur if call to readTable1() is not made.
> The Exception DOES NOT occur if the size of the Blob is reduced - for example 
> calling:
>       addRows(con, 1000, 1);
> This is a show stopper.
> Output from: java org.apache.derby.tools.sysinfo
> ------------------ Java Information ------------------
> Java Version:    1.5.0_05
> Java Vendor:     Sun Microsystems Inc.
> Java home:       C:\Program Files\Java\jre1.5.0_05
> Java classpath:  
> C:\tools\derby\db-derby-10.1.2.1-bin\lib\derby.jar;C:\tools\der
> by\db-derby-10.1.2.1-bin\lib\derbytools.jar;
> OS name:         Windows XP
> OS architecture: x86
> OS version:      5.1
> Java user name:  David
> Java user home:  C:\Documents and Settings\David
> Java user dir:   C:\david\novice\derby
> java.specification.name: Java Platform API Specification
> java.specification.version: 1.5
> --------- Derby Information --------
> JRE - JDBC: J2SE 5.0 - JDBC 3.0
> [C:\tools\derby\db-derby-10.1.2.1-bin\lib\derby.jar] 10.1.2.1 - (330608)
> [C:\tools\derby\db-derby-10.1.2.1-bin\lib\derbytools.jar] 10.1.2.1 - (330608)
> ------------------------------------------------------
> ----------------- Locale Information -----------------
> ------------------------------------------------------

-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
   http://issues.apache.org/jira/secure/Administrators.jspa
-
For more information on JIRA, see:
   http://www.atlassian.com/software/jira

Reply via email to