]> granicus.if.org Git - postgresql/commitdiff
Two versions of QueryExecutor, currently only version 2 works 100%
authorDave Cramer <davec@fastcrypt.com>
Thu, 21 Mar 2002 03:20:30 +0000 (03:20 +0000)
committerDave Cramer <davec@fastcrypt.com>
Thu, 21 Mar 2002 03:20:30 +0000 (03:20 +0000)
these versions adhere to the backend protocol better than previous version
fixes problem when an error occurs on the backend, and the connection is still used
previous versions were throwing an exception half way through the protocol, leaving it
indeterminate.
also removes empty query code, should speed things up a bit

src/interfaces/jdbc/org/postgresql/core/QueryExecutor.java
src/interfaces/jdbc/org/postgresql/core/QueryExecutor2.java [new file with mode: 0644]

index c1cc04de3ae50c8e5bb97eae4369c8a88fd7fdde..237aa49684f653ee0991dedd119f2e9780613069 100644 (file)
@@ -13,7 +13,7 @@ import org.postgresql.util.PSQLException;
  * <p>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.10 2002/03/18 04:16:33 davec Exp $
+ * $Id: QueryExecutor.java,v 1.11 2002/03/21 03:20:30 davec Exp $
  */
 
 public class QueryExecutor
@@ -104,7 +104,6 @@ public class QueryExecutor
 
                                                errorMessage.append(pg_stream.ReceiveString(connection.getEncoding()));
                                                // keep processing
-                                               hfr = true;
                                                break;
 
                                        case 'I':       // Empty Query
diff --git a/src/interfaces/jdbc/org/postgresql/core/QueryExecutor2.java b/src/interfaces/jdbc/org/postgresql/core/QueryExecutor2.java
new file mode 100644 (file)
index 0000000..f48c95c
--- /dev/null
@@ -0,0 +1,259 @@
+\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
+ * <p>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