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 km...@apache.org on 2006/01/23 18:34:37 UTC

svn commit: r371603 - 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: kmarsden
Date: Mon Jan 23 09:34:23 2006
New Revision: 371603

URL: http://svn.apache.org/viewcvs?rev=371603&view=rev
Log:
DERBY-125 Network Server can send DSS greater than 32K to client, which breaks DRDA protocol.

Fixes off by one error during segment shifting.
Fixes continuation flag handling to not overwrite the length.

See changes.html attached to DERBY-125 for more info

Contributed by Bryan Pendleton


Modified:
    db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DDMWriter.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/DDMWriter.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DDMWriter.java?rev=371603&r1=371602&r2=371603&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 Mon Jan 23 09:34:23 2006
@@ -1433,6 +1433,54 @@
 			ensureLength (shiftSize);
 			offset += shiftSize;
 
+			// Notes on the behavior of the Layer B segmenting loop below:
+			//
+			// We start with the right most chunk. For a 3-segment object we'd
+			// shift 2 segments: shift the first (rightmost) one 4 bytes and 
+			// the second one 2. Note that by 'first' we mean 'first time
+			// through the loop', but that is actually the last segment
+			// of data since we are moving right-to-left. For an object
+			// of K segments we will pass through this loop K-1 times.
+			// The 0th (leftmost) segment is not shifted, as it is
+			// already in the right place. When we are done, we will
+			// have made room in each segment for an additional
+			// 2 bytes for the continuation header. Thus, each
+			// segment K is shifted K*2 bytes to the right.
+			//
+			// Each time through the loop, "dataByte" points to the
+			// last byte in the segment; "dataToShift" is the amount of
+			// data that we need to shift, and "shiftSize" is the
+			// distance that we need to shift it. Since dataByte points
+			// at the last byte, not one byte beyond it (as with the
+			// "offset" variable used elsewhere in DDMWriter), the start
+			// of the segement is actually at (dataByte-dataToShift+1).
+			//
+			// After we have shifted the segment, we move back to the
+			// start of the segment and set the value of the 2-byte DSS
+			// continuation header, which needs to hold the length of
+			// this segment's data, together with the continuation flag
+			// if this is not the rightmost (passOne) segment.
+			//
+			// In general, each segment except the rightmost will contain
+			// 32765 bytes of data, plus the 2-byte header, and its
+			// continuation flag will be set, so the header value will
+			// be 0xFFFF. The rightmost segment will not have the
+			// continuation flag set, so its value may be anything from
+			// 0x0001 to 0x7FFF, depending on the amount of data in that
+			// segment.
+			//
+			// Note that the 0th (leftmost) segment also has a 2-byte
+			// DSS header, which needs to have its continuation flag set.
+			// This is done by resetting the "totalSize" variable below,
+			// at which point that variable no longer holds the total size
+			// of the object, but rather just the length of segment 0. The
+			// total size of the object was written using extended length
+			// bytes by the endDdm() method earlier.
+			//
+			// Additional information about this routine is available in the
+			// bug notes for DERBY-125:
+			// http://issues.apache.org/jira/browse/DERBY-125
+			
 			// mark passOne to help with calculating the length of the final (first or
 			// rightmost) continuation header.
 			boolean passOne = true;
@@ -1441,12 +1489,7 @@
 				int dataToShift = bytesRequiringContDssHeader % 32765;
 				if (dataToShift == 0)
 					dataToShift = 32765;
-				// We start with the right most chunk. If we had to copy two
-				// chunks we would shift the first one 4 bytes and then 
-				// the second one
-				// 2 when we come back on the next loop so they would each have
-				// 2 bytes for the continuation header
-				int startOfCopyData = dataByte - dataToShift;
+				int startOfCopyData = dataByte - dataToShift + 1;
 				System.arraycopy(bytes,startOfCopyData, bytes, 
 								 startOfCopyData + shiftSize, dataToShift);
 				dataByte -= dataToShift;
@@ -1462,7 +1505,9 @@
 				else
 				{
 					if (twoByteContDssHeader == DssConstants.MAX_DSS_LENGTH)
-				    	twoByteContDssHeader = DssConstants.CONTINUATION_BIT;
+					twoByteContDssHeader = (twoByteContDssHeader |
+						DssConstants.CONTINUATION_BIT);
+
 				}
 
 				// insert the header's length bytes
@@ -1481,7 +1526,9 @@
 			while (bytesRequiringContDssHeader > 0);
 
 			// set the continuation dss header flag on for the first header
-			totalSize = DssConstants.CONTINUATION_BIT;
+			totalSize = (DssConstants.MAX_DSS_LENGTH |
+					DssConstants.CONTINUATION_BIT);
+
 
 		}
 

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNet/jdk15/prepStmt.out
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNet/jdk15/prepStmt.out?rev=371603&r1=371602&r2=371603&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 Mon Jan 23 09:34:23 2006
@@ -77,4 +77,6 @@
 SQLState: 22007 message: The syntax of the string representation of a datetime value is incorrect.
 Test jira614 completed successfully -- no Distributed Protocol Exception occurred
 Jira170: caught expected table not found
+Iteration 1 successful: 555 parameter markers successfully prepared and executed.
+Test jira125 successful: 557 parameter markers successfully prepared and executed.
 prepStmt Test Ends

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNetClient/jdk15/prepStmt.out
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNetClient/jdk15/prepStmt.out?rev=371603&r1=371602&r2=371603&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 Mon Jan 23 09:34:23 2006
@@ -77,4 +77,6 @@
 SQLState: 22007 message: The syntax of the string representation of a datetime value is incorrect.
 Test jira614 completed successfully -- no Distributed Protocol Exception occurred
 Jira170: caught expected table not found
+Iteration 1 successful: 555 parameter markers successfully prepared and executed.
+Test jira125 successful: 557 parameter markers successfully prepared and executed.
 prepStmt Test Ends

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/prepStmt.out
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/prepStmt.out?rev=371603&r1=371602&r2=371603&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 Mon Jan 23 09:34:23 2006
@@ -77,4 +77,6 @@
 SQLState: 22007 message: The syntax of the string representation of a datetime value is incorrect.
 Test jira614 completed successfully -- no Distributed Protocol Exception occurred
 Jira170: caught expected table not found
+Iteration 1 successful: 555 parameter markers successfully prepared and executed.
+Test jira125 successful: 557 parameter markers successfully prepared and executed.
 prepStmt Test Ends

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=371603&r1=371602&r2=371603&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 Mon Jan 23 09:34:23 2006
@@ -310,6 +310,7 @@
 			test5172(conn);
 			jira614Test(conn);
 			jira170Test(conn);
+			jira125Test(conn);
 			conn.close();
 			// refresh conn before cleaning up
 			conn = ij.startJBMS();
@@ -787,5 +788,121 @@
                 e.printStackTrace();
         }
     }
-}
+	/**
+	 * Jira-125 has to do with proper use of continuation headers 
+	 * for very large reply messages, such as the SQLDARD which is
+	 * returned for a prepared statement with an enormous number of
+	 * parameter markers. This test generates a multi-segment SQLDARD
+	 * response message from the server, to verify that the code in
+	 * DDMWriter.finalizeDSSLength is executed.
+	 *
+	 * Repro for DERBY-125 off-by-one error.  This repro runs in
+	 * two iterations.  The first iteration, we use a table name
+	 * and a column name that are extra long, so that the server-
+	 * side buffer has more data in it.  The second iteration, we
+	 * use simpler names for the table and column, which take up
+	 * less space in the server buffer.  Then, since the server-
+	 * side bytes array was previously used for a larger amount of
+	 * data, then the unused bytes contain old data.  Since we
+	 * intentionally put the "larger amount of data" into the buffer
+	 * during the first iteration, we know what the old data bytes
+	 * are going to be.  Thus, by using specific lengths for the 
+	 * table and column names, we can 'shift' the old data until we
+	 * reach a point where the off-by-one error manifests itself:
+	 * namely, we end up incorrectly leaving a non-zero data byte
+	 * in the last position of the current server buffer, which
+	 * is wrong.
+	 */
+
+    private static void jira125Test(Connection conn)
+        throws Exception
+    {
+		jira125Test_a(conn);
+		jira125Test_b(conn);
+    }
 
+    private static void jira125Test_b(Connection conn)
+	    throws Exception
+    {
+	    Statement stmt = conn.createStatement();
+        PreparedStatement ps ;
+	    try {
+		    stmt.execute("drop table jira125");
+	    } catch (Throwable t) { }
+		try {
+	        stmt.execute("create table jira125 (id integer)");
+			stmt.execute("insert into jira125 values 1, 2, 3");
+		} catch (Throwable t) { }
+        StringBuffer buf = new StringBuffer();
+        buf.append("SELECT id FROM jira125 WHERE id IN ( ");
+
+		// Must have at least 551 columns here, in order to force
+		// server buffer beyond 32k.  NOTE: Changing this number
+		// could cause the test to "pass" even if a regression
+		// occurs--so only change it if needed!
+        int nCols = 556;
+        for (int i = 0; i < nCols; i++) buf.append("?,");
+        buf.append("?)");
+        ps = conn.prepareStatement(buf.toString());
+        // Note that we actually have nCols+1 parameter markers
+        for (int i = 0; i <= nCols; i++) ps.setInt(i+1, 1);
+        ResultSet rs = ps.executeQuery();
+        while (rs.next());
+        System.out.println("Test jira125 successful: " + (nCols + 1) +
+			" parameter markers successfully prepared and executed.");
+    }
+
+    private static void jira125Test_a(Connection conn)
+	    throws Exception
+    {
+	    Statement stmt = conn.createStatement();
+
+		// Build a column name that is 99 characters long;
+		// the length of the column name and the length of
+		// the table name are important to the repro--so
+		// do not change these unless you can confirm that
+		// the new values will behave in the same way.
+		StringBuffer id = new StringBuffer();
+		for (int i = 0; i < 49; i++)
+			id.append("id");
+		id.append("i");
+
+		// Build a table name that is 97 characters long;
+		// the length of the column name and the length of
+		// the table name are important to the repro--so
+		// do not change these unless you can confirm that
+		// the new values will behave in the same way.
+		StringBuffer tabName = new StringBuffer("jira");
+		for (int i = 0; i < 31; i++)
+			tabName.append("125");
+
+	    try {
+		    stmt.execute("drop table " + tabName.toString());
+	    } catch (Throwable t) { }
+		try {
+	        stmt.execute("create table " + tabName.toString() + " (" +
+				id.toString() + " integer)");
+			stmt.execute("insert into " + tabName.toString() + " values 1, 2, 3");
+		} catch (Throwable t) { }
+
+        PreparedStatement ps;
+        StringBuffer buf = new StringBuffer();
+        buf.append("SELECT " + id.toString() + " FROM " +
+			tabName.toString() + " WHERE " + id.toString() + " IN ( ");
+
+		// Must have at least 551 columns here, in order to force
+		// server buffer beyond 32k.  NOTE: Changing this number
+		// could cause the test to "pass" even if a regression
+		// occurs--so only change it if needed!
+        int nCols = 554;
+        for (int i = 0; i < nCols; i++) buf.append("?,");
+        buf.append("?)");
+        ps = conn.prepareStatement(buf.toString());
+        // Note that we actually have nCols+1 parameter markers
+        for (int i = 0; i <= nCols; i++) ps.setInt(i+1, 1);
+        ResultSet rs = ps.executeQuery();
+        while (rs.next());
+        System.out.println("Iteration 1 successful: " + (nCols + 1) +
+			" parameter markers successfully prepared and executed.");
+    }
+}