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 be...@apache.org on 2006/01/27 16:12:16 UTC

svn commit: r372871 - 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/ testing/org/apache/derbyTesting/functionTests/mast...

Author: bernt
Date: Fri Jan 27 07:11:55 2006
New Revision: 372871

URL: http://svn.apache.org/viewcvs?rev=372871&view=rev
Log:
DERBY-491 Protocol exception when Network Server
 tries to return ~32K of data or greater in a result set for a Java stored
 procedure.
Submitted by Bryan Pendleton

Modified:
    db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DDMWriter.java
    db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNet/procedure.out
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNetClient/procedure.out
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/procedure.out
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/procedure.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/ProcedureTest.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=372871&r1=372870&r2=372871&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 Fri Jan 27 07:11:55 2006
@@ -289,10 +289,29 @@
 	 * Copy Data to End
 	 * Create a buffer and copy from the position given to the end of data
 	 *
+	 * Note that the position given is treated as relative to the
+	 * current DSS, for there may be other DSS blocks (chained, presumably)
+	 * which are sitting unwritten in the buffer. The caller doesn't
+	 * know this, though, and works only with the current DSS.
+	 *
+	 * getDSSLength, copyDSSDataToEnd, and truncateDSS work together to
+	 * provide a sub-protocol for DRDAConnThread to use in its
+	 * implementation of the LMTBLKPRC protocol. They enable the caller
+	 * to determine when it has written too much data into the current
+	 * DSS, to reclaim the extra data that won't fit, and to truncate
+	 * that extra data once it has been reclaimed and stored elsewhere.
+	 * Note that this support only works for the current DSS. Earlier,
+	 * chained DSS blocks cannot be accessed using these methods. For
+	 * additional background information, the interested reader should
+	 * investigate bugs DERBY-491 and 492 at:
+	 * http://issues.apache.org/jira/browse/DERBY-491 and
+	 * http://issues.apache.org/jira/browse/DERBY-492
+	 *
 	 * @param start
 	 */
-	protected byte [] copyDataToEnd(int start)
+	protected byte [] copyDSSDataToEnd(int start)
 	{
+		start = start + dssLengthLocation;
 		int length = offset - start;
 		byte [] temp = new byte[length];
 		System.arraycopy(bytes,start,temp,0,length);
@@ -399,26 +418,31 @@
 
 	}
 
-	/**
-	 * Get offset
-	 *
-	 * @return offset into the buffer
-	 */
-	protected int getOffset()
-	{
-		return offset;
-	}
-
-	/**
-	 * Set offset
-	 *
-	 * @param value new offset value
-	 */
-	protected void setOffset(int value)
-	{
-		offset = value;
-	}
-
+    /**
+     * Get the length of the current DSS block we're working on. This is
+     * used by the LMTBLKPRC protocol, which does its own conversational
+     * blocking protocol above the layer of the DRDA blocking. The LMTBLKPRC
+     * implementation (in DRDAConnThread) needs to be able to truncate a
+     * DSS block when splitting a QRYDTA response.
+     *
+     * @return current DSS block length
+    */
+    protected int getDSSLength()
+    {
+        return offset - dssLengthLocation;
+    }
+ 
+    /**
+     * Truncate the current DSS. Before making this call, you should ensure
+     * that you have copied the data to be truncated somewhere else, by
+     * calling copyDSSDataToEnd
+     *
+     * @param desired DSS length
+    */
+    protected void truncateDSS(int value)
+    {
+        offset = dssLengthLocation + value;
+    }
 
 
 	// Write routines
@@ -1898,7 +1922,7 @@
 	{
 
 		lastDSSBeforeMark = prevHdrLocation;
-		return getOffset();
+		return offset;
 
 	}
 
@@ -1917,7 +1941,7 @@
 	{
 
 		// Logical clear.
-		setOffset(mark);
+		offset = mark;
 
 		// Because we've just cleared out the most recently-
 		// written DSSes, we have to make sure the next thing

Modified: db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java?rev=372871&r1=372870&r2=372871&view=diff
==============================================================================
--- db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java (original)
+++ db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java Fri Jan 27 07:11:55 2006
@@ -5738,7 +5738,7 @@
 
 		// check for remaining space in current query block
 		// Need to mod with blksize so remaining doesn't go negative. 4868
-		int remaining = blksize - (writer.getOffset()  % blksize) - (3 + 
+		int remaining = blksize - (writer.getDSSLength()  % blksize) - (3 + 
 				FdocaConstants.SQLCADTA_SQLDTARD_RLO_SIZE);
 
 
@@ -5985,7 +5985,7 @@
 	{
 		boolean getMoreData = true;
 		boolean sentExtData = false;
-		int startOffset = writer.getOffset();
+		int startLength = 0;
 		writer.createDssObject();
 
 		if (SanityManager.DEBUG) 
@@ -6026,12 +6026,12 @@
 			// It would get split up but it is not very efficient.
 			if (getMoreData == true)
 			{
-				int endOffset = writer.getOffset();
-				int rowsize = endOffset- startOffset;
-				if ((stmt.getBlksize() - endOffset ) < rowsize)
+				int endLength = writer.getDSSLength();
+				int rowsize = endLength - startLength;
+				if ((stmt.getBlksize() - endLength ) < rowsize)
 					getMoreData = false;
 
-				startOffset = endOffset;
+				startLength = endLength;
 			}
 
 		}
@@ -6255,7 +6255,7 @@
 				}
 			}
 			// does all this fit in one QRYDTA
-			if (writer.getOffset() >= blksize)
+			if (writer.getDSSLength() >= blksize)
 			{
 				splitQRYDTA(stmt, blksize);
 				return false;
@@ -6300,6 +6300,17 @@
 	 * set. At some later point, when the client returns with a CNTQRY,
 	 * we will call processLeftoverQRYDTA to handle that data.
 	 *
+	 * The interaction between DRDAConnThread and DDMWriter is rather
+	 * complicated here. This routine gets called because DRDAConnThread
+	 * realizes that it has constructed a QRYDTA message which is too
+	 * large. At that point, we need to reclaim the "extra" data and
+	 * hold on to it. To aid us in that processing, DDMWriter provides
+	 * the routines getDSSLength, copyDSSDataToEnd, and truncateDSS.
+	 * For some additional detail on this complex sub-protocol, the
+	 * interested reader should study bug DERBY-491 and 492 at:
+	 * http://issues.apache.org/jira/browse/DERBY-491 and
+	 * http://issues.apache.org/jira/browse/DERBY-492
+	 *
 	 * @param stmt DRDA statment
 	 * @param blksize size of query block
 	 * 
@@ -6310,9 +6321,9 @@
 			DRDAProtocolException
 	{
 		// make copy of extra data
-		byte [] temp = writer.copyDataToEnd(blksize);
+		byte [] temp = writer.copyDSSDataToEnd(blksize);
 		// truncate to end of blocksize
-		writer.setOffset(blksize);
+		writer.truncateDSS(blksize);
 		if (temp.length == 0)
 			agentError("LMTBLKPRC violation: splitQRYDTA was " +
 				"called to split a QRYDTA block, but the " +
@@ -6423,7 +6434,7 @@
                 
 		writer.writeByte(CodePoint.NULLDATA);
 		// does all this fit in one QRYDTA
-		if (writer.getOffset() >= blksize)
+		if (writer.getDSSLength() >= blksize)
 		{
 			splitQRYDTA(stmt, blksize);
 		}

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNet/procedure.out
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNet/procedure.out?rev=372871&r1=372870&r2=372871&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNet/procedure.out (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNet/procedure.out Fri Jan 27 07:11:55 2006
@@ -935,3 +935,5 @@
 CALL LITT.TY_DECIMAL ('12.34', ?) (42821) Columns of type 'DECIMAL' cannot hold values of type 'CHAR'. 
 CALL LITT.TY_CHAR ('12.34', ?)=>12.34     <
 CALL LITT.TY_VARCHAR ('12.34', ?)=>12.34<
+JIRA-491 Successful.
+JIRA-492 successful -- no hang!

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNetClient/procedure.out
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNetClient/procedure.out?rev=372871&r1=372870&r2=372871&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNetClient/procedure.out (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNetClient/procedure.out Fri Jan 27 07:11:55 2006
@@ -937,3 +937,5 @@
 CALL LITT.TY_VARCHAR ('12.34', ?)=>12.34<
 MultipleRSAutoCommit: PASS. 
 MultipleRSNoCommit: PASS. 
+JIRA-491 Successful.
+JIRA-492 successful -- no hang!

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/procedure.out
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/procedure.out?rev=372871&r1=372870&r2=372871&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/procedure.out (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/procedure.out Fri Jan 27 07:11:55 2006
@@ -1013,3 +1013,5 @@
 CALL LITT.TY_VARCHAR ('12.34', ?)=>12.34<
 MultipleRSAutoCommit: PASS. 
 MultipleRSNoCommit: PASS. 
+JIRA-491 Successful.
+JIRA-492 successful -- no hang!

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/procedure.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/procedure.java?rev=372871&r1=372870&r2=372871&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/procedure.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/procedure.java Fri Jan 27 07:11:55 2006
@@ -87,6 +87,7 @@
 			testLiterals(conn);
             
             multipleRSTests(conn);
+                        jira_491_492(conn);
 			cleanUp(conn);
 		} catch (SQLException sqle) {
 			org.apache.derby.tools.JDBCDisplayUtil.ShowSQLException(System.out, sqle);
@@ -416,6 +417,129 @@
 
 		s.close();
 	}
+    // This test case provides tests for bugs DERBY-491 and DERBY-492. These
+    // two bug reports describe different symptoms, but the underlying bug
+    // is identical: the network server's implementation of LMTBLKPRC was
+    // incorrectly manipulating DDMWriter's bytes buffer. Depending on the
+    // details, the symptom of this bug was generally a hang, because the
+    // server mistakenly truncated the unsent data in its network buffer and
+    // hence sent only a partial transmission, causing the client to hang,
+    // waiting for data that would never arrive. A more detailed analysis
+    // of some other possible symptoms that could arise from these tests is
+    // available in the bug notes for bug 491 in JIRA at:
+    // http://issues.apache.org/jira/browse/DERBY-491
+    //
+    private static void jira_491_492(Connection conn)
+        throws SQLException
+    {
+        Statement st = conn.createStatement();
+        PreparedStatement pSt = null;
+
+        // JIRA-491: Result set has a row that is approx 32K long.
+        // When originally filed, this bug script caused  a protocol
+        // exception and connection deallocation, but that was because the
+        // bug script provoked both JIRA-614 *and* JIRA-491. If you have
+        // the fix for JIRA-614, but JIRA-491 has regressed, you will hang.
+
+        try {
+            st.execute("drop table testtable1");
+        } catch (SQLException se) {}
+
+        // Create an array of chars to be used as the input parameter.
+        // Note that the array should roughly 32K or larger.
+        char [] cData = new char[32500];
+        for (int i = 0; i < cData.length; i++)
+            cData[i] = Character.forDigit(i%10, 10);
+
+        try {
+            st.execute("create table jira491 (int1 integer, varchar32k varchar(32500))");
+            pSt=conn.prepareStatement("insert into jira491 values (?,?)");
+            for (int i = 1; i <= 5; i++) {
+                pSt.setInt(1, i);
+                pSt.setString(2, new String(cData));
+                pSt.execute();
+            }
+        } catch (SQLException se) {
+            System.out.println("JIRA-491: FAILURE in data generation:");
+            se.printStackTrace(System.out);
+        }
+
+        try {
+            st.execute("drop procedure TEST_PROC_JIRA_491");
+        } catch (SQLException se) {} // Ignore "proc does not exist" errors
+	
+        try {
+            st.execute("create procedure TEST_PROC_JIRA_491(in i int) " +
+						"language java parameter style java external name " +
+						"'org.apache.derbyTesting.functionTests.util.ProcedureTest.BIG_COL_491' result sets 2");
+        } catch (SQLException se) {
+            System.out.println("JIRA-491: FAILURE in procedure creation:");
+            se.printStackTrace(System.out);
+        }
+
+        CallableStatement cSt = conn.prepareCall("call TEST_PROC_JIRA_491(?)");
+        cSt.setInt(1, 3);
+        try {
+            cSt.execute();
+            do {
+                ResultSet rs = cSt.getResultSet();
+                while (rs.next()) {
+                    String s = rs.getString(2);
+                }
+            } while (cSt.getMoreResults());
+            System.out.println("JIRA-491 Successful.");
+        }
+        catch (Exception e)
+        {
+            System.out.println("JIRA-491 FAILURE: Caught Exception:");
+            e.printStackTrace(System.out);
+        }
+	
+        // JIRA-492: Result set has hundreds of columns.
+        // This test case, when originally filed, exposed several problems:
+        // - first, this test case causes the server to respond with a very
+        // long response message which gets handled using DRDA Layer B DSS
+        // segmentation. This long message was corrupted due to bug DERBY-125.
+        // - then, the test case causes the server to perform LMTBLKPRC
+        // message truncation in a situation in which there are multiple
+        // chained messages in the DDMWriter buffer. Due to bug DERBY-491/2,
+        // the message truncation logic truncated not only the last DSS block,
+        // but also the multi-segment long message which was still sitting
+        // unsent in the buffer.This then caused a HANG in the client, which
+        // waited forever for the never-to-be-sent truncated data.
+
+        try {
+            st.execute("drop table jira492");
+        } catch (SQLException se) {}
+
+        try {
+            st.execute("create table jira492 (id integer, nsi smallint, " +
+                "ni integer, nbi DECIMAL(19,0), nd decimal(7,2), nr real, " +
+                "ndo double)");
+            st.execute("insert into jira492 values (" +
+						"1, 2, 3, 4.5, 6.7, 8.9, 10.11)");
+        } catch (SQLException se) {
+            System.out.println("JIRA-492: FAILURE in data setup:");
+            se.printStackTrace(System.out);
+        }
+	
+        try {
+            st.execute("drop procedure TEST_PROC_JIRA_492");
+        } catch (SQLException se) {}
+	
+        try {
+            st.execute("create procedure TEST_PROC_JIRA_492() " +
+                    "language java parameter style java external name " +
+                    "'org.apache.derbyTesting.functionTests.util.ProcedureTest.LOTS_O_COLS_492' result sets 1");
+        } catch (SQLException se) {
+            System.out.println("JIRA-492: FAILURE in procedure creation:");
+            se.printStackTrace(System.out);
+        }
+	
+        cSt = conn.prepareCall("call TEST_PROC_JIRA_492()");
+        cSt.execute();
+        System.out.println("JIRA-492 successful -- no hang!");
+    }
 
 	private static void executeProcedure(Statement s, String sql) throws SQLException {
 		boolean firstResultIsAResultSet = s.execute(sql);

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/ProcedureTest.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/ProcedureTest.java?rev=372871&r1=372870&r2=372871&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/ProcedureTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/util/ProcedureTest.java Fri Jan 27 07:11:55 2006
@@ -628,5 +628,46 @@
 
         conn.close();
      }
+
+     // Procedure used by the test for bug JIRA-491. The client side part
+     // of this test is in lang/procedure.java
+
+	public static void BIG_COL_491 (int i, ResultSet [] rs1, ResultSet [] rs2)
+		throws SQLException
+	{
+		Connection conn = DriverManager.getConnection("jdbc:default:connection");
+		Statement st1 = conn.createStatement();
+		rs1[0] = st1.executeQuery(
+			"select int1, varchar32k from jira491 where int1 < " + i + " order by 1");
+
+		Statement st2 = conn.createStatement();
+		rs2[0] = st2.executeQuery(
+			"select int1, varchar32k from jira491 where int1 > " + i + " order by 1");
+	}
+
+    // Procedure used by the test for bug JIRA-492. The client side part of
+    // this test is in lang/procedure.java
+
+	public static void LOTS_O_COLS_492(ResultSet [] rs)
+        throws SQLException
+    {
+		Connection conn = DriverManager.getConnection("jdbc:default:connection");
+		Statement st1 = conn.createStatement();
+
+		StringBuffer query = new StringBuffer("SELECT ");
+        for (int i = 0; i < 100; i++)
+        {
+            int cno = 1000 + (i * 10);
+            if (i > 0) query.append(", ");
+            query.append("id AS col").append(cno).append(", nsi as col").
+                append(cno+1).append(", ni AS col").append(cno+2).
+                append(", nbi AS col").append(cno+3).append(", nd AS col").
+                append(cno+4).append(", nr AS col").append(cno+5).
+                append(", ndo AS col").append(cno+6).append(" ");
+        }
+		query.append("FROM jira492 a WHERE a.id = 0");
+
+		rs[0] = st1.executeQuery(query.toString());
+	}
 }