You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by bp...@apache.org on 2006/03/22 18:23:12 UTC

svn commit: r387895 - in /db/derby/code/trunk/java: client/org/apache/derby/client/am/PreparedStatement.java drda/org/apache/derby/impl/drda/DDMWriter.java testing/org/apache/derbyTesting/functionTests/tests/derbynet/prepStmt.java

Author: bpendleton
Date: Wed Mar 22 09:23:09 2006
New Revision: 387895

URL: http://svn.apache.org/viewcvs?rev=387895&view=rev
Log:
DERBY-428: PreparedStatement.executeBatch hangs if batch is too large

This patch contains a server-side change, a client-side change, and a test.

The server-side change is to call ensureLength() in DDMWriter.startDDM().
The DDMWriter working buffer is designed to dynamically grow to accomodate
the data being written; this dynamic growth is implemented using a coding
rule which requires that all DDMWriter internal routines must call
ensureLength to communicate the buffer size requirements prior to writing
bytes into the buffer. StartDDM was missing the call to ensureLength.

The client-side change is due to the fact that DRDA imposes a hard limit
of 65535 elements in a single correlated request because the correlation
identifier is a two byte unsigned integer. Prior to this change, the
correlation identifiers would wrap around when we added the 65536th
element into the batch, which breaks the DRDA protocol rule that requires
correlation IDs in a single request be always increasing. This change
causes the client to throw an exception if it is asked to execute a batch
containing more than 65534 elements. The reason for the number 65534,
rather than 65535, is that the value 0xFFFF is reserved for special use.


Modified:
    db/derby/code/trunk/java/client/org/apache/derby/client/am/PreparedStatement.java
    db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DDMWriter.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/derbynet/prepStmt.java

Modified: db/derby/code/trunk/java/client/org/apache/derby/client/am/PreparedStatement.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/client/org/apache/derby/client/am/PreparedStatement.java?rev=387895&r1=387894&r2=387895&view=diff
==============================================================================
--- db/derby/code/trunk/java/client/org/apache/derby/client/am/PreparedStatement.java (original)
+++ db/derby/code/trunk/java/client/org/apache/derby/client/am/PreparedStatement.java Wed Mar 22 09:23:09 2006
@@ -1750,6 +1750,12 @@
         if (batchSize == 0) {
             return updateCounts;
         }
+		// The network client has a hard limit of 65,534 commands in a single
+		// DRDA request. This is because DRDA uses a 2-byte correlation ID,
+		// and the values 0 and 0xffff are reserved as special values. So
+		// that imposes an upper limit on the batch size we can support:
+		if (batchSize > 65534)
+            throw new BatchUpdateException(agent_.logWriter_, "No more than 65534 commands may be added to a single batch", updateCounts);
 
         // Initialize all the updateCounts to indicate failure
         // This is done to account for "chain-breaking" errors where we cannot

Modified: db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DDMWriter.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DDMWriter.java?rev=387895&r1=387894&r2=387895&view=diff
==============================================================================
--- db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DDMWriter.java (original)
+++ db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DDMWriter.java Wed Mar 22 09:23:09 2006
@@ -330,6 +330,7 @@
 		// save the location of the beginning of the collection so
 		// that we can come back and fill in the length bytes
 		markStack[top++] = offset;
+		ensureLength (4); // verify space for length bytes and code point
 		offset += 2; // move past the length bytes before writing the code point
 		bytes[offset] = (byte) ((codePoint >>> 8) & 0xff);
 		bytes[offset + 1] = (byte) (codePoint & 0xff);

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/derbynet/prepStmt.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/derbynet/prepStmt.java?rev=387895&r1=387894&r2=387895&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/derbynet/prepStmt.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/derbynet/prepStmt.java Wed Mar 22 09:23:09 2006
@@ -30,6 +30,7 @@
 import java.sql.Time;
 import java.sql.Timestamp;
 import java.sql.SQLException;
+import java.sql.BatchUpdateException;
 import java.io.ByteArrayInputStream; 
 import java.io.InputStreamReader;
 import org.apache.derbyTesting.functionTests.util.TestUtil;
@@ -47,7 +48,7 @@
     private static String[] testObjects =  // string array for cleaning up
         {"table t1", "table tab1", "table t2", "table bigtab", "table tstab",
          "table doubletab", "table numtab", "table Numeric_Tab", "table jira614", 
-	 "table jira614_a", "table jira125", 
+	 "table jira614_a", "table jira428", "table jira125", 
          "table jira125125125125125125125125125125125125125125125125125125125125125125125125125125125125125125125"};
 
 	public static void main (String args[])
@@ -314,6 +315,7 @@
 			jira614Test_a(conn);
 			jira170Test(conn);
 			jira125Test(conn);
+			jira428Test(conn);
 			conn.close();
 			// refresh conn before cleaning up
 			conn = ij.startJBMS();
@@ -947,5 +949,58 @@
         while (rs.next());
         System.out.println("Iteration 1 successful: " + (nCols + 1) +
 			" parameter markers successfully prepared and executed.");
+    }
+    // Jira 428 involves large batch sizes for Statement.addBatch and
+    // Statement.executeBatch. Currently, there is a hard DRDA limit of
+    // 65535 statements per batch (prior to DERBY-428, the server failed
+    // at around 9000 statements). The different JDBC clients support slightly
+    // lower limits: the Network Client supports 65534
+    // statements in a single batch, while the DB2JCC driver supports
+    // 65532 statements. This test just verifies that a batch
+    // of 65532 statements works, and that a batch of 100000 statements
+    // gets a BatchUpdateException from the Network Client.
+    private static void jira428Test(Connection conn)
+        throws Exception
+    {
+        Statement stmt = conn.createStatement();
+        PreparedStatement ps ;
+        try { stmt.execute("drop table jira428"); } catch (Throwable t) { }
+        stmt.execute("create table jira428 (i integer)");
+        boolean savedAutoCommit = conn.getAutoCommit();
+        conn.setAutoCommit(false);
+        ps = conn.prepareStatement("insert into jira428 values (?)");
+        for (int i = 0; i < 65532; i++)
+        {
+            ps.setInt(1, i);
+            ps.addBatch();
+        }
+        ps.executeBatch();
+        conn.commit();
+        // We don't run this part of the test for the JCC client because
+        // the exception forces the connection closed. For DerbyNetClient, it's
+        // a clean exception that we can catch and recover from, so we test
+        // that code path:
+        if (TestUtil.isDerbyNetClientFramework())
+        {
+            ps = conn.prepareStatement("insert into jira428 values (?)");
+            for (int i = 0; i < 100000; i++)
+            {
+                ps.setInt(1, i);
+                ps.addBatch();
+            }
+            try {
+                ps.executeBatch();
+                System.out.println("JIRA428 FAILURE: expected an exception saying no more than 65534 statements in a single batch");
+            }
+            catch (BatchUpdateException bue)
+            {
+                // We don't print anything here because we use the same
+                // master files for DerbyNet and DerbyNetClient, and we only
+                // run this portion of the test for DerbyNetClient.
+                // The exception that we get says "no more than 65534 stmts".
+            }
+            conn.commit();
+        }
+        conn.setAutoCommit(savedAutoCommit);
     }
 }