Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit ec0ad67

Browse files
committed
Attached is a patch to add bytea support to JDBC.
This patch does the following: - Adds binary datatype support (bytea) - Changes getXXXStream()/setXXXStream() methods to be spec compliant - Adds ability to revert to old behavior Details: Adds support for the binary type bytea. The ResultSet.getBytes() and PreparedStatement.setBytes() methods now work against columns of bytea type. This is a change in behavior from the previous code which assumed the column type was OID and thus a LargeObject. The new behavior is more complient with the JDBC spec as BLOB/CLOB are to be used for LargeObjects and the getBytes()/setBytes() methods are for the databases binary datatype (which is bytea in postgres). Changes the behavior of the getBinaryStream(), getAsciiStream(), getCharacterStream(), getUnicodeStream() and their setXXXStream() counterparts. These methos now work against either the bytea type (BinaryStream) or the text types (AsciiStream, CharacterStream, UnicodeStream). The previous behavior was that these all assumed the underlying column was of type OID and thus a LargeObject. The spec/javadoc for these methods indicate that they are for LONGVARCHAR and LONGVARBINARY datatypes, which are distinct from the BLOB/CLOB datatypes. Given that the bytea and text types support upto 1G, they are the LONGVARBINARY and LONGVARCHAR datatypes in postgres. Added support for turning off the above new functionality. Given that the changes above are not backwardly compatible (however they are more spec complient), I added the ability to revert back to the old behavior. The Connection now takes an optional parameter named 'compatible'. If the value of '7.1' is passed, the driver reverts to the 7.1 behavior. If the parameter is not passed or the value '7.2' is passed the behavior is the new behavior. The mechanism put in place can be used in the future when/if similar needs arise to change behavior. This is patterned after how Oracle does this (i.e. Oracle has a 'compatible' parameter that behaves in a similar manner). Misc fixes. Cleaned up a few things I encountered along the way. Note that in testing the patch I needed to ignore whitespace differences in order to get it to apply cleanly (i.e. patch -l -i byteapatch.diff). Also this patch introduces a new file (src/interfaces/jdbc/org/postgresql/util/PGbytea.java). Barry Lind
1 parent 6b50f9a commit ec0ad67

File tree

10 files changed

+557
-49
lines changed

10 files changed

+557
-49
lines changed

src/interfaces/jdbc/org/postgresql/Connection.java

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import org.postgresql.core.*;
1212

1313
/**
14-
* $Id: Connection.java,v 1.28 2001/09/07 22:17:02 momjian Exp $
14+
* $Id: Connection.java,v 1.29 2001/09/10 15:07:05 momjian Exp $
1515
*
1616
* This abstract class is used by org.postgresql.Driver to open either the JDBC1 or
1717
* JDBC2 versions of the Connection class.
@@ -22,15 +22,13 @@ public abstract class Connection
2222
// This is the network stream associated with this connection
2323
public PG_Stream pg_stream;
2424

25-
// This is set by org.postgresql.Statement.setMaxRows()
26-
//public int maxrows = 0; // maximum no. of rows; 0 = unlimited
27-
2825
private String PG_HOST;
2926
private int PG_PORT;
3027
private String PG_USER;
3128
private String PG_PASSWORD;
3229
private String PG_DATABASE;
3330
private boolean PG_STATUS;
31+
private String compatible;
3432

3533
/**
3634
* The encoding to use for this connection.
@@ -123,6 +121,11 @@ protected void openConnection(String host, int port, Properties info, String dat
123121
PG_PORT = port;
124122
PG_HOST = host;
125123
PG_STATUS = CONNECTION_BAD;
124+
if(info.getProperty("compatible")==null) {
125+
compatible = d.getMajorVersion() + "." + d.getMinorVersion();
126+
} else {
127+
compatible = info.getProperty("compatible");
128+
}
126129

127130
// Now make the initial connection
128131
try
@@ -968,6 +971,23 @@ public boolean haveMinimumServerVersion(String ver) throws SQLException
968971
return (getDBVersionNumber().compareTo(ver) >= 0);
969972
}
970973

974+
/**
975+
* This method returns true if the compatible level set in the connection
976+
* (which can be passed into the connection or specified in the URL)
977+
* is at least the value passed to this method. This is used to toggle
978+
* between different functionality as it changes across different releases
979+
* of the jdbc driver code. The values here are versions of the jdbc client
980+
* and not server versions. For example in 7.1 get/setBytes worked on
981+
* LargeObject values, in 7.2 these methods were changed to work on bytea
982+
* values. This change in functionality could be disabled by setting the
983+
* "compatible" level to be 7.1, in which case the driver will revert to
984+
* the 7.1 functionality.
985+
*/
986+
public boolean haveMinimumCompatibleVersion(String ver) throws SQLException
987+
{
988+
return (compatible.compareTo(ver) >= 0);
989+
}
990+
971991

972992
/**
973993
* This returns the java.sql.Types type for a PG type oid

src/interfaces/jdbc/org/postgresql/Driver.java.in

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,26 @@ public class Driver implements java.sql.Driver
8585
* database.
8686
*
8787
* <p>The java.util.Properties argument can be used to pass arbitrary
88-
* string tag/value pairs as connection arguments. Normally, at least
88+
* string tag/value pairs as connection arguments.
89+
*
90+
* user - (optional) The user to connect as
91+
* password - (optional) The password for the user
92+
* charSet - (optional) The character set to be used for converting
93+
* to/from the database to unicode. If multibyte is enabled on the
94+
* server then the character set of the database is used as the default,
95+
* otherwise the jvm character encoding is used as the default.
96+
* compatible - This is used to toggle
97+
* between different functionality as it changes across different releases
98+
* of the jdbc driver code. The values here are versions of the jdbc
99+
* client and not server versions. For example in 7.1 get/setBytes
100+
* worked on LargeObject values, in 7.2 these methods were changed
101+
* to work on bytea values. This change in functionality could
102+
* be disabled by setting the compatible level to be "7.1", in
103+
* which case the driver will revert to the 7.1 functionality.
104+
*
105+
* <p>Normally, at least
89106
* "user" and "password" properties should be included in the
90-
* properties. In addition, the "charSet" property can be used to
91-
* set a character set encoding (e.g. "utf-8") other than the platform
92-
* default (typically Latin1). This is necessary in particular if storing
93-
* multibyte characters in the database. For a list of supported
107+
* properties. For a list of supported
94108
* character encoding , see
95109
* http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html
96110
* Note that you will probably want to have set up the Postgres database

src/interfaces/jdbc/org/postgresql/ResultSet.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,8 @@ public String getFixedString(int col) throws SQLException {
192192
String s = getString(col);
193193

194194
// Handle SQL Null
195-
if(s==null)
195+
wasNullFlag = (this_row[col - 1] == null);
196+
if(wasNullFlag)
196197
return null;
197198

198199
// Handle Money

src/interfaces/jdbc/org/postgresql/jdbc1/Connection.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import org.postgresql.util.*;
1818

1919
/**
20-
* $Id: Connection.java,v 1.9 2001/09/06 03:13:34 momjian Exp $
20+
* $Id: Connection.java,v 1.10 2001/09/10 15:07:05 momjian Exp $
2121
*
2222
* A Connection represents a session with a specific database. Within the
2323
* context of a Connection, SQL statements are executed and results are
@@ -174,6 +174,7 @@ public int getSQLType(String pgTypeName)
174174
"float8",
175175
"bpchar","char","char2","char4","char8","char16",
176176
"varchar","text","name","filename",
177+
"bytea",
177178
"bool",
178179
"date",
179180
"time",
@@ -197,6 +198,7 @@ public int getSQLType(String pgTypeName)
197198
Types.DOUBLE,
198199
Types.CHAR,Types.CHAR,Types.CHAR,Types.CHAR,Types.CHAR,Types.CHAR,
199200
Types.VARCHAR,Types.VARCHAR,Types.VARCHAR,Types.VARCHAR,
201+
Types.BINARY,
200202
Types.BIT,
201203
Types.DATE,
202204
Types.TIME,

src/interfaces/jdbc/org/postgresql/jdbc1/PreparedStatement.java

Lines changed: 105 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public PreparedStatement(Connection connection, String sql) throws SQLException
8282
* A Prepared SQL query is executed and its ResultSet is returned
8383
*
8484
* @return a ResultSet that contains the data produced by the
85-
* query - never null
85+
* * query - never null
8686
* @exception SQLException if a database access error occurs
8787
*/
8888
public java.sql.ResultSet executeQuery() throws SQLException
@@ -107,7 +107,7 @@ public java.sql.ResultSet executeQuery() throws SQLException
107107
* be executed.
108108
*
109109
* @return either the row count for INSERT, UPDATE or DELETE; or
110-
* 0 for SQL statements that return nothing.
110+
* * 0 for SQL statements that return nothing.
111111
* @exception SQLException if a database access error occurs
112112
*/
113113
public int executeUpdate() throws SQLException
@@ -294,12 +294,22 @@ public void setString(int parameterIndex, String x) throws SQLException
294294
*/
295295
public void setBytes(int parameterIndex, byte x[]) throws SQLException
296296
{
297+
if (connection.haveMinimumCompatibleVersion("7.2")) {
298+
//Version 7.2 supports the bytea datatype for byte arrays
299+
if(null == x){
300+
setNull(parameterIndex,Types.OTHER);
301+
} else {
302+
setString(parameterIndex, PGbytea.toPGString(x));
303+
}
304+
} else {
305+
//Version 7.1 and earlier support done as LargeObjects
297306
LargeObjectManager lom = connection.getLargeObjectAPI();
298307
int oid = lom.create();
299308
LargeObject lob = lom.open(oid);
300309
lob.write(x);
301310
lob.close();
302311
setInt(parameterIndex,oid);
312+
}
303313
}
304314

305315
/**
@@ -386,8 +396,29 @@ public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException
386396
*/
387397
public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException
388398
{
399+
if (connection.haveMinimumCompatibleVersion("7.2")) {
400+
//Version 7.2 supports AsciiStream for all PG text types (char, varchar, text)
401+
//As the spec/javadoc for this method indicate this is to be used for
402+
//large String values (i.e. LONGVARCHAR) PG doesn't have a separate
403+
//long varchar datatype, but with toast all text datatypes are capable of
404+
//handling very large values. Thus the implementation ends up calling
405+
//setString() since there is no current way to stream the value to the server
406+
try {
407+
InputStreamReader l_inStream = new InputStreamReader(x, "ASCII");
408+
char[] l_chars = new char[length];
409+
int l_charsRead = l_inStream.read(l_chars,0,length);
410+
setString(parameterIndex, new String(l_chars,0,l_charsRead));
411+
} catch (UnsupportedEncodingException l_uee) {
412+
throw new PSQLException("postgresql.unusual",l_uee);
413+
} catch (IOException l_ioe) {
414+
throw new PSQLException("postgresql.unusual",l_ioe);
415+
}
416+
} else {
417+
//Version 7.1 supported only LargeObjects by treating everything
418+
//as binary data
389419
setBinaryStream(parameterIndex, x, length);
390420
}
421+
}
391422

392423
/**
393424
* When a very large Unicode value is input to a LONGVARCHAR parameter,
@@ -406,8 +437,29 @@ public void setAsciiStream(int parameterIndex, InputStream x, int length) throws
406437
*/
407438
public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException
408439
{
440+
if (connection.haveMinimumCompatibleVersion("7.2")) {
441+
//Version 7.2 supports AsciiStream for all PG text types (char, varchar, text)
442+
//As the spec/javadoc for this method indicate this is to be used for
443+
//large String values (i.e. LONGVARCHAR) PG doesn't have a separate
444+
//long varchar datatype, but with toast all text datatypes are capable of
445+
//handling very large values. Thus the implementation ends up calling
446+
//setString() since there is no current way to stream the value to the server
447+
try {
448+
InputStreamReader l_inStream = new InputStreamReader(x, "UTF-8");
449+
char[] l_chars = new char[length];
450+
int l_charsRead = l_inStream.read(l_chars,0,length);
451+
setString(parameterIndex, new String(l_chars,0,l_charsRead));
452+
} catch (UnsupportedEncodingException l_uee) {
453+
throw new PSQLException("postgresql.unusual",l_uee);
454+
} catch (IOException l_ioe) {
455+
throw new PSQLException("postgresql.unusual",l_ioe);
456+
}
457+
} else {
458+
//Version 7.1 supported only LargeObjects by treating everything
459+
//as binary data
409460
setBinaryStream(parameterIndex, x, length);
410461
}
462+
}
411463

412464
/**
413465
* When a very large binary value is input to a LONGVARBINARY parameter,
@@ -425,7 +477,54 @@ public void setUnicodeStream(int parameterIndex, InputStream x, int length) thro
425477
*/
426478
public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException
427479
{
428-
throw org.postgresql.Driver.notImplemented();
480+
if (connection.haveMinimumCompatibleVersion("7.2")) {
481+
//Version 7.2 supports BinaryStream for for the PG bytea type
482+
//As the spec/javadoc for this method indicate this is to be used for
483+
//large binary values (i.e. LONGVARBINARY) PG doesn't have a separate
484+
//long binary datatype, but with toast the bytea datatype is capable of
485+
//handling very large values. Thus the implementation ends up calling
486+
//setBytes() since there is no current way to stream the value to the server
487+
byte[] l_bytes = new byte[length];
488+
int l_bytesRead;
489+
try {
490+
l_bytesRead = x.read(l_bytes,0,length);
491+
} catch (IOException l_ioe) {
492+
throw new PSQLException("postgresql.unusual",l_ioe);
493+
}
494+
if (l_bytesRead == length) {
495+
setBytes(parameterIndex, l_bytes);
496+
} else {
497+
//the stream contained less data than they said
498+
byte[] l_bytes2 = new byte[l_bytesRead];
499+
System.arraycopy(l_bytes,0,l_bytes2,0,l_bytesRead);
500+
setBytes(parameterIndex, l_bytes2);
501+
}
502+
} else {
503+
//Version 7.1 only supported streams for LargeObjects
504+
//but the jdbc spec indicates that streams should be
505+
//available for LONGVARBINARY instead
506+
LargeObjectManager lom = connection.getLargeObjectAPI();
507+
int oid = lom.create();
508+
LargeObject lob = lom.open(oid);
509+
OutputStream los = lob.getOutputStream();
510+
try {
511+
// could be buffered, but then the OutputStream returned by LargeObject
512+
// is buffered internally anyhow, so there would be no performance
513+
// boost gained, if anything it would be worse!
514+
int c=x.read();
515+
int p=0;
516+
while(c>-1 && p<length) {
517+
los.write(c);
518+
c=x.read();
519+
p++;
520+
}
521+
los.close();
522+
} catch(IOException se) {
523+
throw new PSQLException("postgresql.unusual",se);
524+
}
525+
// lob is closed by the stream so don't call lob.close()
526+
setInt(parameterIndex,oid);
527+
}
429528
}
430529

431530
/**
@@ -460,8 +559,8 @@ public void clearParameters() throws SQLException
460559
* @param x the object containing the input parameter value
461560
* @param targetSqlType The SQL type to be send to the database
462561
* @param scale For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC
463-
* types this is the number of digits after the decimal. For
464-
* all other types this value will be ignored.
562+
* * types this is the number of digits after the decimal. For
563+
* * all other types this value will be ignored.
465564
* @exception SQLException if a database access error occurs
466565
*/
467566
public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException
@@ -572,7 +671,7 @@ else if (x instanceof PGobject)
572671
* statements handled by executeQuery and executeUpdate
573672
*
574673
* @return true if the next result is a ResultSet; false if it is an
575-
* update count or there are no more results
674+
* * update count or there are no more results
576675
* @exception SQLException if a database access error occurs
577676
*/
578677
public boolean execute() throws SQLException

0 commit comments

Comments
 (0)