Patch to add bytea support to JDBC - Mailing list pgsql-jdbc

From Barry Lind
Subject Patch to add bytea support to JDBC
Date
Msg-id 3B9B217C.9070904@xythos.com
Whole thread Raw
Responses Re: Patch to add bytea support to JDBC  ("Thomas O'Dowd" <tom@nooper.com>)
List pgsql-jdbc
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).


thanks,
--Barry

PS.  In case this patch gets caught in the email list approvals because
it is greater than 40K in size, I have CCed Bruce so that he knows this
is coming before he does the beta builds.
*** ./src/interfaces/jdbc/org/postgresql/Connection.java.orig    Sat Sep  8 23:26:04 2001
--- ./src/interfaces/jdbc/org/postgresql/Connection.java    Sat Sep  8 23:21:48 2001
***************
*** 22,36 ****
    // 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;

    /**
     *  The encoding to use for this connection.
--- 22,34 ----
    // This is the network stream associated with this connection
    public PG_Stream pg_stream;

    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,128 ****
--- 121,131 ----
      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
***************
*** 966,971 ****
--- 969,991 ----
    public boolean haveMinimumServerVersion(String ver) throws SQLException
    {
        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);
    }


*** ./src/interfaces/jdbc/org/postgresql/Driver.java.in.orig    Sat Sep  8 23:09:58 2001
--- ./src/interfaces/jdbc/org/postgresql/Driver.java.in    Sat Sep  8 23:08:04 2001
***************
*** 85,96 ****
     * database.
     *
     * <p>The java.util.Properties argument can be used to pass arbitrary
!    * string tag/value pairs as connection arguments.  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
     * 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
--- 85,110 ----
     * database.
     *
     * <p>The java.util.Properties argument can be used to pass arbitrary
!    * 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.
!    *
!    * <p>Normally, at least
     * "user" and "password" properties should be included in the
!    * 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
*** ./src/interfaces/jdbc/org/postgresql/jdbc1/Connection.java.orig    Sat Sep  8 23:55:32 2001
--- ./src/interfaces/jdbc/org/postgresql/jdbc1/Connection.java    Sat Sep  8 23:57:32 2001
***************
*** 174,179 ****
--- 174,180 ----
      "float8",
      "bpchar","char","char2","char4","char8","char16",
      "varchar","text","name","filename",
+     "bytea",
      "bool",
      "date",
      "time",
***************
*** 197,202 ****
--- 198,204 ----
      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,
*** ./src/interfaces/jdbc/org/postgresql/jdbc1/PreparedStatement.java.orig    Sun Sep  9 00:01:02 2001
--- ./src/interfaces/jdbc/org/postgresql/jdbc1/PreparedStatement.java    Sat Sep  8 14:37:14 2001
***************
*** 82,88 ****
       * A Prepared SQL query is executed and its ResultSet is returned
       *
       * @return a ResultSet that contains the data produced by the
!      *    query - never null
       * @exception SQLException if a database access error occurs
       */
      public java.sql.ResultSet executeQuery() throws SQLException
--- 82,88 ----
           * A Prepared SQL query is executed and its ResultSet is returned
           *
           * @return a ResultSet that contains the data produced by the
!      *          *    query - never null
           * @exception SQLException if a database access error occurs
           */
          public java.sql.ResultSet executeQuery() throws SQLException
***************
*** 107,113 ****
       * be executed.
       *
       * @return either the row count for INSERT, UPDATE or DELETE; or
!      *     0 for SQL statements that return nothing.
       * @exception SQLException if a database access error occurs
       */
      public int executeUpdate() throws SQLException
--- 107,113 ----
           * be executed.
           *
           * @return either the row count for INSERT, UPDATE or DELETE; or
!      *          *     0 for SQL statements that return nothing.
           * @exception SQLException if a database access error occurs
           */
          public int executeUpdate() throws SQLException
***************
*** 294,299 ****
--- 294,308 ----
     */
    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);
***************
*** 301,306 ****
--- 310,316 ----
        lob.close();
        setInt(parameterIndex,oid);
      }
+   }

          /**
           * Set a parameter to a java.sql.Date value.  The driver converts this
***************
*** 386,393 ****
--- 396,424 ----
           */
          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,413 ****
--- 437,465 ----
           */
          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,431 ****
       */
      public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException
      {
!         throw org.postgresql.Driver.notImplemented();
      }

      /**
--- 477,530 ----
           */
          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);
!             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<length) {
!                 los.write(c);
!                 c=x.read();
!                 p++;
!               }
!               los.close();
!             } catch(IOException se) {
!               throw new PSQLException("postgresql.unusual",se);
!             }
!             // lob is closed by the stream so don't call lob.close()
!             setInt(parameterIndex,oid);
!           }
          }

          /**
***************
*** 460,467 ****
       * @param x the object containing the input parameter value
       * @param targetSqlType The SQL type to be send to the database
       * @param scale For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC
!      *    types this is the number of digits after the decimal.  For
!      *    all other types this value will be ignored.
       * @exception SQLException if a database access error occurs
       */
      public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException
--- 559,566 ----
           * @param x the object containing the input parameter value
           * @param targetSqlType The SQL type to be send to the database
           * @param scale For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC
!      *          *    types this is the number of digits after the decimal.  For
!      *          *    all other types this value will be ignored.
           * @exception SQLException if a database access error occurs
           */
          public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException
***************
*** 572,578 ****
       * statements handled by executeQuery and executeUpdate
       *
       * @return true if the next result is a ResultSet; false if it is an
!      *    update count or there are no more results
       * @exception SQLException if a database access error occurs
       */
      public boolean execute() throws SQLException
--- 671,677 ----
           * statements handled by executeQuery and executeUpdate
           *
           * @return true if the next result is a ResultSet; false if it is an
!      *          *    update count or there are no more results
           * @exception SQLException if a database access error occurs
           */
          public boolean execute() throws SQLException
*** ./src/interfaces/jdbc/org/postgresql/jdbc1/ResultSet.java.orig    Sat Sep  8 23:58:31 2001
--- ./src/interfaces/jdbc/org/postgresql/jdbc1/ResultSet.java    Sat Sep  8 22:47:01 2001
***************
*** 374,383 ****
    {
      if (columnIndex < 1 || columnIndex > fields.length)
        throw new PSQLException("postgresql.res.colrange");
-     wasNullFlag = (this_row[columnIndex - 1] == null);

      // Handle OID's as BLOBS
!     if(!wasNullFlag)
        if( fields[columnIndex - 1].getOID() == 26) {
      LargeObjectManager lom = connection.getLargeObjectAPI();
      LargeObject lob = lom.open(getInt(columnIndex));
--- 374,388 ----
    {
      if (columnIndex < 1 || columnIndex > fields.length)
        throw new PSQLException("postgresql.res.colrange");

+     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( fields[columnIndex - 1].getOID() == 26) {
            LargeObjectManager lom = connection.getLargeObjectAPI();
            LargeObject lob = lom.open(getInt(columnIndex));
***************
*** 385,392 ****
      lob.close();
      return buf;
        }
!
!     return this_row[columnIndex - 1];
    }

    /**
--- 390,398 ----
            lob.close();
            return buf;
          }
!       }
!     }
!     return null;
    }

    /**
***************
*** 545,552 ****
--- 551,577 ----
     */
    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,569 ****
--- 587,613 ----
     */
    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,589 ****
     */
    public InputStream getBinaryStream(int columnIndex) throws SQLException
    {
!     byte b[] = getBytes(columnIndex);

      if (b != null)
        return new ByteArrayInputStream(b);
!     return null;        // SQL NULL
    }

    /**
--- 623,651 ----
     */
    public InputStream getBinaryStream(int columnIndex) throws SQLException
    {
!     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();
!       }
!     }
!     return null;
    }

    /**
*** ./src/interfaces/jdbc/org/postgresql/jdbc2/Connection.java.orig    Sat Sep  8 23:29:16 2001
--- ./src/interfaces/jdbc/org/postgresql/jdbc2/Connection.java    Sat Sep  8 23:33:29 2001
***************
*** 291,302 ****
      "float8",
      "bpchar","char","char2","char4","char8","char16",
      "varchar","text","name","filename",
      "bool",
      "date",
      "time",
      "abstime","timestamp",
!     "_bool", "_char", "_int2", "_int4", "_text", "_oid", "_varchar", "_int8",
!     "_float4", "_float8", "_abstime", "_date", "_time", "_timestamp", "_numeric"
    };

    /**
--- 291,305 ----
      "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",
!     "_bytea"
    };

    /**
***************
*** 316,327 ****
      Types.DOUBLE,
      Types.CHAR,Types.CHAR,Types.CHAR,Types.CHAR,Types.CHAR,Types.CHAR,
      Types.VARCHAR,Types.VARCHAR,Types.VARCHAR,Types.VARCHAR,
      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
    };


--- 319,333 ----
      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
    };


*** ./src/interfaces/jdbc/org/postgresql/jdbc2/PreparedStatement.java.orig    Sat Sep  8 23:35:02 2001
--- ./src/interfaces/jdbc/org/postgresql/jdbc2/PreparedStatement.java    Sat Sep  8 14:21:29 2001
***************
*** 91,97 ****
       * A Prepared SQL query is executed and its ResultSet is returned
       *
       * @return a ResultSet that contains the data produced by the
!      *    query - never null
       * @exception SQLException if a database access error occurs
       */
      public java.sql.ResultSet executeQuery() throws SQLException
--- 91,97 ----
           * A Prepared SQL query is executed and its ResultSet is returned
           *
           * @return a ResultSet that contains the data produced by the
!          *             *         query - never null
           * @exception SQLException if a database access error occurs
           */
          public java.sql.ResultSet executeQuery() throws SQLException
***************
*** 105,111 ****
       * be executed.
       *
       * @return either the row count for INSERT, UPDATE or DELETE; or
!      *     0 for SQL statements that return nothing.
       * @exception SQLException if a database access error occurs
       */
      public int executeUpdate() throws SQLException
--- 105,111 ----
           * be executed.
           *
           * @return either the row count for INSERT, UPDATE or DELETE; or
!          *             *         0 for SQL statements that return nothing.
           * @exception SQLException if a database access error occurs
           */
          public int executeUpdate() throws SQLException
***************
*** 305,310 ****
--- 305,319 ----
     */
    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);
***************
*** 312,317 ****
--- 321,327 ----
        lob.close();
        setInt(parameterIndex,oid);
      }
+   }

          /**
           * Set a parameter to a java.sql.Date value.  The driver converts this
***************
*** 413,420 ****
--- 423,451 ----
           */
          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,443 ****
--- 467,495 ----
           */
          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,460 ****
--- 507,538 ----
           */
          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,482 ****
              }
              los.close();
            } catch(IOException se) {
!             throw new PSQLException("postgresql.prep.is",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
--- 550,561 ----
                }
                los.close();
              } catch(IOException 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,738 ****
      }

      /**
!      * Sets a Blob - basically its similar to setBinaryStream()
       */
      public void setBlob(int i,Blob x) throws SQLException
      {
!       setBinaryStream(i,x.getBinaryStream(),(int)x.length());
      }

      /**
--- 807,839 ----
      }

      /**
!      * Sets a Blob
       */
      public void setBlob(int i,Blob x) throws SQLException
      {
!             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<l_length) {
!                 los.write(c);
!                 c=l_inStream.read();
!                 p++;
!               }
!               los.close();
!             } catch(IOException se) {
!               throw new PSQLException("postgresql.unusual",se);
!             }
!             // lob is closed by the stream so don't call lob.close()
!             setInt(i,oid);
      }

      /**
***************
*** 741,746 ****
--- 842,866 ----
       */
      public void setCharacterStream(int i,java.io.Reader x,int length) throws SQLException
      {
+           if (connection.haveMinimumCompatibleVersion("7.2")) {
+             //Version 7.2 supports CharacterStream for for 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 varchar datatype, but with toast all the 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
+             char[] l_chars = new char[length];
+             int l_charsRead;
+             try {
+               l_charsRead = x.read(l_chars,0,length);
+             } catch (IOException l_ioe) {
+               throw new PSQLException("postgresql.unusual",l_ioe);
+             }
+             setString(i, new String(l_chars,0,l_charsRead));
+           } else {
+             //Version 7.1 only supported streams for LargeObjects
+             //but the jdbc spec indicates that streams should be
+             //available for LONGVARCHAR instead
              LargeObjectManager lom = connection.getLargeObjectAPI();
              int oid = lom.create();
              LargeObject lob = lom.open(oid);
***************
*** 758,775 ****
              }
              los.close();
            } catch(IOException se) {
!             throw new PSQLException("postgresql.prep.is",se);
            }
            // lob is closed by the stream so don't call lob.close()
            setInt(i,oid);
      }

      /**
       * New in 7.1
       */
      public void setClob(int i,Clob x) throws SQLException
      {
!       setBinaryStream(i,x.getAsciiStream(),(int)x.length());
      }

      /**
--- 878,918 ----
                }
                los.close();
              } catch(IOException se) {
!               throw new PSQLException("postgresql.unusual",se);
              }
              // lob is closed by the stream so don't call lob.close()
              setInt(i,oid);
            }
+     }

      /**
       * New in 7.1
       */
      public void setClob(int i,Clob x) throws SQLException
      {
!             InputStream l_inStream = x.getAsciiStream();
!             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<l_length) {
!                 los.write(c);
!                 c=l_inStream.read();
!                 p++;
!               }
!               los.close();
!             } catch(IOException se) {
!               throw new PSQLException("postgresql.unusual",se);
!             }
!             // lob is closed by the stream so don't call lob.close()
!             setInt(i,oid);
      }

      /**
*** ./src/interfaces/jdbc/org/postgresql/jdbc2/ResultSet.java.orig    Sat Sep  8 23:38:56 2001
--- ./src/interfaces/jdbc/org/postgresql/jdbc2/ResultSet.java    Sat Sep  8 23:52:24 2001
***************
*** 312,321 ****
    {
      if (columnIndex < 1 || columnIndex > fields.length)
        throw new PSQLException("postgresql.res.colrange");
-     wasNullFlag = (this_row[columnIndex - 1] == null);

      // Handle OID's as BLOBS
!     if(!wasNullFlag)
        if( fields[columnIndex - 1].getOID() == 26) {
      LargeObjectManager lom = connection.getLargeObjectAPI();
      LargeObject lob = lom.open(getInt(columnIndex));
--- 312,326 ----
    {
      if (columnIndex < 1 || columnIndex > fields.length)
        throw new PSQLException("postgresql.res.colrange");

+     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( fields[columnIndex - 1].getOID() == 26) {
            LargeObjectManager lom = connection.getLargeObjectAPI();
            LargeObject lob = lom.open(getInt(columnIndex));
***************
*** 323,330 ****
      lob.close();
      return buf;
        }
!
!     return this_row[columnIndex - 1];
    }

    /**
--- 328,336 ----
            lob.close();
            return buf;
          }
!       }
!     }
!     return null;
    }

    /**
***************
*** 392,399 ****
--- 398,424 ----
     */
    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,419 ****
--- 437,463 ----
     */
    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,448 ****
     */
    public InputStream getBinaryStream(int columnIndex) throws SQLException
    {
!     // New in 7.1 Handle OID's as BLOBS so return the input stream
!     if(!wasNullFlag)
        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
    }

    /**
--- 473,501 ----
     */
    public InputStream getBinaryStream(int columnIndex) throws SQLException
    {
!     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();
        }
!     }
!     return null;
    }

    /**
***************
*** 731,737 ****
      //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)
          internalIndex = rows_size+index;
          else {
          beforeFirst();
--- 784,790 ----
      //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)
          internalIndex = rows_size+index;
          else {
          beforeFirst();
***************
*** 794,799 ****
--- 847,856 ----

      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,835 ****
--- 883,907 ----

      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,1488 ****
--- 1557,1563 ----
                          }
                  }
          }
+
+
  }
+
*** ./src/interfaces/jdbc/org/postgresql/ResultSet.java.orig    Sat Sep  8 23:12:41 2001
--- ./src/interfaces/jdbc/org/postgresql/ResultSet.java    Fri Sep  7 10:46:01 2001
***************
*** 192,198 ****
        String s = getString(col);

        // Handle SQL Null
!       if(s==null)
          return null;

        // Handle Money
--- 192,199 ----
        String s = getString(col);

        // Handle SQL Null
!       wasNullFlag = (this_row[col - 1] == null);
!       if(wasNullFlag)
          return null;

        // Handle Money
package org.postgresql.util;

import java.sql.*;

/**
 * Converts to and from the postgresql bytea datatype used by the backend.
 *
 * $Id: Encoding.java,v 1.1 2001/07/21 18:52:11 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();
        }


}

pgsql-jdbc by date:

Previous
From: Alexaki Sofia
Date:
Subject: Transaction size limit & Data Truncation
Next
From: Bhuvaneswari
Date:
Subject: Regarding Installation of jdbc7.0-1.1.jar with postgresql-7.0.3-2.i386.rpm