diff --git a/src/interfaces/jdbc/org/postgresql/Connection.java b/src/interfaces/jdbc/org/postgresql/Connection.java index fc03116f43c..aba2849171c 100644 --- a/src/interfaces/jdbc/org/postgresql/Connection.java +++ b/src/interfaces/jdbc/org/postgresql/Connection.java @@ -11,7 +11,7 @@ import org.postgresql.util.*; import org.postgresql.core.*; /** - * $Id: Connection.java,v 1.28 2001/09/07 22:17:02 momjian Exp $ + * $Id: Connection.java,v 1.29 2001/09/10 15:07:05 momjian Exp $ * * This abstract class is used by org.postgresql.Driver to open either the JDBC1 or * JDBC2 versions of the Connection class. @@ -22,15 +22,13 @@ public abstract class Connection // This is the network stream associated with this connection public PG_Stream pg_stream; - // This is set by org.postgresql.Statement.setMaxRows() - //public int maxrows = 0; // maximum no. of rows; 0 = unlimited - private String PG_HOST; private int PG_PORT; private String PG_USER; private String PG_PASSWORD; private String PG_DATABASE; private boolean PG_STATUS; + private String compatible; /** * The encoding to use for this connection. @@ -123,6 +121,11 @@ public abstract class Connection PG_PORT = port; PG_HOST = host; PG_STATUS = CONNECTION_BAD; + if(info.getProperty("compatible")==null) { + compatible = d.getMajorVersion() + "." + d.getMinorVersion(); + } else { + compatible = info.getProperty("compatible"); + } // Now make the initial connection try @@ -968,6 +971,23 @@ public abstract class Connection return (getDBVersionNumber().compareTo(ver) >= 0); } + /** + * This method returns true if the compatible level set in the connection + * (which can be passed into the connection or specified in the URL) + * is at least the value passed to this method. This is used to toggle + * between different functionality as it changes across different releases + * of the jdbc driver code. The values here are versions of the jdbc client + * and not server versions. For example in 7.1 get/setBytes worked on + * LargeObject values, in 7.2 these methods were changed to work on bytea + * values. This change in functionality could be disabled by setting the + * "compatible" level to be 7.1, in which case the driver will revert to + * the 7.1 functionality. + */ + public boolean haveMinimumCompatibleVersion(String ver) throws SQLException + { + return (compatible.compareTo(ver) >= 0); + } + /** * This returns the java.sql.Types type for a PG type oid diff --git a/src/interfaces/jdbc/org/postgresql/Driver.java.in b/src/interfaces/jdbc/org/postgresql/Driver.java.in index c243a2f284f..a0d5c6e70b6 100644 --- a/src/interfaces/jdbc/org/postgresql/Driver.java.in +++ b/src/interfaces/jdbc/org/postgresql/Driver.java.in @@ -85,12 +85,26 @@ public class Driver implements java.sql.Driver * database. * *

The java.util.Properties argument can be used to pass arbitrary - * string tag/value pairs as connection arguments. Normally, at least + * string tag/value pairs as connection arguments. + * + * user - (optional) The user to connect as + * password - (optional) The password for the user + * charSet - (optional) The character set to be used for converting + * to/from the database to unicode. If multibyte is enabled on the + * server then the character set of the database is used as the default, + * otherwise the jvm character encoding is used as the default. + * compatible - This is used to toggle + * between different functionality as it changes across different releases + * of the jdbc driver code. The values here are versions of the jdbc + * client and not server versions. For example in 7.1 get/setBytes + * worked on LargeObject values, in 7.2 these methods were changed + * to work on bytea values. This change in functionality could + * be disabled by setting the compatible level to be "7.1", in + * which case the driver will revert to the 7.1 functionality. + * + *

Normally, at least * "user" and "password" properties should be included in the - * properties. In addition, the "charSet" property can be used to - * set a character set encoding (e.g. "utf-8") other than the platform - * default (typically Latin1). This is necessary in particular if storing - * multibyte characters in the database. For a list of supported + * properties. For a list of supported * character encoding , see * http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html * Note that you will probably want to have set up the Postgres database diff --git a/src/interfaces/jdbc/org/postgresql/ResultSet.java b/src/interfaces/jdbc/org/postgresql/ResultSet.java index bea07e639b1..8757d21c7ec 100644 --- a/src/interfaces/jdbc/org/postgresql/ResultSet.java +++ b/src/interfaces/jdbc/org/postgresql/ResultSet.java @@ -192,7 +192,8 @@ public abstract class ResultSet String s = getString(col); // Handle SQL Null - if(s==null) + wasNullFlag = (this_row[col - 1] == null); + if(wasNullFlag) return null; // Handle Money diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/Connection.java b/src/interfaces/jdbc/org/postgresql/jdbc1/Connection.java index cf2b4bfd29f..814d693df5b 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/Connection.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc1/Connection.java @@ -17,7 +17,7 @@ import org.postgresql.largeobject.*; import org.postgresql.util.*; /** - * $Id: Connection.java,v 1.9 2001/09/06 03:13:34 momjian Exp $ + * $Id: Connection.java,v 1.10 2001/09/10 15:07:05 momjian Exp $ * * A Connection represents a session with a specific database. Within the * context of a Connection, SQL statements are executed and results are @@ -174,6 +174,7 @@ public class Connection extends org.postgresql.Connection implements java.sql.Co "float8", "bpchar","char","char2","char4","char8","char16", "varchar","text","name","filename", + "bytea", "bool", "date", "time", @@ -197,6 +198,7 @@ public class Connection extends org.postgresql.Connection implements java.sql.Co Types.DOUBLE, Types.CHAR,Types.CHAR,Types.CHAR,Types.CHAR,Types.CHAR,Types.CHAR, Types.VARCHAR,Types.VARCHAR,Types.VARCHAR,Types.VARCHAR, + Types.BINARY, Types.BIT, Types.DATE, Types.TIME, diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/PreparedStatement.java b/src/interfaces/jdbc/org/postgresql/jdbc1/PreparedStatement.java index b2b68d50a55..0c850e36ea0 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc1/PreparedStatement.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc1/PreparedStatement.java @@ -82,7 +82,7 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta * A Prepared SQL query is executed and its ResultSet is returned * * @return a ResultSet that contains the data produced by the - * query - never null + * * query - never null * @exception SQLException if a database access error occurs */ public java.sql.ResultSet executeQuery() throws SQLException @@ -107,7 +107,7 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta * be executed. * * @return either the row count for INSERT, UPDATE or DELETE; or - * 0 for SQL statements that return nothing. + * * 0 for SQL statements that return nothing. * @exception SQLException if a database access error occurs */ public int executeUpdate() throws SQLException @@ -294,12 +294,22 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta */ public void setBytes(int parameterIndex, byte x[]) throws SQLException { + if (connection.haveMinimumCompatibleVersion("7.2")) { + //Version 7.2 supports the bytea datatype for byte arrays + if(null == x){ + setNull(parameterIndex,Types.OTHER); + } else { + setString(parameterIndex, PGbytea.toPGString(x)); + } + } else { + //Version 7.1 and earlier support done as LargeObjects LargeObjectManager lom = connection.getLargeObjectAPI(); int oid = lom.create(); LargeObject lob = lom.open(oid); lob.write(x); lob.close(); setInt(parameterIndex,oid); + } } /** @@ -386,8 +396,29 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta */ public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { + 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)); + } catch (UnsupportedEncodingException l_uee) { + throw new PSQLException("postgresql.unusual",l_uee); + } catch (IOException l_ioe) { + throw new PSQLException("postgresql.unusual",l_ioe); + } + } else { + //Version 7.1 supported only LargeObjects by treating everything + //as binary data setBinaryStream(parameterIndex, x, length); } + } /** * When a very large Unicode value is input to a LONGVARCHAR parameter, @@ -406,8 +437,29 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta */ public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { + 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)); + } catch (UnsupportedEncodingException l_uee) { + throw new PSQLException("postgresql.unusual",l_uee); + } catch (IOException l_ioe) { + throw new PSQLException("postgresql.unusual",l_ioe); + } + } else { + //Version 7.1 supported only LargeObjects by treating everything + //as binary data setBinaryStream(parameterIndex, x, length); } + } /** * When a very large binary value is input to a LONGVARBINARY parameter, @@ -425,7 +477,54 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta */ public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { - throw org.postgresql.Driver.notImplemented(); + if (connection.haveMinimumCompatibleVersion("7.2")) { + //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 + //long binary datatype, but with toast the bytea datatype is capable of + //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; + try { + l_bytesRead = x.read(l_bytes,0,length); + } catch (IOException l_ioe) { + throw new PSQLException("postgresql.unusual",l_ioe); + } + if (l_bytesRead == length) { + setBytes(parameterIndex, l_bytes); + } else { + //the stream contained less data than they said + byte[] l_bytes2 = new byte[l_bytesRead]; + System.arraycopy(l_bytes,0,l_bytes2,0,l_bytesRead); + setBytes(parameterIndex, l_bytes2); + } + } else { + //Version 7.1 only supported streams for LargeObjects + //but the jdbc spec indicates that streams should be + //available for LONGVARBINARY instead + LargeObjectManager lom = connection.getLargeObjectAPI(); + int oid = lom.create(); + LargeObject lob = lom.open(oid); + OutputStream los = lob.getOutputStream(); + try { + // could be buffered, but then the OutputStream returned by LargeObject + // is buffered internally anyhow, so there would be no performance + // boost gained, if anything it would be worse! + int c=x.read(); + int p=0; + while(c>-1 && p fields.length) throw new PSQLException("postgresql.res.colrange"); - wasNullFlag = (this_row[columnIndex - 1] == null); + if (connection.haveMinimumCompatibleVersion("7.2")) { + //Version 7.2 supports the bytea datatype for byte arrays + return PGbytea.toBytes(getString(columnIndex)); + } else { + //Version 7.1 and earlier supports LargeObjects for byte arrays + wasNullFlag = (this_row[columnIndex - 1] == null); // Handle OID's as BLOBS - if(!wasNullFlag) + if(!wasNullFlag) { if( fields[columnIndex - 1].getOID() == 26) { LargeObjectManager lom = connection.getLargeObjectAPI(); LargeObject lob = lom.open(getInt(columnIndex)); @@ -385,8 +390,9 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu lob.close(); return buf; } - - return this_row[columnIndex - 1]; + } + } + return null; } /** @@ -545,8 +551,27 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu */ public InputStream getAsciiStream(int columnIndex) throws SQLException { + wasNullFlag = (this_row[columnIndex - 1] == null); + if (wasNullFlag) + return null; + + if (connection.haveMinimumCompatibleVersion("7.2")) { + //Version 7.2 supports AsciiStream for all the PG text types + //As the spec/javadoc for this method indicate this is to be used for + //large text values (i.e. LONGVARCHAR) PG doesn't have a separate + //long string datatype, but with toast the text datatype is capable of + //handling very large values. Thus the implementation ends up calling + //getString() since there is no current way to stream the value from the server + try { + return new ByteArrayInputStream(getString(columnIndex).getBytes("ASCII")); + } catch (UnsupportedEncodingException l_uee) { + throw new PSQLException("postgresql.unusual", l_uee); + } + } else { + // In 7.1 Handle as BLOBS so return the LargeObject input stream return getBinaryStream(columnIndex); } + } /** * A column value can also be retrieved as a stream of Unicode @@ -562,8 +587,27 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu */ public InputStream getUnicodeStream(int columnIndex) throws SQLException { + wasNullFlag = (this_row[columnIndex - 1] == null); + if (wasNullFlag) + return null; + + if (connection.haveMinimumCompatibleVersion("7.2")) { + //Version 7.2 supports AsciiStream for all the PG text types + //As the spec/javadoc for this method indicate this is to be used for + //large text values (i.e. LONGVARCHAR) PG doesn't have a separate + //long string datatype, but with toast the text datatype is capable of + //handling very large values. Thus the implementation ends up calling + //getString() since there is no current way to stream the value from the server + try { + return new ByteArrayInputStream(getString(columnIndex).getBytes("UTF-8")); + } catch (UnsupportedEncodingException l_uee) { + throw new PSQLException("postgresql.unusual", l_uee); + } + } else { + // In 7.1 Handle as BLOBS so return the LargeObject input stream return getBinaryStream(columnIndex); } + } /** * A column value can also be retrieved as a binary strea. This @@ -579,11 +623,29 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu */ public InputStream getBinaryStream(int columnIndex) throws SQLException { - byte b[] = getBytes(columnIndex); + wasNullFlag = (this_row[columnIndex - 1] == null); + if (wasNullFlag) + return null; + if (connection.haveMinimumCompatibleVersion("7.2")) { + //Version 7.2 supports BinaryStream for all 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 + //long binary datatype, but with toast the bytea datatype is capable of + //handling very large values. Thus the implementation ends up calling + //getBytes() since there is no current way to stream the value from the server + byte b[] = getBytes(columnIndex); if (b != null) return new ByteArrayInputStream(b); - return null; // SQL NULL + } else { + // In 7.1 Handle as BLOBS so return the LargeObject input stream + if( fields[columnIndex - 1].getOID() == 26) { + LargeObjectManager lom = connection.getLargeObjectAPI(); + LargeObject lob = lom.open(getInt(columnIndex)); + return lob.getInputStream(); + } + } + return null; } /** diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/Connection.java b/src/interfaces/jdbc/org/postgresql/jdbc2/Connection.java index f705693983c..9aa98cdbb62 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/Connection.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/Connection.java @@ -17,7 +17,7 @@ import org.postgresql.largeobject.*; import org.postgresql.util.*; /** - * $Id: Connection.java,v 1.11 2001/09/06 03:13:34 momjian Exp $ + * $Id: Connection.java,v 1.12 2001/09/10 15:07:05 momjian Exp $ * * A Connection represents a session with a specific database. Within the * context of a Connection, SQL statements are executed and results are @@ -291,12 +291,15 @@ public class Connection extends org.postgresql.Connection implements java.sql.Co "float8", "bpchar","char","char2","char4","char8","char16", "varchar","text","name","filename", + "bytea", "bool", "date", "time", "abstime","timestamp", - "_bool", "_char", "_int2", "_int4", "_text", "_oid", "_varchar", "_int8", - "_float4", "_float8", "_abstime", "_date", "_time", "_timestamp", "_numeric" + "_bool", "_char", "_int2", "_int4", "_text", + "_oid", "_varchar", "_int8", "_float4", "_float8", + "_abstime", "_date", "_time", "_timestamp", "_numeric", + "_bytea" }; /** @@ -316,12 +319,15 @@ public class Connection extends org.postgresql.Connection implements java.sql.Co Types.DOUBLE, Types.CHAR,Types.CHAR,Types.CHAR,Types.CHAR,Types.CHAR,Types.CHAR, Types.VARCHAR,Types.VARCHAR,Types.VARCHAR,Types.VARCHAR, + Types.BINARY, Types.BIT, Types.DATE, Types.TIME, Types.TIMESTAMP,Types.TIMESTAMP, - Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, - Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY + Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, + Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, + Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, + Types.ARRAY }; diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/PreparedStatement.java b/src/interfaces/jdbc/org/postgresql/jdbc2/PreparedStatement.java index aabb4923097..16b07ab5b66 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/PreparedStatement.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/PreparedStatement.java @@ -91,7 +91,7 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta * A Prepared SQL query is executed and its ResultSet is returned * * @return a ResultSet that contains the data produced by the - * query - never null + * * query - never null * @exception SQLException if a database access error occurs */ public java.sql.ResultSet executeQuery() throws SQLException @@ -105,7 +105,7 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta * be executed. * * @return either the row count for INSERT, UPDATE or DELETE; or - * 0 for SQL statements that return nothing. + * * 0 for SQL statements that return nothing. * @exception SQLException if a database access error occurs */ public int executeUpdate() throws SQLException @@ -305,12 +305,22 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta */ public void setBytes(int parameterIndex, byte x[]) throws SQLException { + if (connection.haveMinimumCompatibleVersion("7.2")) { + //Version 7.2 supports the bytea datatype for byte arrays + if(null == x){ + setNull(parameterIndex,Types.OTHER); + } else { + setString(parameterIndex, PGbytea.toPGString(x)); + } + } else { + //Version 7.1 and earlier support done as LargeObjects LargeObjectManager lom = connection.getLargeObjectAPI(); int oid = lom.create(); LargeObject lob = lom.open(oid); lob.write(x); lob.close(); setInt(parameterIndex,oid); + } } /** @@ -413,8 +423,29 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta */ public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { + 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)); + } catch (UnsupportedEncodingException l_uee) { + throw new PSQLException("postgresql.unusual",l_uee); + } catch (IOException l_ioe) { + throw new PSQLException("postgresql.unusual",l_ioe); + } + } else { + //Version 7.1 supported only LargeObjects by treating everything + //as binary data setBinaryStream(parameterIndex, x, length); } + } /** * When a very large Unicode value is input to a LONGVARCHAR parameter, @@ -436,8 +467,29 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta */ public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { + 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)); + } catch (UnsupportedEncodingException l_uee) { + throw new PSQLException("postgresql.unusual",l_uee); + } catch (IOException l_ioe) { + throw new PSQLException("postgresql.unusual",l_ioe); + } + } else { + //Version 7.1 supported only LargeObjects by treating everything + //as binary data setBinaryStream(parameterIndex, x, length); } + } /** * When a very large binary value is input to a LONGVARBINARY parameter, @@ -455,6 +507,32 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta */ public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { + if (connection.haveMinimumCompatibleVersion("7.2")) { + //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 + //long binary datatype, but with toast the bytea datatype is capable of + //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; + try { + l_bytesRead = x.read(l_bytes,0,length); + } catch (IOException l_ioe) { + throw new PSQLException("postgresql.unusual",l_ioe); + } + if (l_bytesRead == length) { + setBytes(parameterIndex, l_bytes); + } else { + //the stream contained less data than they said + byte[] l_bytes2 = new byte[l_bytesRead]; + System.arraycopy(l_bytes,0,l_bytes2,0,l_bytesRead); + setBytes(parameterIndex, l_bytes2); + } + } else { + //Version 7.1 only supported streams for LargeObjects + //but the jdbc spec indicates that streams should be + //available for LONGVARBINARY instead LargeObjectManager lom = connection.getLargeObjectAPI(); int oid = lom.create(); LargeObject lob = lom.open(oid); @@ -472,11 +550,12 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta } los.close(); } catch(IOException se) { - throw new PSQLException("postgresql.prep.is",se); + throw new PSQLException("postgresql.unusual",se); } // lob is closed by the stream so don't call lob.close() setInt(parameterIndex,oid); } + } /** * In general, parameter values remain in force for repeated used of a @@ -728,11 +807,33 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta } /** - * Sets a Blob - basically its similar to setBinaryStream() + * Sets a Blob */ public void setBlob(int i,Blob x) throws SQLException { - setBinaryStream(i,x.getBinaryStream(),(int)x.length()); + InputStream l_inStream = x.getBinaryStream(); + int l_length = (int) x.length(); + LargeObjectManager lom = connection.getLargeObjectAPI(); + int oid = lom.create(); + LargeObject lob = lom.open(oid); + OutputStream los = lob.getOutputStream(); + try { + // could be buffered, but then the OutputStream returned by LargeObject + // is buffered internally anyhow, so there would be no performance + // boost gained, if anything it would be worse! + int c=l_inStream.read(); + int p=0; + while(c>-1 && p-1 && p fields.length) throw new PSQLException("postgresql.res.colrange"); - wasNullFlag = (this_row[columnIndex - 1] == null); + if (connection.haveMinimumCompatibleVersion("7.2")) { + //Version 7.2 supports the bytea datatype for byte arrays + return PGbytea.toBytes(getString(columnIndex)); + } else { + //Version 7.1 and earlier supports LargeObjects for byte arrays + wasNullFlag = (this_row[columnIndex - 1] == null); // Handle OID's as BLOBS - if(!wasNullFlag) + if(!wasNullFlag) { if( fields[columnIndex - 1].getOID() == 26) { LargeObjectManager lom = connection.getLargeObjectAPI(); LargeObject lob = lom.open(getInt(columnIndex)); @@ -323,8 +328,9 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu lob.close(); return buf; } - - return this_row[columnIndex - 1]; + } + } + return null; } /** @@ -392,8 +398,27 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu */ public InputStream getAsciiStream(int columnIndex) throws SQLException { + wasNullFlag = (this_row[columnIndex - 1] == null); + if (wasNullFlag) + return null; + + if (connection.haveMinimumCompatibleVersion("7.2")) { + //Version 7.2 supports AsciiStream for all the PG text types + //As the spec/javadoc for this method indicate this is to be used for + //large text values (i.e. LONGVARCHAR) PG doesn't have a separate + //long string datatype, but with toast the text datatype is capable of + //handling very large values. Thus the implementation ends up calling + //getString() since there is no current way to stream the value from the server + try { + return new ByteArrayInputStream(getString(columnIndex).getBytes("ASCII")); + } catch (UnsupportedEncodingException l_uee) { + throw new PSQLException("postgresql.unusual", l_uee); + } + } else { + // In 7.1 Handle as BLOBS so return the LargeObject input stream return getBinaryStream(columnIndex); } + } /** * A column value can also be retrieved as a stream of Unicode @@ -412,8 +437,27 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu */ public InputStream getUnicodeStream(int columnIndex) throws SQLException { + wasNullFlag = (this_row[columnIndex - 1] == null); + if (wasNullFlag) + return null; + + if (connection.haveMinimumCompatibleVersion("7.2")) { + //Version 7.2 supports AsciiStream for all the PG text types + //As the spec/javadoc for this method indicate this is to be used for + //large text values (i.e. LONGVARCHAR) PG doesn't have a separate + //long string datatype, but with toast the text datatype is capable of + //handling very large values. Thus the implementation ends up calling + //getString() since there is no current way to stream the value from the server + try { + return new ByteArrayInputStream(getString(columnIndex).getBytes("UTF-8")); + } catch (UnsupportedEncodingException l_uee) { + throw new PSQLException("postgresql.unusual", l_uee); + } + } else { + // In 7.1 Handle as BLOBS so return the LargeObject input stream return getBinaryStream(columnIndex); } + } /** * A column value can also be retrieved as a binary strea. This @@ -429,20 +473,29 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu */ public InputStream getBinaryStream(int columnIndex) throws SQLException { - // New in 7.1 Handle OID's as BLOBS so return the input stream - if(!wasNullFlag) + wasNullFlag = (this_row[columnIndex - 1] == null); + if (wasNullFlag) + return null; + + if (connection.haveMinimumCompatibleVersion("7.2")) { + //Version 7.2 supports BinaryStream for all 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 + //long binary datatype, but with toast the bytea datatype is capable of + //handling very large values. Thus the implementation ends up calling + //getBytes() since there is no current way to stream the value from the server + byte b[] = getBytes(columnIndex); + if (b != null) + return new ByteArrayInputStream(b); + } else { + // In 7.1 Handle as BLOBS so return the LargeObject input stream if( fields[columnIndex - 1].getOID() == 26) { LargeObjectManager lom = connection.getLargeObjectAPI(); LargeObject lob = lom.open(getInt(columnIndex)); return lob.getInputStream(); } - - // Not an OID so fake the stream - byte b[] = getBytes(columnIndex); - - if (b != null) - return new ByteArrayInputStream(b); - return null; // SQL NULL + } + return null; } /** @@ -731,7 +784,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu //if index<0, count from the end of the result set, but check //to be sure that it is not beyond the first index if (index<0) - if (index > -rows_size) + if (index >= -rows_size) internalIndex = rows_size+index; else { beforeFirst(); @@ -794,6 +847,10 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu public java.sql.Array getArray(int i) throws SQLException { + wasNullFlag = (this_row[i - 1] == null); + if(wasNullFlag) + return null; + if (i < 1 || i > fields.length) throw new PSQLException("postgresql.res.colrange"); return (java.sql.Array) new org.postgresql.jdbc2.Array( connection, i, fields[i-1], this ); @@ -826,10 +883,25 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu public java.io.Reader getCharacterStream(int i) throws SQLException { + wasNullFlag = (this_row[i - 1] == null); + if (wasNullFlag) + return null; + + if (connection.haveMinimumCompatibleVersion("7.2")) { + //Version 7.2 supports AsciiStream for all the PG text types + //As the spec/javadoc for this method indicate this is to be used for + //large text values (i.e. LONGVARCHAR) PG doesn't have a separate + //long string datatype, but with toast the text datatype is capable of + //handling very large values. Thus the implementation ends up calling + //getString() since there is no current way to stream the value from the server + return new CharArrayReader(getString(i).toCharArray()); + } else { + // In 7.1 Handle as BLOBS so return the LargeObject input stream Encoding encoding = connection.getEncoding(); InputStream input = getBinaryStream(i); return encoding.getDecodingReader(input); } + } /** * New in 7.1 @@ -1485,4 +1557,7 @@ public class ResultSet extends org.postgresql.ResultSet implements java.sql.Resu } } } + + } + diff --git a/src/interfaces/jdbc/org/postgresql/util/PGbytea.java b/src/interfaces/jdbc/org/postgresql/util/PGbytea.java new file mode 100644 index 00000000000..e994fce5bcd --- /dev/null +++ b/src/interfaces/jdbc/org/postgresql/util/PGbytea.java @@ -0,0 +1,86 @@ +package org.postgresql.util; + +import java.sql.*; + +/** + * Converts to and from the postgresql bytea datatype used by the backend. + * + * $Id: PGbytea.java,v 1.1 2001/09/10 15:07:05 momjian Exp $ + */ + +public class PGbytea { + + /** + * Converts a PG bytea string (i.e. the text representation + * of the bytea data type) into a java byte[] + */ + public static byte[] toBytes(String s) throws SQLException { + if(s==null) + return null; + int slength = s.length(); + byte[] buf = new byte[slength]; + int bufpos = 0; + int thebyte; + char nextchar; + char secondchar; + for (int i = 0; i < slength; i++) { + nextchar = s.charAt(i); + if (nextchar == '\\') { + secondchar = s.charAt(++i); + if (secondchar == '\\') { + //escaped \ + buf[bufpos++] = (byte)'\\'; + } else { + thebyte = (secondchar-48)*64 + (s.charAt(++i)-48)*8 + (s.charAt(++i)-48); + if (thebyte > 127) + thebyte -= 256; + buf[bufpos++] = (byte)thebyte; + } + } else { + buf[bufpos++] = (byte)nextchar; + } + } + byte[] l_return = new byte[bufpos]; + System.arraycopy(buf,0,l_return,0,bufpos); + return l_return; + } + + /** + * Converts a java byte[] into a PG bytea string (i.e. the text + * representation of the bytea data type) + */ + public static String toPGString(byte[] p_buf) throws SQLException + { + if(p_buf==null) + return null; + StringBuffer l_strbuf = new StringBuffer(); + for (int i = 0; i < p_buf.length; i++) { + int l_int = (int)p_buf[i]; + if (l_int < 0) { + l_int = 256 + l_int; + } + //we escape the same non-printable characters as the backend + //we must escape all 8bit characters otherwise when convering + //from java unicode to the db character set we may end up with + //question marks if the character set is SQL_ASCII + if (l_int < 040 || l_int > 0176) { + //escape charcter with the form \000, but need two \\ because of + //the parser + l_strbuf.append("\\"); + l_strbuf.append((char)(((l_int >> 6) & 0x3)+48)); + l_strbuf.append((char)(((l_int >> 3) & 0x7)+48)); + l_strbuf.append((char)((l_int & 0x07)+48)); + } else if (p_buf[i] == (byte)'\\') { + //escape the backslash character as \\, but need four \\\\ because + //of the parser + l_strbuf.append("\\\\"); + } else { + //other characters are left alone + l_strbuf.append((char)p_buf[i]); + } + } + return l_strbuf.toString(); + } + + +}