Index: java/engine/org/apache/derby/impl/sql/execute/DMLWriteResultSet.java =================================================================== --- java/engine/org/apache/derby/impl/sql/execute/DMLWriteResultSet.java (revision 598683) +++ java/engine/org/apache/derby/impl/sql/execute/DMLWriteResultSet.java (working copy) @@ -21,6 +21,8 @@ package org.apache.derby.impl.sql.execute; +import java.io.InputStream; + import org.apache.derby.iapi.types.DataValueDescriptor; import org.apache.derby.iapi.sql.execute.NoPutResultSet; import org.apache.derby.iapi.services.io.StreamStorable; @@ -148,10 +150,29 @@ heapIx : baseRowReadMap[heapIx]; + DataValueDescriptor col = row.getColumn(readIx+1); + InputStream stream = ((StreamStorable)col).returnStream(); ((StreamStorable)col).loadStream(); + // DERBY-3238 + // fix up any duplicate streams, for instance in the case of an update with a trigger, + // all the columns are read as update columns even if they are not updated, so + // the update column will still have a reference to the original stream. + // If we knew from this context that this was an update and we knew the number + // of columns in the base table we would be able to calculate exactly the offset to + // check, but we don't have that information from this context. + // If DERBY-1482 is fixed, perhaps this code can be removed. + + if (stream != null) + for (int i = 1; i <= row.nColumns(); i++) + { + DataValueDescriptor c = row.getColumn(i); + if (c instanceof StreamStorable) + if (((StreamStorable)c).returnStream() == stream) + row.setColumn(i, col.getClone()); + } + } } - } } /** Index: java/testing/org/apache/derbyTesting/functionTests/tests/lang/TriggerTest.java =================================================================== --- java/testing/org/apache/derbyTesting/functionTests/tests/lang/TriggerTest.java (revision 598683) +++ java/testing/org/apache/derbyTesting/functionTests/tests/lang/TriggerTest.java (working copy) @@ -20,6 +20,7 @@ */ package org.apache.derbyTesting.functionTests.tests.lang; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.Reader; @@ -398,6 +399,124 @@ ((List) TRIGGER_INFO.get()).add(info); } + /** + * Test for DERBY-3238 trigger fails with IOException if triggering table has large lob. + * + * @throws SQLException + * @throws IOException + */ + public void testClobInTriggerTable() throws SQLException, IOException + { + String trig = " create trigger t_lob1 after update of str1 on lob1 "; + trig = trig + " REFERENCING OLD AS old NEW AS new FOR EACH ROW MODE DB2SQL "; + trig = trig + " insert into t_lob1_log(oldvalue, newvalue) values (old.str1, new.str1)"; + Connection conn = getConnection(); + + Statement s = conn.createStatement(); + + s.executeUpdate("create table LOB1 (str1 Varchar(80), b_lob BLOB(50M))"); + s.executeUpdate("create table t_lob1_log(oldvalue varchar(80), newvalue varchar(80), chng_time timestamp default current_timestamp)"); + s.executeUpdate(trig); + conn.commit(); + testClobSize(conn, s, 1024); + testClobSize(conn, s, 16384); + testClobSize(conn, s, 32658); + + testClobSize(conn, s, 32659); + + testClobSize(conn, s, 1024 *32 -1); + testClobSize(conn, s, 1024 *32); + testClobSize(conn, s, 1024 *32+1); + testClobSize(conn, s, 1024 *64 -1); + testClobSize(conn, s, 1024 *64); + testClobSize(conn, s, 1024 *64+1); + s.executeUpdate("drop table lob1"); + s.executeUpdate("drop table t_lob1_log"); + } + + private static void testClobSize(Connection conn, Statement s, int blobSize) throws SQLException, IOException { + // --- add a blob + PreparedStatement ps = conn.prepareStatement("INSERT INTO LOB1 VALUES (?, ?)"); + + ps.setString(1, blobSize +""); + + + byte[] arr = new byte[blobSize]; + for (int i = 0; i < arr.length; i++) + arr[i] = (byte)8; + + // - set the value of the input parameter to the input stream + ps.setBinaryStream(2, new ByteArrayInputStream(arr) , blobSize); + ps.execute(); + conn.commit(); + + // Now executing update to fire trigger + s.executeUpdate("update LOB1 set str1 = str1 || ' '"); + s.executeUpdate("DELETE FROM LOB1"); + + } + + + /** + * Test for DERBY-3238 trigger fails with IOException if triggering table has large lob. + * + * @throws SQLException + * @throws IOException + */ + public void testBlobInTriggerTable() throws SQLException, IOException + { + String trig = " create trigger t_lob1 after update of str1 on lob1 "; + trig = trig + " REFERENCING OLD AS old NEW AS new FOR EACH ROW MODE DB2SQL "; + trig = trig + " insert into t_lob1_log(oldvalue, newvalue) values (old.str1, new.str1)"; + Connection conn = getConnection(); + + Statement s = conn.createStatement(); + + s.executeUpdate("create table LOB1 (str1 Varchar(80), b_lob BLOB(50M))"); + s.executeUpdate("create table t_lob1_log(oldvalue varchar(80), newvalue varchar(80), chng_time timestamp default current_timestamp)"); + s.executeUpdate(trig); + conn.commit(); + testBlobSize(conn, s, 1024); + testBlobSize(conn, s, 16384); + testBlobSize(conn, s, 32658); + + testBlobSize(conn, s, 32659); + + testBlobSize(conn, s, 1024 *32 -1); + testBlobSize(conn, s, 1024 *32); + testBlobSize(conn, s, 1024 *32+1); + testBlobSize(conn, s, 1024 *64 -1); + testBlobSize(conn, s, 1024 *64); + testBlobSize(conn, s, 1024 *64+1); + testBlobSize(conn, s, 1024 *1024* 15); + s.executeUpdate("drop table lob1"); + s.executeUpdate("drop table t_lob1_log"); + } + + private static void testBlobSize(Connection conn, Statement s, int blobSize) throws SQLException, IOException { + // --- add a blob + PreparedStatement ps = conn.prepareStatement("INSERT INTO LOB1 VALUES (?, ?)"); + + ps.setString(1, blobSize +""); + + + byte[] arr = new byte[blobSize]; + for (int i = 0; i < arr.length; i++) + arr[i] = (byte)8; + + // - set the value of the input parameter to the input stream + ps.setBinaryStream(2, new ByteArrayInputStream(arr) , blobSize); + ps.execute(); + conn.commit(); + + // Now executing update to fire trigger + s.executeUpdate("update LOB1 set str1 = str1 || ' '"); + s.executeUpdate("DELETE FROM LOB1"); + + } + + + /** * Test that the action statement of a trigger * can work with all datatypes.