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 2007/10/01 20:27:14 UTC

svn commit: r581031 - in /db/derby/code/branches/10.3/java: drda/org/apache/derby/impl/drda/DRDAConnThread.java drda/org/apache/derby/impl/drda/DRDAStatement.java testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BlobClob4BlobTest.java

Author: kmarsden
Date: Mon Oct  1 11:27:09 2007
New Revision: 581031

URL: http://svn.apache.org/viewvc?rev=581031&view=rev
Log:
DERBY-3085 Fails to handle BLOB fields with a PreparedStatement with size >32750 bytes

port from trunk


Modified:
    db/derby/code/branches/10.3/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java
    db/derby/code/branches/10.3/java/drda/org/apache/derby/impl/drda/DRDAStatement.java
    db/derby/code/branches/10.3/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BlobClob4BlobTest.java

Modified: db/derby/code/branches/10.3/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.3/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java?rev=581031&r1=581030&r2=581031&view=diff
==============================================================================
--- db/derby/code/branches/10.3/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java (original)
+++ db/derby/code/branches/10.3/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java Mon Oct  1 11:27:09 2007
@@ -4527,6 +4527,10 @@
 		for (int i = 0; i < numExt; i++)
 					{
 						int paramPos = stmt.getExtPosition(i);
+						// Only the last EXTDTA is streamed.  This is because all of 
+						// the parameters have to be set before execution and are 
+						// consecutive in the network server stream, so only the last
+						// one can be streamed.
 						final boolean doStreamLOB = (streamLOB && i == numExt -1);
 						readAndSetExtParam(paramPos,
 										   stmt,
@@ -4580,12 +4584,13 @@
 							paramBytes = null;
 							final EXTDTAReaderInputStream stream = 
 								reader.getEXTDTAReaderInputStream(checkNullability);
-                            
+                            // Save the streamed parameter so we can drain it if it does not get used
+                            // by embedded when the statement is executed. DERBY-3085
+                            stmt.setStreamedParameter(stream);
                             if( stream instanceof StandardEXTDTAReaderInputStream ){
                                 
                                 final StandardEXTDTAReaderInputStream stdeis = 
                                     (StandardEXTDTAReaderInputStream) stream ;
-
                                 ps.setBinaryStream( i + 1, 
                                                     stdeis, 
                                                     (int) stdeis.getLength() );
@@ -4643,7 +4648,7 @@
 					case DRDAConstants.DRDA_TYPE_LOBCSBCS:
 					case DRDAConstants.DRDA_TYPE_NLOBCSBCS:
                         
-                        setAsCharacterStream(ps,
+                        setAsCharacterStream(stmt,
                                              i,
                                              checkNullability,
                                              reader,
@@ -4654,7 +4659,7 @@
 					case DRDAConstants.DRDA_TYPE_LOBCDBCS:
 					case DRDAConstants.DRDA_TYPE_NLOBCDBCS:
                         
-                        setAsCharacterStream(ps,
+                        setAsCharacterStream(stmt,
                                              i,
                                              checkNullability,
                                              reader,
@@ -4665,7 +4670,7 @@
 					case DRDAConstants.DRDA_TYPE_LOBCMIXED:
 					case DRDAConstants.DRDA_TYPE_NLOBCMIXED:
 
-                        setAsCharacterStream(ps,
+                        setAsCharacterStream(stmt,
                                              i,
                                              checkNullability,
                                              reader,
@@ -8259,7 +8264,7 @@
         
     }
     
-    private static void setAsCharacterStream(PreparedStatement ps,
+    private static void setAsCharacterStream(DRDAStatement stmt,
                                              int i,
                                              boolean checkNullability,
                                              DDMReader reader,
@@ -8268,12 +8273,16 @@
         throws DRDAProtocolException ,
                SQLException ,
                IOException {
-        
+        PreparedStatement ps = stmt.getPreparedStatement();
         EnginePreparedStatement engnps = 
             ( EnginePreparedStatement ) ps;
         
         final EXTDTAReaderInputStream extdtastream = 
             reader.getEXTDTAReaderInputStream(checkNullability);
+        // DERBY-3085. Save the stream so it can be drained later
+        // if not  used.
+        if (streamLOB)
+            stmt.setStreamedParameter(extdtastream);
         
         final InputStream is = 
             streamLOB ?

Modified: db/derby/code/branches/10.3/java/drda/org/apache/derby/impl/drda/DRDAStatement.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.3/java/drda/org/apache/derby/impl/drda/DRDAStatement.java?rev=581031&r1=581030&r2=581031&view=diff
==============================================================================
--- db/derby/code/branches/10.3/java/drda/org/apache/derby/impl/drda/DRDAStatement.java (original)
+++ db/derby/code/branches/10.3/java/drda/org/apache/derby/impl/drda/DRDAStatement.java Mon Oct  1 11:27:09 2007
@@ -21,6 +21,7 @@
 
 package org.apache.derby.impl.drda;
 
+import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -108,6 +109,12 @@
 	 * of Integer/Byte in order to re-use the same storage each time
 	 * the statement is executed. */
 	private static class DrdaParamState {
+		// The last parameter may be streamed. 
+		// We need to keep a record of it so we can drain it if it is not 
+		// used.
+		// Only the last parameter with an EXTDTA will be streamed. 
+		//(See DRDAConnThread.readAndSetAllExtParams()). 
+		private EXTDTAReaderInputStream streamedParameter = null;
 		private int typeLstEnd_ = 0;
 		private byte[] typeLst_ = new byte[10];
 		private int[]  lenLst_ = new int[10];
@@ -132,6 +139,7 @@
 		 * @param trim - if true; release excess storage
 		 */
 		protected void clear(boolean trim) {
+			streamedParameter = null;
 			typeLstEnd_ = 0;
 			extLstEnd_ = 0;
 			if (trim && typeLst_.length > 10) {
@@ -223,6 +231,33 @@
 		 * of the ith external parameter (zero-based)
 		 */
 		protected int getExtPos(int i) { return extLst_[i]; }
+        
+		/**
+		 * Read the rest of the streamed parameter if not consumed
+		 * by the executing statement.  DERBY-3085
+		 * @throws IOException
+		 */
+		protected void drainStreamedParameter() throws IOException
+		{
+			if (streamedParameter != null)
+			{   
+				// we drain the buffer 1000 bytes at a time.
+				// 1000 is just a random selection that doesn't take
+				// too much memory. Perhaps something else would be 
+				// more efficient?
+				byte[] buffer = new byte[1000];
+				int i;
+				do {
+					i= streamedParameter.read(buffer,0,1000);
+				}  while (i != -1);
+			}
+		}
+            
+
+		public void setStreamedParameter(EXTDTAReaderInputStream eis) {
+			streamedParameter = eis;    
+		}
+		
 	}
 	private DrdaParamState drdaParamState_ = new DrdaParamState();
 
@@ -664,7 +699,16 @@
 	protected boolean execute() throws SQLException
 	{
 		boolean hasResultSet = ps.execute();
-
+		// DERBY-3085 - We need to make sure we drain the streamed parameter
+		// if not used by the server, for example if an update statement does not 
+		// update any rows, the parameter won't be used.  Network Server will
+		// stream only the last parameter with an EXTDTA. This is stored when the
+		// parameter is set and drained now after statement execution if needed.
+		try {
+			drdaParamState_.drainStreamedParameter();
+		} catch (IOException e) { 
+			Util.javaException(e);
+		}
 		// java.sql.Statement says any result sets that are opened
 		// when the statement is re-executed must be closed; this
 		// is handled by the call to "ps.execute()" above--but we
@@ -1260,6 +1304,11 @@
 		drdaParamState_.addDrdaParam(t, l);
 	}
 
+    protected void setStreamedParameter(EXTDTAReaderInputStream eis)
+    {
+        drdaParamState_.setStreamedParameter(eis);
+    }
+    
 	/**
 	 * get parameter DRDAType
 	 *

Modified: db/derby/code/branches/10.3/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BlobClob4BlobTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.3/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BlobClob4BlobTest.java?rev=581031&r1=581030&r2=581031&view=diff
==============================================================================
--- db/derby/code/branches/10.3/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BlobClob4BlobTest.java (original)
+++ db/derby/code/branches/10.3/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BlobClob4BlobTest.java Mon Oct  1 11:27:09 2007
@@ -20,6 +20,7 @@
 package org.apache.derbyTesting.functionTests.tests.jdbcapi;
 
 import java.io.ByteArrayInputStream;
+import java.io.CharArrayReader;
 import java.io.InputStream;
 import java.io.Reader;
 import java.io.StringReader;
@@ -87,6 +88,101 @@
     /***                TESTS               ***/
 
     /**
+     * DERBY-3085.  Test update where streamed parameter is not 
+     * consumed by the server. Network Server needs to clean-up 
+     * after execution.
+     * 
+     */
+    public void testUnconsumedParameter() throws SQLException
+    {
+        Connection conn = getConnection();
+        conn.setAutoCommit(false);
+        Statement s = conn.createStatement();
+        // Test table with no rows.
+        s.executeUpdate("create table testing(num int, addr varchar(40), contents blob(16M))");
+        // no rows inserted so there is no match.
+        byte[] data = new byte[ 38000];
+        for (int i = 0; i < data.length; i++)
+            data[i] = 'a';
+        ByteArrayInputStream is = new ByteArrayInputStream( data);           
+        String sql = "UPDATE testing SET Contents=? WHERE num=1";
+        
+        PreparedStatement ps = prepareStatement( sql);
+        ps.setBinaryStream( 1, is,data.length);
+        ps.executeUpdate();          
+        // Make sure things still work ok when we have a parameter that does get consumed.
+        // insert a matching row.
+        s.executeUpdate("insert into testing values (1,null,null)");
+        is = new ByteArrayInputStream(data);
+        ps.setBinaryStream( 1, is,data.length);
+        ps.executeUpdate();
+        // Check update occurred
+        ResultSet rs = s.executeQuery("select length(contents) from testing where num = 1");
+        JDBC.assertSingleValueResultSet(rs, "38000");
+        ps.close();
+        conn.commit();
+        // Check the case where there are rows inserted but there is no match.
+        is = new ByteArrayInputStream( data);           
+        sql = "UPDATE testing SET Contents=? WHERE num=2";
+        ps = prepareStatement( sql);
+        ps.setBinaryStream( 1, is,data.length);
+        ps.executeUpdate();
+        ps.close();
+        s.executeUpdate("drop table testing");
+        conn.commit();
+        
+        // Test with multiple parameters
+        s.executeUpdate("create table testing(num int, addr varchar(40), contents blob(16M),contents2 blob(16M))");
+        
+        is = new ByteArrayInputStream( data);
+        ByteArrayInputStream is2 = new ByteArrayInputStream(data);
+        sql = "UPDATE testing SET Contents=?, contents2=?  WHERE num=1";
+
+        ps = prepareStatement( sql);
+        ps.setBinaryStream( 1, is,data.length);
+        ps.setBinaryStream(2, is2,data.length);
+        ps.executeUpdate();
+        
+        
+        // multiple parameters and matching row
+        s.executeUpdate("insert into testing values (1,'addr',NULL,NULL)");
+        is = new ByteArrayInputStream( data);
+        is2 = new ByteArrayInputStream(data);
+        ps.setBinaryStream( 1, is,data.length);
+        ps.setBinaryStream(2, is2,data.length);
+        ps.executeUpdate();
+        rs = s.executeQuery("select length(contents), length(contents2) from testing where num = 1");
+        JDBC.assertFullResultSet(rs, new String[][] {{"38000","38000"}});
+        rs.close();
+        s.executeUpdate("drop table testing");
+        
+        // With Clob
+        s.executeUpdate("create table testing(num int, addr varchar(40), contents Clob(16M))");
+        char[] charData = new char[ 38000];
+        for (int i = 0; i < data.length; i++)
+       data[i] = 'a';
+        CharArrayReader reader = new CharArrayReader( charData);            
+        sql = "UPDATE testing SET Contents=? WHERE num=1";
+
+       ps = prepareStatement( sql);
+       ps.setCharacterStream( 1, reader,charData.length);
+       ps.executeUpdate();
+       // with a matching row
+       s.executeUpdate("insert into testing values (1,null,null)");
+       reader = new CharArrayReader(charData);
+       ps.setCharacterStream( 1, reader,data.length);
+       ps.executeUpdate();
+       // Check update occurred
+       rs = s.executeQuery("select length(contents) from testing where num = 1");
+       JDBC.assertSingleValueResultSet(rs, "38000");
+       s.executeUpdate("drop table testing");
+       ps.close();
+       
+       conn.commit();
+        
+    }
+
+    /**
      * Tests PreparedStatement.setCharacterStream
      */
     public void testSetCharacterStream() throws Exception {
@@ -3189,6 +3285,7 @@
         s.execute("drop table blobCheck");
     }
 
+     
     private void checkException(String SQLState, SQLException se)
             throws Exception
     {