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
{