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 18:49:39 UTC

svn commit: r581012 - in /db/derby/code/trunk/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 09:49:37 2007
New Revision: 581012

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

Store a reference to the stream for the streamed parameter in the DRDAStatement.paramState and then drain the stream after statement execution if needed. There is only one parameter ever streamed, so only one field needed to be added. I added a test for both BLOB's and CLOB's to Blob4ClobTest.java.


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

Modified: db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java?rev=581012&r1=581011&r2=581012&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 Mon Oct  1 09:49:37 2007
@@ -4629,6 +4629,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,
@@ -4682,12 +4686,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() );
@@ -4745,7 +4750,7 @@
 					case DRDAConstants.DRDA_TYPE_LOBCSBCS:
 					case DRDAConstants.DRDA_TYPE_NLOBCSBCS:
                         
-                        setAsCharacterStream(ps,
+                        setAsCharacterStream(stmt,
                                              i,
                                              checkNullability,
                                              reader,
@@ -4756,7 +4761,7 @@
 					case DRDAConstants.DRDA_TYPE_LOBCDBCS:
 					case DRDAConstants.DRDA_TYPE_NLOBCDBCS:
                         
-                        setAsCharacterStream(ps,
+                        setAsCharacterStream(stmt,
                                              i,
                                              checkNullability,
                                              reader,
@@ -4767,7 +4772,7 @@
 					case DRDAConstants.DRDA_TYPE_LOBCMIXED:
 					case DRDAConstants.DRDA_TYPE_NLOBCMIXED:
 
-                        setAsCharacterStream(ps,
+                        setAsCharacterStream(stmt,
                                              i,
                                              checkNullability,
                                              reader,
@@ -8361,7 +8366,7 @@
         
     }
     
-    private static void setAsCharacterStream(PreparedStatement ps,
+    private static void setAsCharacterStream(DRDAStatement stmt,
                                              int i,
                                              boolean checkNullability,
                                              DDMReader reader,
@@ -8370,12 +8375,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/trunk/java/drda/org/apache/derby/impl/drda/DRDAStatement.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAStatement.java?rev=581012&r1=581011&r2=581012&view=diff
==============================================================================
--- db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAStatement.java (original)
+++ db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAStatement.java Mon Oct  1 09:49:37 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
@@ -1198,6 +1242,11 @@
 		drdaParamState_.addDrdaParam(t, l);
 	}
 
+    protected void setStreamedParameter(EXTDTAReaderInputStream eis)
+    {
+        drdaParamState_.setStreamedParameter(eis);
+    }
+    
 	/**
 	 * get parameter DRDAType
 	 *

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BlobClob4BlobTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BlobClob4BlobTest.java?rev=581012&r1=581011&r2=581012&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BlobClob4BlobTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/BlobClob4BlobTest.java Mon Oct  1 09:49:37 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
     {