]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix the setXXXStream methods. If passed a null InputStream, convert
authorKris Jurka <books@ejurka.com>
Tue, 3 Feb 2004 05:13:56 +0000 (05:13 +0000)
committerKris Jurka <books@ejurka.com>
Tue, 3 Feb 2004 05:13:56 +0000 (05:13 +0000)
this to a setNull call.  The code originally would try to read the
whole stream in one call to read(), but this doesn't work.  The
InputStream API makes it clear you must be prepared to loop and
continue reading if you didn't get the whole request on the first
try.

Per report from Martin Holz.

src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java
src/interfaces/jdbc/org/postgresql/test/jdbc2/Jdbc2TestSuite.java
src/interfaces/jdbc/org/postgresql/test/jdbc2/PreparedStatementTest.java [new file with mode: 0644]

index 9645893987df5d9881adef26217bac95e0fe0b0a..50c214a30d9d8c13bc72332939b05ef440bd63f0 100644 (file)
@@ -26,7 +26,7 @@ import java.sql.Timestamp;
 import java.sql.Types;
 import java.util.Vector;
 
-/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Statement.java,v 1.41.2.2 2003/12/12 18:39:00 davec Exp $
+/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Statement.java,v 1.41.2.3 2004/02/03 05:13:55 jurka Exp $
  * This class defines methods of the jdbc1 specification.  This class is
  * extended by org.postgresql.jdbc2.AbstractJdbc2Statement which adds the jdbc2
  * methods.  The real Statement class (for jdbc1) is org.postgresql.jdbc1.Jdbc1Statement
@@ -1342,6 +1342,50 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
                }
        }
 
+       private void setCharacterStreamPost71(int parameterIndex, InputStream x, int length, String encoding) throws SQLException
+       {
+
+               if (x == null)
+               {
+                       setNull(parameterIndex, Types.VARCHAR);
+                               return;
+               }
+
+               //Version 7.2 supports AsciiStream for all PG text types (char, varchar, text)
+               //As the spec/javadoc for this method indicate this is to be used for
+               //large String values (i.e. LONGVARCHAR)  PG doesn't have a separate
+               //long varchar datatype, but with toast all text datatypes are capable of
+               //handling very large values.  Thus the implementation ends up calling
+               //setString() since there is no current way to stream the value to the server
+               try
+               {
+                       InputStreamReader l_inStream = new InputStreamReader(x, encoding);
+                       char[] l_chars = new char[length];
+                       int l_charsRead = 0;
+                       while (true)
+                       {
+                               int n = l_inStream.read(l_chars, l_charsRead, length - l_charsRead);
+                               if (n == -1)
+                                       break;
+
+                               l_charsRead += n;
+
+                               if (l_charsRead == length)
+                                       break;
+                       }
+
+                       setString(parameterIndex, new String(l_chars, 0, l_charsRead), PG_TEXT);
+               }
+               catch (UnsupportedEncodingException l_uee)
+               {
+                       throw new PSQLException("postgresql.unusual", PSQLState.UNEXPECTED_ERROR, l_uee);
+               }
+               catch (IOException l_ioe)
+               {
+                       throw new PSQLException("postgresql.unusual", PSQLState.UNEXPECTED_ERROR, l_ioe);
+               }
+       }
+
        /*
         * When a very large ASCII value is input to a LONGVARCHAR parameter,
         * it may be more practical to send it via a java.io.InputStream.
@@ -1362,27 +1406,7 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
        {
                if (connection.haveMinimumCompatibleVersion("7.2"))
                {
-                       //Version 7.2 supports AsciiStream for all PG text types (char, varchar, text)
-                       //As the spec/javadoc for this method indicate this is to be used for
-                       //large String values (i.e. LONGVARCHAR)  PG doesn't have a separate
-                       //long varchar datatype, but with toast all text datatypes are capable of
-                       //handling very large values.  Thus the implementation ends up calling
-                       //setString() since there is no current way to stream the value to the server
-                       try
-                       {
-                               InputStreamReader l_inStream = new InputStreamReader(x, "ASCII");
-                               char[] l_chars = new char[length];
-                               int l_charsRead = l_inStream.read(l_chars, 0, length);
-                               setString(parameterIndex, new String(l_chars, 0, l_charsRead), PG_TEXT);
-                       }
-                       catch (UnsupportedEncodingException l_uee)
-                       {
-                               throw new PSQLException("postgresql.unusual", PSQLState.UNEXPECTED_ERROR, l_uee);
-                       }
-                       catch (IOException l_ioe)
-                       {
-                               throw new PSQLException("postgresql.unusual", PSQLState.UNEXPECTED_ERROR, l_ioe);
-                       }
+                       setCharacterStreamPost71(parameterIndex, x, length, "ASCII");
                }
                else
                {
@@ -1411,27 +1435,7 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
        {
                if (connection.haveMinimumCompatibleVersion("7.2"))
                {
-                       //Version 7.2 supports AsciiStream for all PG text types (char, varchar, text)
-                       //As the spec/javadoc for this method indicate this is to be used for
-                       //large String values (i.e. LONGVARCHAR)  PG doesn't have a separate
-                       //long varchar datatype, but with toast all text datatypes are capable of
-                       //handling very large values.  Thus the implementation ends up calling
-                       //setString() since there is no current way to stream the value to the server
-                       try
-                       {
-                               InputStreamReader l_inStream = new InputStreamReader(x, "UTF-8");
-                               char[] l_chars = new char[length];
-                               int l_charsRead = l_inStream.read(l_chars, 0, length);
-                               setString(parameterIndex, new String(l_chars, 0, l_charsRead), PG_TEXT);
-                       }
-                       catch (UnsupportedEncodingException l_uee)
-                       {
-                               throw new PSQLException("postgresql.unusual", PSQLState.UNEXPECTED_ERROR, l_uee);
-                       }
-                       catch (IOException l_ioe)
-                       {
-                               throw new PSQLException("postgresql.unusual", PSQLState.UNEXPECTED_ERROR, l_ioe);
-                       }
+                       setCharacterStreamPost71(parameterIndex, x, length, "UTF-8");
                }
                else
                {
@@ -1459,6 +1463,12 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
        {
                if (connection.haveMinimumCompatibleVersion("7.2"))
                {
+                       if (x == null)
+                       {
+                               setNull(parameterIndex, Types.VARBINARY);
+                               return;
+                       }
+
                        //Version 7.2 supports BinaryStream for for the PG bytea type
                        //As the spec/javadoc for this method indicate this is to be used for
                        //large binary values (i.e. LONGVARBINARY)      PG doesn't have a separate
@@ -1466,10 +1476,21 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
                        //handling very large values.  Thus the implementation ends up calling
                        //setBytes() since there is no current way to stream the value to the server
                        byte[] l_bytes = new byte[length];
-                       int l_bytesRead;
+                       int l_bytesRead = 0;
                        try
                        {
-                               l_bytesRead = x.read(l_bytes, 0, length);
+                               while (true)
+                               {
+                                       int n = x.read(l_bytes, l_bytesRead, length - l_bytesRead);
+                                       if (n == -1)
+                                               break;
+
+                                       l_bytesRead += n;
+
+                                       if (l_bytesRead == length)
+                                               break;
+
+                               }
                        }
                        catch (IOException l_ioe)
                        {
@@ -1479,11 +1500,6 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
                        {
                                setBytes(parameterIndex, l_bytes);
                        }
-                       // x.read will return -1 not 0 on an empty InputStream
-                       else if (l_bytesRead == -1)
-                       {
-                               setBytes(parameterIndex, new byte[0]);
-                       }
                        else
                        {
                                //the stream contained less data than they said
index 7a5acab8e26bab4a8b3f14cf9635a2a803eff3e4..e1b480a1f1c168fb1a340d7323035b6eb8760efa 100644 (file)
@@ -42,6 +42,7 @@ public class Jdbc2TestSuite extends TestSuite
                suite.addTestSuite(TimestampTest.class);
 
                // PreparedStatement
+               suite.addTestSuite(PreparedStatementTest.class);
 
                // ServerSide Prepared Statements
                suite.addTestSuite(ServerPreparedStmtTest.class);
diff --git a/src/interfaces/jdbc/org/postgresql/test/jdbc2/PreparedStatementTest.java b/src/interfaces/jdbc/org/postgresql/test/jdbc2/PreparedStatementTest.java
new file mode 100644 (file)
index 0000000..e6ddf59
--- /dev/null
@@ -0,0 +1,91 @@
+package org.postgresql.test.jdbc2;
+
+import org.postgresql.test.TestUtil;
+import junit.framework.TestCase;
+import java.io.*;
+import java.sql.*;
+
+
+public class PreparedStatementTest extends TestCase
+{
+
+       private Connection conn;
+
+       public PreparedStatementTest(String name)
+       {
+               super(name);
+       }
+
+       protected void setUp() throws SQLException
+       {
+               conn = TestUtil.openDB();
+               TestUtil.createTable(conn, "streamtable", "bin bytea, str text");
+       }
+
+       protected void tearDown() throws SQLException
+       {
+               TestUtil.dropTable(conn, "streamtable");
+               TestUtil.closeDB(conn);
+       }
+
+       public void testSetBinaryStream() throws SQLException
+       {
+               ByteArrayInputStream bais;
+               byte buf[] = new byte[10];
+               for (int i=0; i<buf.length; i++) {
+                       buf[i] = (byte)i;
+               }
+
+               bais = null;
+               doSetBinaryStream(bais,0);
+
+               bais = new ByteArrayInputStream(new byte[0]);
+               doSetBinaryStream(bais,100);
+
+               bais = new ByteArrayInputStream(buf);
+               doSetBinaryStream(bais,0);
+
+               bais = new ByteArrayInputStream(buf);
+               doSetBinaryStream(bais,10);
+
+               bais = new ByteArrayInputStream(buf);
+               doSetBinaryStream(bais,100);
+       }
+
+       public void testSetAsciiStream() throws Exception
+       {
+               ByteArrayOutputStream baos = new ByteArrayOutputStream();
+               PrintWriter pw = new PrintWriter(new OutputStreamWriter(baos,"ASCII"));
+               pw.println("Hello");
+               pw.flush();
+               
+               ByteArrayInputStream bais;
+               
+               bais = new ByteArrayInputStream(baos.toByteArray());
+               doSetAsciiStream(bais, 0);
+
+               bais = new ByteArrayInputStream(baos.toByteArray());
+               doSetAsciiStream(bais, 6);
+
+               bais = new ByteArrayInputStream(baos.toByteArray());
+               doSetAsciiStream(bais, 100);
+       }
+
+       private void doSetBinaryStream(ByteArrayInputStream bais, int length) throws SQLException
+       {
+               PreparedStatement pstmt = conn.prepareStatement("INSERT INTO streamtable (bin,str) VALUES (?,?)");
+               pstmt.setBinaryStream(1,bais, length);
+               pstmt.setString(2,null);
+               pstmt.executeUpdate();
+               pstmt.close();
+       }
+
+       private void doSetAsciiStream(InputStream is, int length) throws SQLException
+       {
+               PreparedStatement pstmt = conn.prepareStatement("INSERT INTO streamtable (bin,str) VALUES (?,?)");
+               pstmt.setBytes(1,null);
+               pstmt.setAsciiStream(2, is, length);
+               pstmt.executeUpdate();
+               pstmt.close();
+       }
+}