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/08/09 17:45:22 UTC

svn commit: r430077 - in /db/derby/code/trunk/java: drda/org/apache/derby/impl/drda/ testing/org/apache/derbyTesting/functionTests/master/ testing/org/apache/derbyTesting/functionTests/master/DerbyNet/jdk15/ testing/org/apache/derbyTesting/functionTest...

Author: bpendleton
Date: Wed Aug  9 08:45:21 2006
New Revision: 430077

URL: http://svn.apache.org/viewvc?rev=430077&view=rev
Log:
DERBY-1533: ArrayIndexOutOfBoundsException in DDMReader

DERBY-1533 follows in a series of bugs (DERBY-170, DERBY-428, DERBY-491,
DERBY-492, DERBY-614 among others) which have to do with the particulars of
the DRDA protocols used for handling long messages and large amounts of data.
In this particular issue, the problems involve the situation in which the
server is receiving a message which is segmented across multiple layer A
blocks using DSS continuations. This is a relatively unusual case, as
generally large amounts of data flow from the server to the client, not vice
versa.

This change contains two modifications to DDMReader, as well as some tests:

1) The Network Server method DDMReader.compressBLayerData was incorrect in
several respects. Rather than going through that code in detail, I noticed
that an almost identical version of this routine exists in the DerbyNetClient,
in the class Reply, and I am pretty confident that the client side code is
working, so I just wholesale modified the server's compressBLayerData method
so that it matched the client's method.

2) When the client is sending parameter data to the server, and the data to be
sent is a large array of bytes for a blob, the client, the client may send the
data inline as DRDA_TYPE_NVARBYTE, or it may externalize the data and send it
as DRDA_TYPE_NLOBBYTES. I believe that similar things happen with CLOB.
The client makes this decision based on the length of the data to be sent;
short values are sent inline, and long values are sent externalized. 
When the server receives the data, it must decide whether it is reading an
inline string of bytes, or externalized data, and it, too, makes this decision
based on the length of the data. However, the server's code for this was
comparing the length of the byte string against the amount of data left in the
current segment, as opposed to comparing it against the constant threshold
value for switching from inline bytes to externalized bytes; hence if a long
byte string happened to start late in a buffer, and was then segmented
(continued) into a subsequent buffer, the server would mistakenly think the
byte string was being sent as externalized bytes, when in fact it was sent as
inline bytes. 


Modified:
    db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DDMReader.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNet/jdk15/prepStmt.out
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNetClient/jdk15/prepStmt.out
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/prepStmt.out
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/derbynet/prepStmt.java

Modified: db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DDMReader.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DDMReader.java?rev=430077&r1=430076&r2=430077&view=diff
==============================================================================
--- db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DDMReader.java (original)
+++ db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DDMReader.java Wed Aug  9 08:45:21 2006
@@ -1321,7 +1321,7 @@
 	{
 		byte[] b;
 
-		if (length < dssLength)
+		if (length < DssConstants.MAX_DSS_LENGTH)
 		{
 			ensureBLayerDataInBuffer (length, ADJUST_LENGTHS);
 		    b = new byte[length];
@@ -1595,22 +1595,21 @@
 							   DRDAProtocolException.NO_CODPNT_ARG);
 			}
 
-			newdssLength += continueHeaderLength;
+			newdssLength += (continueHeaderLength-2);
 
 			// calculate the number of bytes to shift
-			if (i == (continueDssHeaderCount - 1))
+			if (i != (continueDssHeaderCount - 1))
 				bytesToShift = DssConstants.MAX_DSS_LENGTH;
 			else
 				bytesToShift = dssLength;
 
-			tempPos -= (shiftSize - 1);
-			System.arraycopy(buffer, tempPos, buffer, tempPos - bytesToShift +
-							 shiftSize , bytesToShift);
-			tempPos -= bytesToShift;
-			tempPos += (shiftSize + 1);
+			tempPos -= (bytesToShift - 2);
+			System.arraycopy(buffer, tempPos - shiftSize, buffer, tempPos,
+							 bytesToShift);
 		}
 		// reposition the start of the data after the final DSS shift.
 		pos = tempPos;
+		dssLength += newdssLength;
 	}
 
 	/**

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNet/jdk15/prepStmt.out
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNet/jdk15/prepStmt.out?rev=430077&r1=430076&r2=430077&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNet/jdk15/prepStmt.out (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNet/jdk15/prepStmt.out Wed Aug  9 08:45:21 2006
@@ -85,4 +85,6 @@
 Fetched a row, c2.length=12750
 JIRA-1454 repro with c2 len=12749
 Fetched a row, c2.length=12749
+JIRA Test 1533(a) successful (no exception)
+JIRA Test 1533(b) successful (no exception)
 prepStmt Test Ends

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNetClient/jdk15/prepStmt.out
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNetClient/jdk15/prepStmt.out?rev=430077&r1=430076&r2=430077&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNetClient/jdk15/prepStmt.out (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNetClient/jdk15/prepStmt.out Wed Aug  9 08:45:21 2006
@@ -85,4 +85,6 @@
 Fetched a row, c2.length=12750
 JIRA-1454 repro with c2 len=12749
 Fetched a row, c2.length=12749
+JIRA Test 1533(a) successful (no exception)
+JIRA Test 1533(b) successful (no exception)
 prepStmt Test Ends

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/prepStmt.out
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/prepStmt.out?rev=430077&r1=430076&r2=430077&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/prepStmt.out (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/prepStmt.out Wed Aug  9 08:45:21 2006
@@ -85,4 +85,6 @@
 Fetched a row, c2.length=12750
 JIRA-1454 repro with c2 len=12749
 Fetched a row, c2.length=12749
+JIRA Test 1533(a) successful (no exception)
+JIRA Test 1533(b) successful (no exception)
 prepStmt Test Ends

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/derbynet/prepStmt.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/derbynet/prepStmt.java?rev=430077&r1=430076&r2=430077&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 Aug  9 08:45:21 2006
@@ -49,7 +49,8 @@
         {"table t1", "table tab1", "table t2", "table bigtab", "table tstab",
          "table doubletab", "table numtab", "table Numeric_Tab", "table jira614", 
 	 "table jira614_a", "table jira428", "table jira125", 
-         "table jira125125125125125125125125125125125125125125125125125125125125125125125125125125125125125125125"};
+         "table jira125125125125125125125125125125125125125125125125125125125125125125125125125125125125125125125",
+         "table jira1533_a", "table jira1533_b"};
 
 	public static void main (String args[])
 	{
@@ -317,6 +318,8 @@
 			jira125Test(conn);
 			jira428Test(conn);
 			jira1454Test(conn);
+                        jira1533Test_a(conn);
+                        jira1533Test_b(conn);
 			conn.close();
 			// refresh conn before cleaning up
 			conn = ij.startJBMS();
@@ -1003,6 +1006,107 @@
         while (rs.next());
         System.out.println("Iteration 1 successful: " + (nCols + 1) +
 			" parameter markers successfully prepared and executed.");
+    }
+    // Jira 1533 involves two different bugs regarding the handling of large
+    // amounts of parameter data: first, the Network Server was incorrectly
+    // handling the desegmentation of continued DSS segements, and second,
+    // the Network Server was using the wrong heuristic to determine whether
+    // long string data was being flowed in-line or externalized.
+    //
+    // Tests "a" and "b" provoke two different forms of this problem, one
+    // with just a single continued segement, and one with several continuations
+    private static void jira1533Test_a(Connection conn)
+        throws Exception
+    {
+        Statement stmt = conn.createStatement();
+        PreparedStatement ps ;
+        try { stmt.execute("drop table jira1533_a"); } catch (Throwable t) { }
+        stmt.execute("create table jira1533_a (aa BIGINT NOT NULL, "+
+                "bbbbbb BIGINT DEFAULT 0 NOT NULL,"+
+                " cccc  VARCHAR(40), ddddddddddd BIGINT, eeeeee VARCHAR(128),"+
+                " ffffffffffffffffff VARCHAR(128),"+
+                "ggggggggg  BLOB(2G), hhhhhhhhh VARCHAR(128), "+
+                "iiiiiiii VARCHAR(128), jjjjjjjjjjjjjj BIGINT,"+
+                "kkkkkkkk CHAR(1) DEFAULT 'f', "+
+                "llllllll CHAR(1) DEFAULT 'f', "+
+                "mmmmmmmmmmmmm  CHAR(1) DEFAULT 'f')");
+        stmt.close();
+        ps = conn.prepareStatement(
+                "INSERT INTO jira1533_a (aa,bbbbbb,cccc,ddddddddddd,eeeeee,"+
+                " ffffffffffffffffff,"+
+                "ggggggggg,hhhhhhhhh,iiiiiiii,jjjjjjjjjjjjjj,kkkkkkkk,"+
+                "llllllll,mmmmmmmmmmmmm)"+
+                " VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)");
+        String blobStr = makeString(32584);
+        ps.setLong(1,5);
+        ps.setLong(2,1);
+        ps.setString(3,"AAAAAAAAAAA");
+        ps.setLong(4,30000);
+        ps.setString(5,"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+        ps.setString(6,"AAAAAAAAAAA");
+        ps.setBytes(7,blobStr.getBytes());
+        ps.setString(8,"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+        ps.setString(9,"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+        ps.setLong(10,1);
+        ps.setString(11,"1");
+        ps.setString(12,"1");
+        ps.setString(13,"1");
+        ps.execute();
+        ps.close();
+        System.out.println("JIRA Test 1533(a) successful (no exception)");
+    }
+    private static void jira1533Test_b(Connection conn)
+        throws Exception
+    {
+        Statement stmt = conn.createStatement();
+        PreparedStatement ps ;
+        try { stmt.execute("drop table jira1533_b"); } catch (Throwable t) { }
+        stmt.execute("create table jira1533_b (aa BIGINT NOT NULL, "+
+                "bbbbbb BIGINT DEFAULT 0 NOT NULL,"+
+                " cccc  VARCHAR(40), ddddddddddd BIGINT, eeeeee VARCHAR(128),"+
+                " ffffffffffffffffff VARCHAR(128),"+
+                "g1 BLOB(2G),g2 BLOB(2G),g3 BLOB(2G),g4 BLOB(2G),"+
+                "ggggggggg  BLOB(2G), hhhhhhhhh VARCHAR(128), "+
+                "iiiiiiii VARCHAR(128), jjjjjjjjjjjjjj BIGINT,"+
+                "kkkkkkkk CHAR(1) DEFAULT 'f', "+
+                "llllllll CHAR(1) DEFAULT 'f', "+
+                "mmmmmmmmmmmmm  CHAR(1) DEFAULT 'f')");
+        stmt.close();
+        ps = conn.prepareStatement(
+                "INSERT INTO jira1533_b (aa,bbbbbb,cccc,ddddddddddd,eeeeee,"+
+                " ffffffffffffffffff,"+
+                "g1,g2,g3,g4,"+
+                "ggggggggg,hhhhhhhhh,iiiiiiii,jjjjjjjjjjjjjj,kkkkkkkk,"+
+                "llllllll,mmmmmmmmmmmmm)"+
+                " VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
+        String blobStr = makeString(32584);
+        ps.setLong(1,5);
+        ps.setLong(2,1);
+        ps.setString(3,"AAAAAAAAAAA");
+        ps.setLong(4,30000);
+        ps.setString(5,"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+        ps.setString(6,"AAAAAAAAAAA");
+        ps.setBytes(7,blobStr.getBytes());
+        ps.setBytes(8,blobStr.getBytes());
+        ps.setBytes(9,blobStr.getBytes());
+        ps.setBytes(10,blobStr.getBytes());
+        ps.setBytes(11,blobStr.getBytes());
+        ps.setString(12,"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+        ps.setString(13,"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+        ps.setLong(14,1);
+        ps.setString(15,"1");
+        ps.setString(16,"1");
+        ps.setString(17,"1");
+        ps.execute();
+        ps.close();
+        System.out.println("JIRA Test 1533(b) successful (no exception)");
+    }
+    private static String makeString(int length)
+    {
+        StringBuffer buf = new StringBuffer();
+        for (int i = 0; i < length; i++)
+            buf.append("X");
+        return buf.toString();
     }
     // Jira 428 involves large batch sizes for Statement.addBatch and
     // Statement.executeBatch. Currently, there is a hard DRDA limit of