Changed QueryExecutor.java to correctly read responses from the backend.
authorBarry Lind
Tue, 26 Mar 2002 05:52:50 +0000 (05:52 +0000)
committerBarry Lind
Tue, 26 Mar 2002 05:52:50 +0000 (05:52 +0000)
Fixed NPE when database name was not passed on the jdbc connection URL
Fixed Connection.isClosed() to not hit the DB for every call

src/interfaces/jdbc/org/postgresql/Connection.java
src/interfaces/jdbc/org/postgresql/Driver.java.in
src/interfaces/jdbc/org/postgresql/core/QueryExecutor.java
src/interfaces/jdbc/org/postgresql/core/QueryExecutor2.java [deleted file]
src/interfaces/jdbc/org/postgresql/fastpath/Fastpath.java
src/interfaces/jdbc/org/postgresql/jdbc2/Connection.java

index 3ee6deea0a5d4ba95e95a23e783c6ea7004da6ea..54f067542de446718e9e16f0c6baf07abf5e0e7d 100644 (file)
@@ -11,7 +11,7 @@ import org.postgresql.util.*;
 import org.postgresql.core.*;\r
 \r
 /*\r
- * $Id: Connection.java,v 1.44 2002/03/21 02:39:06 davec Exp $\r
+ * $Id: Connection.java,v 1.45 2002/03/26 05:52:48 barry Exp $\r
  *\r
  * This abstract class is used by org.postgresql.Driver to open either the JDBC1 or\r
  * JDBC2 versions of the Connection class.\r
@@ -59,10 +59,6 @@ public abstract class Connection
    private static final int AUTH_REQ_CRYPT = 4;\r
    private static final int AUTH_REQ_MD5 = 5;\r
 \r
-   public final static int PGASYNC_IDLE = 0;               /* nothing's happening, dude */\r
-   public final static int PGASYNC_BUSY = 1;               /* query in progress */\r
-   public final static int PGASYNC_READY = 2;          /* result ready for PQgetResult */\r
-\r
 \r
    // These are used to cache oids, PGTypes and SQLTypes\r
    private static Hashtable sqlTypeCache = new Hashtable();  // oid -> SQLType\r
@@ -81,7 +77,6 @@ public abstract class Connection
    public int pid;\r
    public int ckey;\r
 \r
-  public int asyncStatus = PGASYNC_READY;\r
    /*\r
     * This is called by Class.forName() from within org.postgresql.Driver\r
     */\r
@@ -427,7 +422,7 @@ public abstract class Connection
     */\r
    public java.sql.ResultSet ExecSQL(String sql, java.sql.Statement stat) throws SQLException\r
    {\r
-       return new QueryExecutor2(sql, stat, pg_stream, this).execute();\r
+       return new QueryExecutor(sql, stat, pg_stream, this).execute();\r
    }\r
 \r
    /*\r
index a6d0fb03a257332a819443545e0f8ae9cfcabd6e..bc1925efe56bd9b86c5e6b3a12abfe26f7a2bf23 100644 (file)
@@ -419,7 +419,7 @@ public class Driver implements java.sql.Driver
     */
    public String database()
    {
-       return props.getProperty("PGDBNAME");
+       return props.getProperty("PGDBNAME", "");
    }
 
    /*
index 237aa49684f653ee0991dedd119f2e9780613069..bfa6af3572380fef41cae2f54c3edc634d73b7ea 100644 (file)
@@ -13,212 +13,199 @@ import org.postgresql.util.PSQLException;
  * 

The lifetime of a QueryExecutor object is from sending the query

  * until the response has been received from the backend.
  *
- * $Id: QueryExecutor.java,v 1.11 2002/03/21 03:20:30 davec Exp $
+ * $Id: QueryExecutor.java,v 1.12 2002/03/26 05:52:49 barry Exp $
  */
 
 public class QueryExecutor
 {
 
-   private final String sql;
-   private final java.sql.Statement statement;
-   private final PG_Stream pg_stream;
-   private final org.postgresql.Connection connection;
-
-   public QueryExecutor(String sql,
-                        java.sql.Statement statement,
-                        PG_Stream pg_stream,
-                        org.postgresql.Connection connection)
-   throws SQLException
-   {
-       this.sql = sql;
-       this.statement = statement;
-       this.pg_stream = pg_stream;
-       this.connection = connection;
-
-       if (statement != null)
-           maxRows = statement.getMaxRows();
-       else
-           maxRows = 0;
-   }
-
-   private Field[] fields = null;
-   private Vector tuples = new Vector();
-   private boolean binaryCursor = false;
-   private String status = null;
-   private int update_count = 1;
-   private long insert_oid = 0;
-   private int maxRows;
-
-   /*
-    * Execute a query on the backend.
-    */
-   public java.sql.ResultSet execute() throws SQLException
-   {
-
-       int fqp = 0;
-       boolean hfr = false;
-
-       StringBuffer errorMessage = null;
-
-       synchronized (pg_stream)
-       {
-
-           sendQuery(sql);
-
-           while (!hfr || fqp > 0)
-           {
-               int c = pg_stream.ReceiveChar();
-
-               switch (c)
-               {
-                   case 'A':   // Asynchronous Notify
-                       int pid = pg_stream.ReceiveInteger(4);
-                       String msg = pg_stream.ReceiveString(connection.getEncoding());
-                       break;
-                   case 'B':   // Binary Data Transfer
-                       receiveTuple(true);
-                       break;
-                   case 'C':   // Command Status
-                       receiveCommandStatus();
-
-                       if (fields != null)
-                           hfr = true;
-                       else
-                       {
-                           sendQuery(" ");
-                           fqp++;
-                       }
-                       break;
-                   case 'D':   // Text Data Transfer
-                       receiveTuple(false);
-                       break;
-                   case 'E':   // Error Message
-
-                       // it's possible to get more than one error message for a query
-                       // see libpq comments wrt backend closing a connection 
-                       // so, append messages to a string buffer and keep processing
-                       // check at the bottom to see if we need to throw an exception
-                       
-                       if ( errorMessage == null )
-                           errorMessage = new StringBuffer();
-
-                       errorMessage.append(pg_stream.ReceiveString(connection.getEncoding()));
-                       // keep processing
-                       break;
-
-                   case 'I':   // Empty Query
-                       int t = pg_stream.ReceiveChar();
-                       if (t != 0)
-                           throw new PSQLException("postgresql.con.garbled");
-
-                       if (fqp > 0)
-                           fqp--;
-                       if (fqp == 0)
-                           hfr = true;
-                       break;
-                   case 'N':   // Error Notification
-                       connection.addWarning(pg_stream.ReceiveString(connection.getEncoding()));
-                       break;
-                   case 'P':   // Portal Name
-                       String pname = pg_stream.ReceiveString(connection.getEncoding());
-                       break;
-                   case 'T':   // MetaData Field Description
-                       receiveFields();
-                       break;
-                   case 'Z':        // backend ready for query, ignore for now :-)
-                       break;
-                   default:
-                       throw new PSQLException("postgresql.con.type",
-                                               new Character((char) c));
-               }
-
-               // did we get an error during this query?
-               if ( errorMessage != null )
-                   throw new SQLException( errorMessage.toString() );
-           }
-           return connection.getResultSet(connection, statement, fields, tuples, status, update_count, insert_oid, binaryCursor);
-       }
-   }
-
-   /*
-    * Send a query to the backend.
-    */
-   private void sendQuery(String query) throws SQLException
-   {
-       try
-       {
-           pg_stream.SendChar('Q');
-           pg_stream.Send(connection.getEncoding().encode(query));
-           pg_stream.SendChar(0);
-           pg_stream.flush();
-
-       }
-       catch (IOException e)
-       {
-           throw new PSQLException("postgresql.con.ioerror", e);
-       }
-   }
-
-   /*
-    * Receive a tuple from the backend.
-    *
-    * @param isBinary set if the tuple should be treated as binary data
-    */
-   private void receiveTuple(boolean isBinary) throws SQLException
-   {
-       if (fields == null)
-           throw new PSQLException("postgresql.con.tuple");
-       Object tuple = pg_stream.ReceiveTuple(fields.length, isBinary);
-       if (isBinary)
-           binaryCursor = true;
-       if (maxRows == 0 || tuples.size() < maxRows)
-           tuples.addElement(tuple);
-   }
-
-   /*
-    * Receive command status from the backend.
-    */
-   private void receiveCommandStatus() throws SQLException
-   {
-       status = pg_stream.ReceiveString(connection.getEncoding());
-
-       try
-       {
-           // Now handle the update count correctly.
-           if (status.startsWith("INSERT") || status.startsWith("UPDATE") || status.startsWith("DELETE") || status.startsWith("MOVE"))
-           {
-               update_count = Integer.parseInt(status.substring(1 + status.lastIndexOf(' ')));
-           }
-           if (status.startsWith("INSERT"))
-           {
-               insert_oid = Long.parseLong(status.substring(1 + status.indexOf(' '),
-                                             status.lastIndexOf(' ')));
-           }
-       }
-       catch (NumberFormatException nfe)
-       {
-           throw new PSQLException("postgresql.con.fathom", status);
-       }
-   }
-
-   /*
-    * Receive the field descriptions from the back end.
-    */
-   private void receiveFields() throws SQLException
-   {
-       if (fields != null)
-           throw new PSQLException("postgresql.con.multres");
-
-       int size = pg_stream.ReceiveIntegerR(2);
-       fields = new Field[size];
-
-       for (int i = 0; i < fields.length; i++)
-       {
-           String typeName = pg_stream.ReceiveString(connection.getEncoding());
-           int typeOid = pg_stream.ReceiveIntegerR(4);
-           int typeLength = pg_stream.ReceiveIntegerR(2);
-           int typeModifier = pg_stream.ReceiveIntegerR(4);
-           fields[i] = new Field(connection, typeName, typeOid, typeLength, typeModifier);
-       }
-   }
+        private final String sql;
+        private final java.sql.Statement statement;
+        private final PG_Stream pg_stream;
+        private final org.postgresql.Connection connection;
+
+        public QueryExecutor(String sql,
+                                                 java.sql.Statement statement,
+                                                 PG_Stream pg_stream,
+                                                 org.postgresql.Connection connection)
+        throws SQLException
+        {
+                this.sql = sql;
+                this.statement = statement;
+                this.pg_stream = pg_stream;
+                this.connection = connection;
+
+                if (statement != null)
+                        maxRows = statement.getMaxRows();
+                else
+                        maxRows = 0;
+        }
+
+        private Field[] fields = null;
+        private Vector tuples = new Vector();
+        private boolean binaryCursor = false;
+        private String status = null;
+        private int update_count = 1;
+        private long insert_oid = 0;
+        private int maxRows;
+
+        /*
+         * Execute a query on the backend.
+         */
+        public java.sql.ResultSet execute() throws SQLException
+        {
+
+                StringBuffer errorMessage = null;
+
+                synchronized (pg_stream)
+                {
+
+                        sendQuery(sql);
+
+                        int c;
+                        boolean l_endQuery = false;
+                        while (!l_endQuery) 
+                        {
+                                c = pg_stream.ReceiveChar();
+
+                                switch (c)
+                                {
+                                        case 'A':  // Asynchronous Notify
+                                                int pid = pg_stream.ReceiveInteger(4);
+                                                String msg = pg_stream.ReceiveString(connection.getEncoding());
+                                                break;
+                                        case 'B':  // Binary Data Transfer
+                                                receiveTuple(true);
+                                                break;
+                                        case 'C':  // Command Status
+                                                receiveCommandStatus();
+                                                break;
+                                        case 'D':  // Text Data Transfer
+                                                receiveTuple(false);
+                                                break;
+                                        case 'E':  // Error Message
+
+                                                // it's possible to get more than one error message for a query
+                                                // see libpq comments wrt backend closing a connection
+                                                // so, append messages to a string buffer and keep processing
+                                                // check at the bottom to see if we need to throw an exception
+
+                                                if ( errorMessage == null )
+                                                        errorMessage = new StringBuffer();
+
+                                                errorMessage.append(pg_stream.ReceiveString(connection.getEncoding()));
+                                                // keep processing
+                                                break;
+                                        case 'I':  // Empty Query
+                                                int t = pg_stream.ReceiveChar();
+                                                break;
+                                        case 'N':  // Error Notification
+                                                connection.addWarning(pg_stream.ReceiveString(connection.getEncoding()));
+                                                break;
+                                        case 'P':  // Portal Name
+                                                String pname = pg_stream.ReceiveString(connection.getEncoding());
+                                                break;
+                                        case 'T':  // MetaData Field Description
+                                                receiveFields();
+                                                break;
+                                        case 'Z':
+                           l_endQuery = true;
+                                                break;
+                                        default:
+                                                throw new PSQLException("postgresql.con.type",
+                                                                                                new Character((char) c));
+                                }
+
+                        }
+
+                        // did we get an error during this query?
+                        if ( errorMessage != null )
+                                throw new SQLException( errorMessage.toString() );
+
+                        return connection.getResultSet(connection, statement, fields, tuples, status, update_count, insert_oid, binaryCursor);
+                }
+        }
+
+        /*
+         * Send a query to the backend.
+         */
+        private void sendQuery(String query) throws SQLException
+        {
+                try
+                {
+                        pg_stream.SendChar('Q');
+                        pg_stream.Send(connection.getEncoding().encode(query));
+                        pg_stream.SendChar(0);
+                        pg_stream.flush();
+
+                }
+                catch (IOException e)
+                {
+                        throw new PSQLException("postgresql.con.ioerror", e);
+                }
+        }
+
+        /*
+         * Receive a tuple from the backend.
+         *
+         * @param isBinary set if the tuple should be treated as binary data
+         */
+        private void receiveTuple(boolean isBinary) throws SQLException
+        {
+                if (fields == null)
+                        throw new PSQLException("postgresql.con.tuple");
+                Object tuple = pg_stream.ReceiveTuple(fields.length, isBinary);
+                if (isBinary)
+                        binaryCursor = true;
+                if (maxRows == 0 || tuples.size() < maxRows)
+                        tuples.addElement(tuple);
+        }
+
+        /*
+         * Receive command status from the backend.
+         */
+        private void receiveCommandStatus() throws SQLException
+        {
+
+                status = pg_stream.ReceiveString(connection.getEncoding());
+
+                try
+                {
+                        // Now handle the update count correctly.
+                        if (status.startsWith("INSERT") || status.startsWith("UPDATE") || status.startsWith("DELETE") || status.startsWith("MOVE"))
+                        {
+                                update_count = Integer.parseInt(status.substring(1 + status.lastIndexOf(' ')));
+                        }
+                        if (status.startsWith("INSERT"))
+                        {
+                                insert_oid = Long.parseLong(status.substring(1 + status.indexOf(' '),
+                                                                                          status.lastIndexOf(' ')));
+                        }
+                }
+                catch (NumberFormatException nfe)
+                {
+                        throw new PSQLException("postgresql.con.fathom", status);
+                }
+        }
+
+        /*
+         * Receive the field descriptions from the back end.
+         */
+        private void receiveFields() throws SQLException
+        {
+                if (fields != null)
+                        throw new PSQLException("postgresql.con.multres");
+
+                int size = pg_stream.ReceiveIntegerR(2);
+                fields = new Field[size];
+
+                for (int i = 0; i < fields.length; i++)
+                {
+                        String typeName = pg_stream.ReceiveString(connection.getEncoding());
+                        int typeOid = pg_stream.ReceiveIntegerR(4);
+                        int typeLength = pg_stream.ReceiveIntegerR(2);
+                        int typeModifier = pg_stream.ReceiveIntegerR(4);
+                        fields[i] = new Field(connection, typeName, typeOid, typeLength, typeModifier);
+                }
+        }
 }
diff --git a/src/interfaces/jdbc/org/postgresql/core/QueryExecutor2.java b/src/interfaces/jdbc/org/postgresql/core/QueryExecutor2.java
deleted file mode 100644 (file)
index f48c95c..0000000
+++ /dev/null
@@ -1,259 +0,0 @@
-\r
-package org.postgresql.core;\r
-\r
-import java.util.Vector;\r
-import java.io.IOException;\r
-import java.sql.*;\r
-import org.postgresql.*;\r
-import org.postgresql.util.PSQLException;\r
-\r
-/*\r
- * Executes a query on the backend.\r
- *\r
- * 

The lifetime of a QueryExecutor object is from sending the query\r

- * until the response has been received from the backend.\r
- *\r
- * $Id: QueryExecutor2.java,v 1.1 2002/03/21 03:20:29 davec Exp $\r
- */\r
-\r
-public class QueryExecutor2\r
-{\r
-\r
-   private final String sql;\r
-   private final java.sql.Statement statement;\r
-   private final PG_Stream pg_stream;\r
-   private final org.postgresql.Connection connection;\r
-\r
-   public QueryExecutor2(String sql,\r
-                        java.sql.Statement statement,\r
-                        PG_Stream pg_stream,\r
-                        org.postgresql.Connection connection)\r
-   throws SQLException\r
-   {\r
-       this.sql = sql;\r
-       this.statement = statement;\r
-       this.pg_stream = pg_stream;\r
-       this.connection = connection;\r
-\r
-       if (statement != null)\r
-           maxRows = statement.getMaxRows();\r
-       else\r
-           maxRows = 0;\r
-   }\r
-\r
-   private Field[] fields = null;\r
-   private Vector tuples = new Vector();\r
-   private boolean binaryCursor = false;\r
-   private String status = null;\r
-   private int update_count = 1;\r
-   private long insert_oid = 0;\r
-   private int maxRows;\r
-\r
-   /*\r
-    * Execute a query on the backend.\r
-    */\r
-   public java.sql.ResultSet execute() throws SQLException\r
-   {\r
-\r
-    StringBuffer errorMessage = null;\r
-\r
-       synchronized (pg_stream)\r
-       {\r
-\r
-           sendQuery(sql);\r
-      connection.asyncStatus = org.postgresql.Connection.PGASYNC_BUSY;\r
-\r
-           while ( connection.asyncStatus != org.postgresql.Connection.PGASYNC_IDLE )\r
-           {\r
-               int c = pg_stream.ReceiveChar();\r
-\r
-        if ( c == 'A' )\r
-        {\r
-\r
-          int pid = pg_stream.ReceiveIntegerR(4);\r
-          String msg = pg_stream.ReceiveString(connection.getEncoding());\r
-\r
-          org.postgresql.Driver.debug(msg);\r
-          continue;\r
-        }\r
-        else if ( c == 'N' )\r
-        {\r
-          String notification = pg_stream.ReceiveString(connection.getEncoding());\r
-          org.postgresql.Driver.debug(notification);\r
-          connection.addWarning(notification);\r
-          continue;\r
-        }\r
-        else if ( connection.asyncStatus != org.postgresql.Connection.PGASYNC_BUSY )\r
-        {\r
-          if ( connection.asyncStatus != org.postgresql.Connection.PGASYNC_IDLE )\r
-          {\r
-            // only one possibility left which is PGASYNC_READY, so let's get out\r
-            break;\r
-          }\r
-          if ( c == 'E' ) {\r
-            String error = pg_stream.ReceiveString(connection.getEncoding());\r
-            org.postgresql.Driver.debug(error);\r
-\r
-            // no sense in creating this object until we really need it\r
-            if ( errorMessage == null ) {\r
-              errorMessage = new StringBuffer();\r
-            }\r
-\r
-            errorMessage.append(error);\r
-            break;\r
-          }\r
-        }else{\r
-\r
-          switch (c)\r
-          {\r
-            case 'C':  // Command Status\r
-              receiveCommandStatus();\r
-              break;\r
-\r
-            case 'E':  // Error Message\r
-\r
-              // it's possible to get multiple error messages from one query\r
-              // see libpq, there are some comments about a connection being closed\r
-              // by the backend after real error occurs, so append error messages here\r
-              // so append them and just remember that an error occured\r
-              // throw the exception at the end of processing\r
-\r
-              String error = pg_stream.ReceiveString(connection.getEncoding());\r
-              org.postgresql.Driver.debug(error);\r
-\r
-              // no sense in creating this object until we really need it\r
-              if ( errorMessage == null ) {\r
-                errorMessage = new StringBuffer();\r
-              }\r
-\r
-              errorMessage.append(error);\r
-              connection.asyncStatus = org.postgresql.Connection.PGASYNC_READY;\r
-              break;\r
-\r
-            case 'Z':       // backend ready for query, ignore for now :-)\r
-              connection.asyncStatus = org.postgresql.Connection.PGASYNC_IDLE;\r
-              break;\r
-\r
-            case 'I':  // Empty Query\r
-              int t = pg_stream.ReceiveChar();\r
-              if (t != 0)\r
-                throw new PSQLException("postgresql.con.garbled");\r
-\r
-              connection.asyncStatus = org.postgresql.Connection.PGASYNC_READY;\r
-              break;\r
-\r
-            case 'P':  // Portal Name\r
-              String pname = pg_stream.ReceiveString(connection.getEncoding());\r
-              org.postgresql.Driver.debug(pname);\r
-              break;\r
-\r
-            case 'T':  // MetaData Field Description\r
-              receiveFields();\r
-              break;\r
-\r
-            case 'B':  // Binary Data Transfer\r
-              receiveTuple(true);\r
-              break;\r
-\r
-            case 'D':  // Text Data Transfer\r
-              receiveTuple(false);\r
-              break;\r
-\r
-           default:\r
-              throw new PSQLException("postgresql.con.type",\r
-                          new Character((char) c));\r
-          }\r
-        }\r
-           }\r
-      // did we get an error message?\r
-\r
-      if ( errorMessage != null ) {\r
-        // yes, throw an exception\r
-        throw new SQLException(errorMessage.toString());\r
-      }\r
-           return connection.getResultSet(connection, statement, fields, tuples, status, update_count, insert_oid, binaryCursor);\r
-       }\r
-   }\r
-\r
-   /*\r
-    * Send a query to the backend.\r
-    */\r
-   private void sendQuery(String query) throws SQLException\r
-   {\r
-       try\r
-       {\r
-           pg_stream.SendChar('Q');\r
-           pg_stream.Send(connection.getEncoding().encode(query));\r
-           pg_stream.SendChar(0);\r
-           pg_stream.flush();\r
-\r
-       }\r
-       catch (IOException e)\r
-       {\r
-           throw new PSQLException("postgresql.con.ioerror", e);\r
-       }\r
-   }\r
-\r
-   /*\r
-    * Receive a tuple from the backend.\r
-    *\r
-    * @param isBinary set if the tuple should be treated as binary data\r
-    */\r
-   private void receiveTuple(boolean isBinary) throws SQLException\r
-   {\r
-       if (fields == null)\r
-           throw new PSQLException("postgresql.con.tuple");\r
-       Object tuple = pg_stream.ReceiveTuple(fields.length, isBinary);\r
-       if (isBinary)\r
-           binaryCursor = true;\r
-       if (maxRows == 0 || tuples.size() < maxRows)\r
-           tuples.addElement(tuple);\r
-   }\r
-\r
-   /*\r
-    * Receive command status from the backend.\r
-    */\r
-   private void receiveCommandStatus() throws SQLException\r
-   {\r
-       status = pg_stream.ReceiveString(connection.getEncoding());\r
-\r
-       try\r
-       {\r
-           // Now handle the update count correctly.\r
-           if (status.startsWith("INSERT") || status.startsWith("UPDATE") || status.startsWith("DELETE") || status.startsWith("MOVE"))\r
-           {\r
-               update_count = Integer.parseInt(status.substring(1 + status.lastIndexOf(' ')));\r
-           }\r
-           if (status.startsWith("INSERT"))\r
-           {\r
-               insert_oid = Long.parseLong(status.substring(1 + status.indexOf(' '),\r
-                                             status.lastIndexOf(' ')));\r
-           }\r
-       }\r
-       catch (NumberFormatException nfe)\r
-       {\r
-           throw new PSQLException("postgresql.con.fathom", status);\r
-       }\r
-   }\r
-\r
-   /*\r
-    * Receive the field descriptions from the back end.\r
-    */\r
-   private void receiveFields() throws SQLException\r
-   {\r
-       if (fields != null)\r
-           throw new PSQLException("postgresql.con.multres");\r
-\r
-       int size = pg_stream.ReceiveIntegerR(2);\r
-       fields = new Field[size];\r
-\r
-       for (int i = 0; i < fields.length; i++)\r
-       {\r
-           String typeName = pg_stream.ReceiveString(connection.getEncoding());\r
-           int typeOid = pg_stream.ReceiveIntegerR(4);\r
-           int typeLength = pg_stream.ReceiveIntegerR(2);\r
-           int typeModifier = pg_stream.ReceiveIntegerR(4);\r
-           fields[i] = new Field(connection, typeName, typeOid, typeLength, typeModifier);\r
-       }\r
-   }\r
-}\r
index 2581f59b52c79cf5e619d5e4612010e0771bbfa8..a74645015a87b2e9764d53d44efa81155b2dcaae 100644 (file)
@@ -23,283 +23,269 @@ import org.postgresql.util.*;
  */
 public class Fastpath
 {
-   // This maps the functions names to their id's (possible unique just
-   // to a connection).
-   protected Hashtable func = new Hashtable();
+        // This maps the functions names to their id's (possible unique just
+        // to a connection).
+        protected Hashtable func = new Hashtable();
 
-   protected org.postgresql.Connection conn;       // our connection
-   protected org.postgresql.PG_Stream stream;  // the network stream
+        protected org.postgresql.Connection conn;      // our connection
+        protected org.postgresql.PG_Stream stream; // the network stream
 
-   /*
-    * Initialises the fastpath system
-    *
-    * 

Important Notice

-    * 
This is called from org.postgresql.Connection, and should not be called
-    * from client code.
-    *
-    * @param conn org.postgresql.Connection to attach to
-    * @param stream The network stream to the backend
-    */
-   public Fastpath(org.postgresql.Connection conn, org.postgresql.PG_Stream stream)
-   {
-       this.conn = conn;
-       this.stream = stream;
-       //DriverManager.println("Fastpath initialised");
-   }
+        /*
+         * Initialises the fastpath system
+         *
+         

Important Notice

+         
This is called from org.postgresql.Connection, and should not be called
+         * from client code.
+         *
+         * @param conn org.postgresql.Connection to attach to
+         * @param stream The network stream to the backend
+         */
+        public Fastpath(org.postgresql.Connection conn, org.postgresql.PG_Stream stream)
+        {
+                this.conn = conn;
+                this.stream = stream;
+                //DriverManager.println("Fastpath initialised");
+        }
 
-   /*
-    * Send a function call to the PostgreSQL backend
-    *
-    * @param fnid Function id
-    * @param resulttype True if the result is an integer, false for other results
-    * @param args FastpathArguments to pass to fastpath
-    * @return null if no data, Integer if an integer result, or byte[] otherwise
-    * @exception SQLException if a database-access error occurs.
-    */
-   public Object fastpath(int fnid, boolean resulttype, FastpathArg[] args) throws SQLException
-   {
-       // added Oct 7 1998 to give us thread safety
-       synchronized (stream)
-       {
-           // send the function call
-           try
-           {
-               // 70 is 'F' in ASCII. Note: don't use SendChar() here as it adds padding
-               // that confuses the backend. The 0 terminates the command line.
-               stream.SendInteger(70, 1);
-               stream.SendInteger(0, 1);
+        /*
+         * Send a function call to the PostgreSQL backend
+         *
+         * @param fnid Function id
+         * @param resulttype True if the result is an integer, false for other results
+         * @param args FastpathArguments to pass to fastpath
+         * @return null if no data, Integer if an integer result, or byte[] otherwise
+         * @exception SQLException if a database-access error occurs.
+         */
+        public Object fastpath(int fnid, boolean resulttype, FastpathArg[] args) throws SQLException
+        {
+                // added Oct 7 1998 to give us thread safety
+                synchronized (stream)
+                {
+                        // send the function call
+                        try
+                        {
+                                // 70 is 'F' in ASCII. Note: don't use SendChar() here as it adds padding
+                                // that confuses the backend. The 0 terminates the command line.
+                                stream.SendInteger(70, 1);
+                                stream.SendInteger(0, 1);
 
-               stream.SendInteger(fnid, 4);
-               stream.SendInteger(args.length, 4);
+                                stream.SendInteger(fnid, 4);
+                                stream.SendInteger(args.length, 4);
 
-               for (int i = 0;i < args.length;i++)
-                   args[i].send(stream);
+                                for (int i = 0;i < args.length;i++)
+                                        args[i].send(stream);
 
-               // This is needed, otherwise data can be lost
-               stream.flush();
+                                // This is needed, otherwise data can be lost
+                                stream.flush();
 
-           }
-           catch (IOException ioe)
-           {
-               throw new PSQLException("postgresql.fp.send", new Integer(fnid), ioe);
-           }
+                        }
+                        catch (IOException ioe)
+                        {
+                                throw new PSQLException("postgresql.fp.send", new Integer(fnid), ioe);
+                        }
 
-           // Now handle the result
+                        // Now handle the result
 
-           // We should get 'V' on sucess or 'E' on error. Anything else is treated
-           // as an error.
-           //int in = stream.ReceiveChar();
-           //DriverManager.println("ReceiveChar() = "+in+" '"+((char)in)+"'");
-           //if (in!='V') {
-           //if (in=='E')
-           //throw new SQLException(stream.ReceiveString(conn.getEncoding()));
-           //throw new SQLException("Fastpath: expected 'V' from backend, got "+((char)in));
-           //}
+                        // Now loop, reading the results
+                        Object result = null; // our result
+                        StringBuffer errorMessage = null;
+                        int c;
+                        boolean l_endQuery = false;
+                        while (!l_endQuery)
+                        {
+                                c = stream.ReceiveChar();
 
-           // Now loop, reading the results
-           Object result = null; // our result
-           StringBuffer errorMessage = null;
-           boolean loop = true;
-           while (loop)
-           {
-               int in = stream.ReceiveChar();
-               //DriverManager.println("ReceiveChar() = "+in+" '"+((char)in)+"'");
-               switch (in)
-               {
-                   case 'V':
-                       break;
+                                switch (c)
+                                {
+                                        case 'A':  // Asynchronous Notify
+                                                int pid = stream.ReceiveInteger(4);
+                                                String msg = stream.ReceiveString(conn.getEncoding());
+                                                break;
 
-                       //------------------------------
-                       // Function returned properly
-                       //
-                   case 'G':
-                       int sz = stream.ReceiveIntegerR(4);
-                       //DriverManager.println("G: size="+sz);  //debug
+                                                //------------------------------
+                                                // Error message returned
+                                        case 'E':
+                                                if ( errorMessage == null )
+                                                         errorMessage = new StringBuffer();
+                                                 errorMessage.append(stream.ReceiveString(conn.getEncoding()));
+                                                break;
 
-                       // Return an Integer if
-                       if (resulttype)
-                           result = new Integer(stream.ReceiveIntegerR(sz));
-                       else
-                       {
-                           byte buf[] = new byte[sz];
-                           stream.Receive(buf, 0, sz);
-                           result = buf;
-                       }
-                       break;
+                                                //------------------------------
+                                                // Notice from backend
+                                        case 'N':
+                                                conn.addWarning(stream.ReceiveString(conn.getEncoding()));
+                                                break;
 
-                       //------------------------------
-                       // Error message returned
-                   case 'E':
-                       if ( errorMessage == null )
-                           errorMessage = new StringBuffer();
-                       errorMessage.append(stream.ReceiveString(conn.getEncoding()));
-                       break;
-                       //------------------------------
-                       // Notice from backend
-                   case 'N':
-                       conn.addWarning(stream.ReceiveString(conn.getEncoding()));
-                       break;
+                                        case 'V':
+                                                int l_nextChar = stream.ReceiveChar();
+                                                if (l_nextChar == 'G') {
+                                                  int sz = stream.ReceiveIntegerR(4);
+                                                  // Return an Integer if
+                                                  if (resulttype)
+                                                        result = new Integer(stream.ReceiveIntegerR(sz));
+                                                  else
+                                                  {
+                                                        byte buf[] = new byte[sz];
+                                                        stream.Receive(buf, 0, sz);
+                                                        result = buf;
+                                                  }
+                                                  //There should be a trailing '0'
+                                                  int l_endChar = stream.ReceiveChar();
+                                                } else {
+                                                  //it must have been a '0', thus no results
+                                                }
+                                                break;
 
-                       //------------------------------
-                       // End of results
-                       //
-                       // Here we simply return res, which would contain the result
-                       // processed earlier. If no result, this already contains null
-                   case '0':
-                       //DriverManager.println("returning "+result);
-                       // return result;
-                       break;
-                   case 'Z':
-                       // cause the loop to exit
-                       loop = false;
-                       break;
+                                        case 'Z':
+                                                l_endQuery = true;
+                                                break;
 
-                   default:
-                       throw new PSQLException("postgresql.fp.protocol", new Character((char)in));
-               }
-           }
-           
-           if ( errorMessage != null )
-               throw new PSQLException("postgresql.fp.error", errorMessage.toString());
+                                        default:
+                                                throw new PSQLException("postgresql.fp.protocol", new Character((char)c));
+                                }
+                        }
 
-           return result;
-       }
-   }
+                        if ( errorMessage != null )
+                                   throw new PSQLException("postgresql.fp.error", errorMessage.toString());
 
-   /*
-    * Send a function call to the PostgreSQL backend by name.
-    *
-    * Note: the mapping for the procedure name to function id needs to exist,
-    * usually to an earlier call to addfunction().
-    *
-    * This is the prefered method to call, as function id's can/may change
-    * between versions of the backend.
-    *
-    * For an example of how this works, refer to org.postgresql.LargeObject
-    *
-    * @param name Function name
-    * @param resulttype True if the result is an integer, false for other
-    * results
-    * @param args FastpathArguments to pass to fastpath
-    * @return null if no data, Integer if an integer result, or byte[] otherwise
-    * @exception SQLException if name is unknown or if a database-access error
-    * occurs.
-    * @see org.postgresql.LargeObject
-    */
-   public Object fastpath(String name, boolean resulttype, FastpathArg[] args) throws SQLException
-   {
-       //DriverManager.println("Fastpath: calling "+name);
-       return fastpath(getID(name), resulttype, args);
-   }
+                        return result;
+                }
+        }
 
-   /*
-    * This convenience method assumes that the return value is an Integer
-    * @param name Function name
-    * @param args Function arguments
-    * @return integer result
-    * @exception SQLException if a database-access error occurs or no result
-    */
-   public int getInteger(String name, FastpathArg[] args) throws SQLException
-   {
-       Integer i = (Integer)fastpath(name, true, args);
-       if (i == null)
-           throw new PSQLException("postgresql.fp.expint", name);
-       return i.intValue();
-   }
+        /*
+         * Send a function call to the PostgreSQL backend by name.
+         *
+         * Note: the mapping for the procedure name to function id needs to exist,
+         * usually to an earlier call to addfunction().
+         *
+         * This is the prefered method to call, as function id's can/may change
+         * between versions of the backend.
+         *
+         * For an example of how this works, refer to org.postgresql.LargeObject
+         *
+         * @param name Function name
+         * @param resulttype True if the result is an integer, false for other
+         * results
+         * @param args FastpathArguments to pass to fastpath
+         * @return null if no data, Integer if an integer result, or byte[] otherwise
+         * @exception SQLException if name is unknown or if a database-access error
+         * occurs.
+         * @see org.postgresql.LargeObject
+         */
+        public Object fastpath(String name, boolean resulttype, FastpathArg[] args) throws SQLException
+        {
+                //DriverManager.println("Fastpath: calling "+name);
+                return fastpath(getID(name), resulttype, args);
+        }
 
-   /*
-    * This convenience method assumes that the return value is an Integer
-    * @param name Function name
-    * @param args Function arguments
-    * @return byte[] array containing result
-    * @exception SQLException if a database-access error occurs or no result
-    */
-   public byte[] getData(String name, FastpathArg[] args) throws SQLException
-   {
-       return (byte[])fastpath(name, false, args);
-   }
+        /*
+         * This convenience method assumes that the return value is an Integer
+         * @param name Function name
+         * @param args Function arguments
+         * @return integer result
+         * @exception SQLException if a database-access error occurs or no result
+         */
+        public int getInteger(String name, FastpathArg[] args) throws SQLException
+        {
+                Integer i = (Integer)fastpath(name, true, args);
+                if (i == null)
+                        throw new PSQLException("postgresql.fp.expint", name);
+                return i.intValue();
+        }
 
-   /*
-    * This adds a function to our lookup table.
-    *
-    * 

User code should use the addFunctions method, which is based upon a

-    * query, rather than hard coding the oid. The oid for a function is not
-    * guaranteed to remain static, even on different servers of the same
-    * version.
-    *
-    * @param name Function name
-    * @param fnid Function id
-    */
-   public void addFunction(String name, int fnid)
-   {
-       func.put(name, new Integer(fnid));
-   }
+        /*
+         * This convenience method assumes that the return value is an Integer
+         * @param name Function name
+         * @param args Function arguments
+         * @return byte[] array containing result
+         * @exception SQLException if a database-access error occurs or no result
+         */
+        public byte[] getData(String name, FastpathArg[] args) throws SQLException
+        {
+                return (byte[])fastpath(name, false, args);
+        }
 
-   /*
-    * This takes a ResultSet containing two columns. Column 1 contains the
-    * function name, Column 2 the oid.
-    *
-    * 

It reads the entire ResultSet, loading the values into the function

-    * table.
-    *
-    * 

REMEMBER to close() the resultset after calling this!!

-    *
-    * 

Implementation note about function name lookups:

-    *
-    * 

PostgreSQL stores the function id's and their corresponding names in

-    * the pg_proc table. To speed things up locally, instead of querying each
-    * function from that table when required, a Hashtable is used. Also, only
-    * the function's required are entered into this table, keeping connection
-    * times as fast as possible.
-    *
-    * 

The org.postgresql.LargeObject class performs a query upon it's startup,

-    * and passes the returned ResultSet to the addFunctions() method here.
-    *
-    * 

Once this has been done, the LargeObject api refers to the functions by

-    * name.
-    *
-    * 

Dont think that manually converting them to the oid's will work. Ok,

-    * they will for now, but they can change during development (there was some
-    * discussion about this for V7.0), so this is implemented to prevent any
-    * unwarranted headaches in the future.
-    *
-    * @param rs ResultSet
-    * @exception SQLException if a database-access error occurs.
-    * @see org.postgresql.LargeObjectManager
-    */
-   public void addFunctions(ResultSet rs) throws SQLException
-   {
-       while (rs.next())
-       {
-           func.put(rs.getString(1), new Integer(rs.getInt(2)));
-       }
-   }
+        /*
+         * This adds a function to our lookup table.
+         *
+         * 

User code should use the addFunctions method, which is based upon a

+         * query, rather than hard coding the oid. The oid for a function is not
+         * guaranteed to remain static, even on different servers of the same
+         * version.
+         *
+         * @param name Function name
+         * @param fnid Function id
+         */
+        public void addFunction(String name, int fnid)
+        {
+                func.put(name, new Integer(fnid));
+        }
 
-   /*
-    * This returns the function id associated by its name
-    *
-    * 

If addFunction() or addFunctions() have not been called for this name,

-    * then an SQLException is thrown.
-    *
-    * @param name Function name to lookup
-    * @return Function ID for fastpath call
-    * @exception SQLException is function is unknown.
-    */
-   public int getID(String name) throws SQLException
-   {
-       Integer id = (Integer)func.get(name);
+        /*
+         * This takes a ResultSet containing two columns. Column 1 contains the
+         * function name, Column 2 the oid.
+         *
+         * 

It reads the entire ResultSet, loading the values into the function

+         * table.
+         *
+         * 

REMEMBER to close() the resultset after calling this!!

+         *
+         * 

Implementation note about function name lookups:

+         *
+         * 

PostgreSQL stores the function id's and their corresponding names in

+         * the pg_proc table. To speed things up locally, instead of querying each
+         * function from that table when required, a Hashtable is used. Also, only
+         * the function's required are entered into this table, keeping connection
+         * times as fast as possible.
+         *
+         * 

The org.postgresql.LargeObject class performs a query upon it's startup,

+         * and passes the returned ResultSet to the addFunctions() method here.
+         *
+         * 

Once this has been done, the LargeObject api refers to the functions by

+         * name.
+         *
+         * 

Dont think that manually converting them to the oid's will work. Ok,

+         * they will for now, but they can change during development (there was some
+         * discussion about this for V7.0), so this is implemented to prevent any
+         * unwarranted headaches in the future.
+         *
+         * @param rs ResultSet
+         * @exception SQLException if a database-access error occurs.
+         * @see org.postgresql.LargeObjectManager
+         */
+        public void addFunctions(ResultSet rs) throws SQLException
+        {
+                while (rs.next())
+                {
+                        func.put(rs.getString(1), new Integer(rs.getInt(2)));
+                }
+        }
 
-       // may be we could add a lookup to the database here, and store the result
-       // in our lookup table, throwing the exception if that fails.
-       // We must, however, ensure that if we do, any existing ResultSet is
-       // unaffected, otherwise we could break user code.
-       //
-       // so, until we know we can do this (needs testing, on the TODO list)
-       // for now, we throw the exception and do no lookups.
-       if (id == null)
-           throw new PSQLException("postgresql.fp.unknown", name);
+        /*
+         * This returns the function id associated by its name
+         *
+         * 

If addFunction() or addFunctions() have not been called for this name,

+         * then an SQLException is thrown.
+         *
+         * @param name Function name to lookup
+         * @return Function ID for fastpath call
+         * @exception SQLException is function is unknown.
+         */
+        public int getID(String name) throws SQLException
+        {
+                Integer id = (Integer)func.get(name);
 
-       return id.intValue();
-   }
+                // may be we could add a lookup to the database here, and store the result
+                // in our lookup table, throwing the exception if that fails.
+                // We must, however, ensure that if we do, any existing ResultSet is
+                // unaffected, otherwise we could break user code.
+                //
+                // so, until we know we can do this (needs testing, on the TODO list)
+                // for now, we throw the exception and do no lookups.
+                if (id == null)
+                        throw new PSQLException("postgresql.fp.unknown", name);
+
+                return id.intValue();
+        }
 }
 
index e3ff6393940c9ec52162eac95414bb373b119579..0d62c74c9ad779f3b79b773520aead7e508cad4a 100644 (file)
@@ -17,7 +17,7 @@ import org.postgresql.largeobject.*;
 import org.postgresql.util.*;
 
 /*
- * $Id: Connection.java,v 1.17 2002/01/15 06:55:13 barry Exp $
+ * $Id: Connection.java,v 1.18 2002/03/26 05:52:50 barry Exp $
  *
  * A Connection represents a session with a specific database. Within the
  * context of a Connection, SQL statements are executed and results are
@@ -36,307 +36,287 @@ import org.postgresql.util.*;
  */
 public class Connection extends org.postgresql.Connection implements java.sql.Connection
 {
-   // This is a cache of the DatabaseMetaData instance for this connection
-   protected DatabaseMetaData metadata;
+        // This is a cache of the DatabaseMetaData instance for this connection
+        protected DatabaseMetaData metadata;
 
-   /*
-    * The current type mappings
-    */
-   protected java.util.Map typemap;
+        /*
+         * The current type mappings
+         */
+        protected java.util.Map typemap;
 
-   /*
-    * SQL statements without parameters are normally executed using
-    * Statement objects.  If the same SQL statement is executed many
-    * times, it is more efficient to use a PreparedStatement
-    *
-    * @return a new Statement object
-    * @exception SQLException passed through from the constructor
-    */
-   public java.sql.Statement createStatement() throws SQLException
-   {
-       // The spec says default of TYPE_FORWARD_ONLY but everyone is used to
-       // using TYPE_SCROLL_INSENSITIVE
-       return createStatement(java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE, java.sql.ResultSet.CONCUR_READ_ONLY);
-   }
+        /*
+         * SQL statements without parameters are normally executed using
+         * Statement objects.  If the same SQL statement is executed many
+         * times, it is more efficient to use a PreparedStatement
+         *
+         * @return a new Statement object
+         * @exception SQLException passed through from the constructor
+         */
+        public java.sql.Statement createStatement() throws SQLException
+        {
+                // The spec says default of TYPE_FORWARD_ONLY but everyone is used to
+                // using TYPE_SCROLL_INSENSITIVE
+                return createStatement(java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE, java.sql.ResultSet.CONCUR_READ_ONLY);
+        }
 
-   /*
-    * SQL statements without parameters are normally executed using
-    * Statement objects.  If the same SQL statement is executed many
-    * times, it is more efficient to use a PreparedStatement
-    *
-    * @param resultSetType to use
-    * @param resultSetCuncurrency to use
-    * @return a new Statement object
-    * @exception SQLException passed through from the constructor
-    */
-   public java.sql.Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException
-   {
-       Statement s = new Statement(this);
-       s.setResultSetType(resultSetType);
-       s.setResultSetConcurrency(resultSetConcurrency);
-       return s;
-   }
+        /*
+         * SQL statements without parameters are normally executed using
+         * Statement objects.  If the same SQL statement is executed many
+         * times, it is more efficient to use a PreparedStatement
+         *
+         * @param resultSetType to use
+         * @param resultSetCuncurrency to use
+         * @return a new Statement object
+         * @exception SQLException passed through from the constructor
+         */
+        public java.sql.Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException
+        {
+                Statement s = new Statement(this);
+                s.setResultSetType(resultSetType);
+                s.setResultSetConcurrency(resultSetConcurrency);
+                return s;
+        }
 
 
-   /*
-    * A SQL statement with or without IN parameters can be pre-compiled
-    * and stored in a PreparedStatement object.  This object can then
-    * be used to efficiently execute this statement multiple times.
-    *
-    * Note: This method is optimized for handling parametric
-    * SQL statements that benefit from precompilation if the drivers
-    * supports precompilation.  PostgreSQL does not support precompilation.
-    * In this case, the statement is not sent to the database until the
-    * PreparedStatement is executed.  This has no direct effect on users;
-    * however it does affect which method throws certain SQLExceptions
-    *
-    * @param sql a SQL statement that may contain one or more '?' IN
-    *  parameter placeholders
-    * @return a new PreparedStatement object containing the pre-compiled
-    *  statement.
-    * @exception SQLException if a database access error occurs.
-    */
-   public java.sql.PreparedStatement prepareStatement(String sql) throws SQLException
-   {
-       return prepareStatement(sql, java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE, java.sql.ResultSet.CONCUR_READ_ONLY);
-   }
+        /*
+         * A SQL statement with or without IN parameters can be pre-compiled
+         * and stored in a PreparedStatement object.  This object can then
+         * be used to efficiently execute this statement multiple times.
+         *
+         Note: This method is optimized for handling parametric
+         * SQL statements that benefit from precompilation if the drivers
+         * supports precompilation.  PostgreSQL does not support precompilation.
+         * In this case, the statement is not sent to the database until the
+         * PreparedStatement is executed.  This has no direct effect on users;
+         * however it does affect which method throws certain SQLExceptions
+         *
+         * @param sql a SQL statement that may contain one or more '?' IN
+    *       parameter placeholders
+         * @return a new PreparedStatement object containing the pre-compiled
+    *       statement.
+         * @exception SQLException if a database access error occurs.
+         */
+        public java.sql.PreparedStatement prepareStatement(String sql) throws SQLException
+        {
+                return prepareStatement(sql, java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE, java.sql.ResultSet.CONCUR_READ_ONLY);
+        }
 
-   public java.sql.PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
-   {
-       PreparedStatement s = new PreparedStatement(this, sql);
-       s.setResultSetType(resultSetType);
-       s.setResultSetConcurrency(resultSetConcurrency);
-       return s;
-   }
+        public java.sql.PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
+        {
+                PreparedStatement s = new PreparedStatement(this, sql);
+                s.setResultSetType(resultSetType);
+                s.setResultSetConcurrency(resultSetConcurrency);
+                return s;
+        }
 
-   /*
-    * A SQL stored procedure call statement is handled by creating a
-    * CallableStatement for it.  The CallableStatement provides methods
-    * for setting up its IN and OUT parameters and methods for executing
-    * it.
-    *
-    * Note: This method is optimised for handling stored procedure
-    * call statements.  Some drivers may send the call statement to the
-    * database when the prepareCall is done; others may wait until the
-    * CallableStatement is executed.  This has no direct effect on users;
-    * however, it does affect which method throws certain SQLExceptions
-    *
-    * @param sql a SQL statement that may contain one or more '?' parameter
-    *  placeholders.  Typically this statement is a JDBC function call
-    *  escape string.
-    * @return a new CallableStatement object containing the pre-compiled
-    *  SQL statement
-    * @exception SQLException if a database access error occurs
-    */
-   public java.sql.CallableStatement prepareCall(String sql) throws SQLException
-   {
-       return prepareCall(sql, java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE, java.sql.ResultSet.CONCUR_READ_ONLY);
-   }
+        /*
+         * A SQL stored procedure call statement is handled by creating a
+         * CallableStatement for it.  The CallableStatement provides methods
+         * for setting up its IN and OUT parameters and methods for executing
+         * it.
+         *
+         Note: This method is optimised for handling stored procedure
+         * call statements.  Some drivers may send the call statement to the
+         * database when the prepareCall is done; others may wait until the
+         * CallableStatement is executed.  This has no direct effect on users;
+         * however, it does affect which method throws certain SQLExceptions
+         *
+         * @param sql a SQL statement that may contain one or more '?' parameter
+    *       placeholders.  Typically this statement is a JDBC function call
+    *       escape string.
+         * @return a new CallableStatement object containing the pre-compiled
+    *       SQL statement
+         * @exception SQLException if a database access error occurs
+         */
+        public java.sql.CallableStatement prepareCall(String sql) throws SQLException
+        {
+                return prepareCall(sql, java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE, java.sql.ResultSet.CONCUR_READ_ONLY);
+        }
 
-   public java.sql.CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
-   {
-       throw new PSQLException("postgresql.con.call");
-       //CallableStatement s = new CallableStatement(this,sql);
-       //s.setResultSetType(resultSetType);
-       //s.setResultSetConcurrency(resultSetConcurrency);
-       //return s;
-   }
+        public java.sql.CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
+        {
+                throw new PSQLException("postgresql.con.call");
+                //CallableStatement s = new CallableStatement(this,sql);
+                //s.setResultSetType(resultSetType);
+                //s.setResultSetConcurrency(resultSetConcurrency);
+                //return s;
+        }
 
-   /*
-    * Tests to see if a Connection is closed.
-    *
-    * Peter Feb 7 2000: Now I've discovered that this doesn't actually obey the
-    * specifications. Under JDBC2.1, this should only be valid _after_ close()
-    * has been called. It's result is not guraranteed to be valid before, and
-    * client code should not use it to see if a connection is open. The spec says
-    * that the client should monitor the SQLExceptions thrown when their queries
-    * fail because the connection is dead.
-    *
-    * I don't like this definition. As it doesn't hurt breaking it here, our
-    * isClosed() implementation does test the connection, so for PostgreSQL, you
-    * can rely on isClosed() returning a valid result.
-    *
-    * @return the status of the connection
-    * @exception SQLException (why?)
-    */
-   public boolean isClosed() throws SQLException
-   {
-       // If the stream is gone, then close() was called
-       if (pg_stream == null)
-           return true;
+        /*
+         * Tests to see if a Connection is closed.
+         *
+         * Peter Feb 7 2000: Now I've discovered that this doesn't actually obey the
+         * specifications. Under JDBC2.1, this should only be valid _after_ close()
+         * has been called. It's result is not guraranteed to be valid before, and
+         * client code should not use it to see if a connection is open. The spec says
+         * that the client should monitor the SQLExceptions thrown when their queries
+         * fail because the connection is dead.
+         *
+         * I don't like this definition. As it doesn't hurt breaking it here, our
+         * isClosed() implementation does test the connection, so for PostgreSQL, you
+         * can rely on isClosed() returning a valid result.
+         *
+         * @return the status of the connection
+         * @exception SQLException (why?)
+         */
+        public boolean isClosed() throws SQLException
+        {
+                // If the stream is gone, then close() was called
+                if (pg_stream == null)
+                        return true;
+                return false;
+        }
 
-       // ok, test the connection
-       try
-       {
-           // by sending an empty query. If we are dead, then an SQLException should
-           // be thrown
-           java.sql.ResultSet rs = ExecSQL(" ");
-           if (rs != null)
-               rs.close();
+        /*
+         * A connection's database is able to provide information describing
+         * its tables, its supported SQL grammar, its stored procedures, the
+         * capabilities of this connection, etc.  This information is made
+         * available through a DatabaseMetaData object.
+         *
+         * @return a DatabaseMetaData object for this connection
+         * @exception SQLException if a database access error occurs
+         */
+        public java.sql.DatabaseMetaData getMetaData() throws SQLException
+        {
+                if (metadata == null)
+                        metadata = new DatabaseMetaData(this);
+                return metadata;
+        }
 
-           // By now, we must be alive
-           return false;
-       }
-       catch (SQLException se)
-       {
-           // Why throw an SQLException as this may fail without throwing one,
-           // ie isClosed() is called incase the connection has died, and we don't
-           // want to find out by an Exception, so instead we return true, as its
-           // most likely why it was thrown in the first place.
-           return true;
-       }
-   }
+        /*
+         * This overides the method in org.postgresql.Connection and returns a
+         * ResultSet.
+         */
+        public java.sql.ResultSet getResultSet(org.postgresql.Connection conn, java.sql.Statement stat, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) throws SQLException
+        {
+                // In 7.1 we now test concurrency to see which class to return. If we are not working with a
+                // Statement then default to a normal ResultSet object.
+                if (stat != null)
+                {
+                        if (stat.getResultSetConcurrency() == java.sql.ResultSet.CONCUR_UPDATABLE)
+                                return new org.postgresql.jdbc2.UpdateableResultSet((org.postgresql.jdbc2.Connection)conn, fields, tuples, status, updateCount, insertOID, binaryCursor);
+                }
 
-   /*
-    * A connection's database is able to provide information describing
-    * its tables, its supported SQL grammar, its stored procedures, the
-    * capabilities of this connection, etc.  This information is made
-    * available through a DatabaseMetaData object.
-    *
-    * @return a DatabaseMetaData object for this connection
-    * @exception SQLException if a database access error occurs
-    */
-   public java.sql.DatabaseMetaData getMetaData() throws SQLException
-   {
-       if (metadata == null)
-           metadata = new DatabaseMetaData(this);
-       return metadata;
-   }
+                return new org.postgresql.jdbc2.ResultSet((org.postgresql.jdbc2.Connection)conn, fields, tuples, status, updateCount, insertOID, binaryCursor);
+        }
 
-   /*
-    * This overides the method in org.postgresql.Connection and returns a
-    * ResultSet.
-    */
-   public java.sql.ResultSet getResultSet(org.postgresql.Connection conn, java.sql.Statement stat, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) throws SQLException
-   {
-       // In 7.1 we now test concurrency to see which class to return. If we are not working with a
-       // Statement then default to a normal ResultSet object.
-       if (stat != null)
-       {
-           if (stat.getResultSetConcurrency() == java.sql.ResultSet.CONCUR_UPDATABLE)
-               return new org.postgresql.jdbc2.UpdateableResultSet((org.postgresql.jdbc2.Connection)conn, fields, tuples, status, updateCount, insertOID, binaryCursor);
-       }
+        // *****************
+        // JDBC 2 extensions
+        // *****************
 
-       return new org.postgresql.jdbc2.ResultSet((org.postgresql.jdbc2.Connection)conn, fields, tuples, status, updateCount, insertOID, binaryCursor);
-   }
+        public java.util.Map getTypeMap() throws SQLException
+        {
+                // new in 7.1
+                return typemap;
+        }
 
-   // *****************
-   // JDBC 2 extensions
-   // *****************
 
-   public java.util.Map getTypeMap() throws SQLException
-   {
-       // new in 7.1
-       return typemap;
-   }
+        public void setTypeMap(java.util.Map map) throws SQLException
+        {
+                // new in 7.1
+                typemap = map;
+        }
 
+        /*
+         * This overides the standard internal getObject method so that we can
+         * check the jdbc2 type map first
+         *
+         * @return PGobject for this type, and set to value
+         * @exception SQLException if value is not correct for this type
+         * @see org.postgresql.util.Serialize
+         */
+        public Object getObject(String type, String value) throws SQLException
+        {
+                if (typemap != null)
+                {
+                        SQLData d = (SQLData) typemap.get(type);
+                        if (d != null)
+                        {
+                                // Handle the type (requires SQLInput & SQLOutput classes to be implemented)
+                                throw org.postgresql.Driver.notImplemented();
+                        }
+                }
 
-   public void setTypeMap(java.util.Map map) throws SQLException
-   {
-       // new in 7.1
-       typemap = map;
-   }
+                // Default to the original method
+                return super.getObject(type, value);
+        }
 
-   /*
-    * This overides the standard internal getObject method so that we can
-    * check the jdbc2 type map first
-    *
-    * @return PGobject for this type, and set to value
-    * @exception SQLException if value is not correct for this type
-    * @see org.postgresql.util.Serialize
-    */
-   public Object getObject(String type, String value) throws SQLException
-   {
-       if (typemap != null)
-       {
-           SQLData d = (SQLData) typemap.get(type);
-           if (d != null)
-           {
-               // Handle the type (requires SQLInput & SQLOutput classes to be implemented)
-               throw org.postgresql.Driver.notImplemented();
-           }
-       }
+        /* An implementation of the abstract method in the parent class.
+         * This implemetation uses the jdbc2Types array to support the jdbc2
+         * datatypes.  Basically jdbc1 and jdbc2 are the same, except that
+         * jdbc2 adds the Array types.
+         */
+        public int getSQLType(String pgTypeName)
+        {
+                int sqlType = Types.OTHER; // default value
+                for (int i = 0;i < jdbc2Types.length;i++)
+                {
+                        if (pgTypeName.equals(jdbc2Types[i]))
+                        {
+                                sqlType = jdbc2Typei[i];
+                                break;
+                        }
+                }
+                return sqlType;
+        }
 
-       // Default to the original method
-       return super.getObject(type, value);
-   }
+        /*
+         * This table holds the org.postgresql names for the types supported.
+         * Any types that map to Types.OTHER (eg POINT) don't go into this table.
+         * They default automatically to Types.OTHER
+         *
+         * Note: This must be in the same order as below.
+         *
+         * Tip: keep these grouped together by the Types. value
+         */
+        private static final String jdbc2Types[] = {
+                                "int2",
+                                "int4", "oid",
+                                "int8",
+                                "cash", "money",
+                                "numeric",
+                                "float4",
+                                "float8",
+                                "bpchar", "char", "char2", "char4", "char8", "char16",
+                                "varchar", "text", "name", "filename",
+                                "bytea",
+                                "bool",
+                                "date",
+                                "time",
+                                "abstime", "timestamp", "timestamptz",
+                                "_bool", "_char", "_int2", "_int4", "_text",
+                                "_oid", "_varchar", "_int8", "_float4", "_float8",
+                                "_abstime", "_date", "_time", "_timestamp", "_numeric",
+                                "_bytea"
+                        };
 
-   /* An implementation of the abstract method in the parent class.
-    * This implemetation uses the jdbc2Types array to support the jdbc2
-    * datatypes.  Basically jdbc1 and jdbc2 are the same, except that
-    * jdbc2 adds the Array types.
-    */
-   public int getSQLType(String pgTypeName)
-   {
-       int sqlType = Types.OTHER; // default value
-       for (int i = 0;i < jdbc2Types.length;i++)
-       {
-           if (pgTypeName.equals(jdbc2Types[i]))
-           {
-               sqlType = jdbc2Typei[i];
-               break;
-           }
-       }
-       return sqlType;
-   }
-
-   /*
-    * This table holds the org.postgresql names for the types supported.
-    * Any types that map to Types.OTHER (eg POINT) don't go into this table.
-    * They default automatically to Types.OTHER
-    *
-    * Note: This must be in the same order as below.
-    *
-    * Tip: keep these grouped together by the Types. value
-    */
-   private static final String jdbc2Types[] = {
-               "int2",
-               "int4", "oid",
-               "int8",
-               "cash", "money",
-               "numeric",
-               "float4",
-               "float8",
-               "bpchar", "char", "char2", "char4", "char8", "char16",
-               "varchar", "text", "name", "filename",
-               "bytea",
-               "bool",
-               "date",
-               "time",
-               "abstime", "timestamp", "timestamptz",
-               "_bool", "_char", "_int2", "_int4", "_text",
-               "_oid", "_varchar", "_int8", "_float4", "_float8",
-               "_abstime", "_date", "_time", "_timestamp", "_numeric",
-               "_bytea"
-           };
-
-   /*
-    * This table holds the JDBC type for each entry above.
-    *
-    * Note: This must be in the same order as above
-    *
-    * Tip: keep these grouped together by the Types. value
-    */
-   private static final int jdbc2Typei[] = {
-                                               Types.SMALLINT,
-                                               Types.INTEGER, Types.INTEGER,
-                                               Types.BIGINT,
-                                               Types.DOUBLE, Types.DOUBLE,
-                                               Types.NUMERIC,
-                                               Types.REAL,
-                                               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.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
-                                           };
+        /*
+         * This table holds the JDBC type for each entry above.
+         *
+         * Note: This must be in the same order as above
+         *
+         * Tip: keep these grouped together by the Types. value
+         */
+        private static final int jdbc2Typei[] = {
+                                                                                                Types.SMALLINT,
+                                                                                                Types.INTEGER, Types.INTEGER,
+                                                                                                Types.BIGINT,
+                                                                                                Types.DOUBLE, Types.DOUBLE,
+                                                                                                Types.NUMERIC,
+                                                                                                Types.REAL,
+                                                                                                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.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
+                                                                                        };
 
 
 }