1
0
mirror of https://github.com/postgres/postgres.git synced 2025-05-18 17:41:14 +03:00

Fix the setXXXStream methods. If passed a null InputStream, convert

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.
This commit is contained in:
Kris Jurka 2004-02-03 05:13:56 +00:00
parent 7ca2bff95f
commit 9287630fbc
3 changed files with 158 additions and 50 deletions

View 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

View 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);

View File

@ -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();
}
}