Re: Attempt to clean up ExecSql() in JDBC

Поиск
Список
Период
Сортировка
Искать
От
Barry Lind
Тема
Re: Attempt to clean up ExecSql() in JDBC
Дата
Msg-id
3B8B30B1.40008@xythos.com
Список
Дерево обсуждения
Attempt to clean up ExecSql() in JDBC Anders Bengtsson <ndrsbngtssn@yahoo.se>
Re: [JDBC] Attempt to clean up ExecSql() in JDBC Bruce Momjian <pgman@candle.pha.pa.us>
Re: [JDBC] Attempt to clean up ExecSql() in JDBC Bruce Momjian <pgman@candle.pha.pa.us>
I looked at the patch and it looks fine.  As for what fqp and hfr stand 
for I don't have a clue.  I was looking through the fe/be protocol 
documentation and think I might have a clue about what they are being 
used for.

thanks,
--Barry

Anders Bengtsson wrote:
> Hi,
> 
> Attached is my attempt to clean up the horrors of the ExecSQL() method in
> the JDBC driver.
> 
> I've done this by extracting it into a new method object called
> QueryExecutor (should go into org/postgresql/core/) and then taking it
> apart into different methods in that class.
> 
> A short summary:
> 
> * Extracted ExecSQL() from Connection into a method object called
>   QueryExecutor.
> 
> * Moved ReceiveFields() from Connection to QueryExecutor.
> 
> * Extracted parts of the original ExecSQL() method body into smaller
>   methods on QueryExecutor.
> 
> * Bug fix: The instance variable "pid" in Connection was used in two
>   places with different meaning. Both were probably in dead code, but it's
>   fixed anyway.
> 
> /Anders
> 
> 
> PS.: If anyone has any idea what the variable names "fqp" and "hfr" stand
> for, please tell me! :)
> 
> _____________________________________________________________________
> A n d e r s  B e n g t s s o n                   ndrsbngtssn@yahoo.se
> Stockholm, Sweden
> 
> 
> ------------------------------------------------------------------------
> 
> Index: src/interfaces/jdbc/org/postgresql/Connection.java
> ===================================================================
> RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/Connection.java,v
> retrieving revision 1.26
> diff -c -r1.26 Connection.java
> *** src/interfaces/jdbc/org/postgresql/Connection.java	2001/08/24 16:50:12	1.26
> --- src/interfaces/jdbc/org/postgresql/Connection.java	2001/08/26 18:33:48
> ***************
> *** 8,14 ****
>   import org.postgresql.fastpath.*;
>   import org.postgresql.largeobject.*;
>   import org.postgresql.util.*;
> ! import org.postgresql.core.Encoding;
>   
>   /**
>    * $Id: Connection.java,v 1.26 2001/08/24 16:50:12 momjian Exp $
> --- 8,14 ----
>   import org.postgresql.fastpath.*;
>   import org.postgresql.largeobject.*;
>   import org.postgresql.util.*;
> ! import org.postgresql.core.*;
>   
>   /**
>    * $Id: Connection.java,v 1.26 2001/08/24 16:50:12 momjian Exp $
> ***************
> *** 348,513 ****
>        * @return a ResultSet holding the results
>        * @exception SQLException if a database error occurs
>        */
> !     public java.sql.ResultSet ExecSQL(String sql,java.sql.Statement stat) throws SQLException
>       {
> !       // added Jan 30 2001 to correct maxrows per statement
> !       int maxrows=0;
> !       if(stat!=null)
> !         maxrows=stat.getMaxRows();
> ! 
> ! 	// added Oct 7 1998 to give us thread safety.
> ! 	synchronized(pg_stream) {
> !  	    // Deallocate all resources in the stream associated
> !   	    // with a previous request.
> !   	    // This will let the driver reuse byte arrays that has already
> !   	    // been allocated instead of allocating new ones in order
> !   	    // to gain performance improvements.
> !   	    // PM 17/01/01: Commented out due to race bug. See comments in
> !             // PG_Stream
> !             //pg_stream.deallocate();
> ! 
> ! 	    Field[] fields = null;
> ! 	    Vector tuples = new Vector();
> ! 	    byte[] buf = null;
> ! 	    int fqp = 0;
> ! 	    boolean hfr = false;
> ! 	    String recv_status = null, msg;
> ! 	    int update_count = 1;
> ! 	    int insert_oid = 0;
> ! 	    SQLException final_error = null;
> ! 
> ! 	    buf = encoding.encode(sql);
> ! 	    try
> ! 		{
> ! 		    pg_stream.SendChar('Q');
> ! 		    pg_stream.Send(buf);
> ! 		    pg_stream.SendChar(0);
> ! 		    pg_stream.flush();
> ! 		} catch (IOException e) {
> ! 		    throw new PSQLException("postgresql.con.ioerror",e);
> ! 		}
> ! 
> ! 	    while (!hfr || fqp > 0)
> ! 		{
> ! 		    Object tup=null;	// holds rows as they are recieved
> ! 
> ! 		    int c = pg_stream.ReceiveChar();
> ! 
> ! 		    switch (c)
> ! 			{
> ! 			case 'A':	// Asynchronous Notify
> ! 			    pid = pg_stream.ReceiveInteger(4);
> !                             msg = pg_stream.ReceiveString(encoding);
> ! 			    break;
> ! 			case 'B':	// Binary Data Transfer
> ! 			    if (fields == null)
> ! 				throw new PSQLException("postgresql.con.tuple");
> ! 			    tup = pg_stream.ReceiveTuple(fields.length, true);
> ! 			    // This implements Statement.setMaxRows()
> ! 			    if(maxrows==0 || tuples.size() ! 				tuples.addElement(tup);
> ! 			    break;
> ! 			case 'C':	// Command Status
> !                             recv_status = pg_stream.ReceiveString(encoding);
> ! 
> ! 				// Now handle the update count correctly.
> ! 				if(recv_status.startsWith("INSERT") || recv_status.startsWith("UPDATE") || recv_status.startsWith("DELETE") || recv_status.startsWith("MOVE")) {
> ! 					try {
> ! 						update_count = Integer.parseInt(recv_status.substring(1+recv_status.lastIndexOf(' ')));
> ! 					} catch(NumberFormatException nfe) {
> ! 						throw new PSQLException("postgresql.con.fathom",recv_status);
> ! 					}
> ! 					if(recv_status.startsWith("INSERT")) {
> ! 					    try {
> ! 						insert_oid = Integer.parseInt(recv_status.substring(1+recv_status.indexOf(' '),recv_status.lastIndexOf(' ')));
> ! 					    } catch(NumberFormatException nfe) {
> ! 						throw new PSQLException("postgresql.con.fathom",recv_status);
> ! 					    }
> ! 					}
> ! 				}
> ! 			    if (fields != null)
> ! 				hfr = true;
> ! 			    else
> ! 				{
> ! 				    try
> ! 					{
> ! 					    pg_stream.SendChar('Q');
> ! 					    pg_stream.SendChar(' ');
> ! 					    pg_stream.SendChar(0);
> ! 					    pg_stream.flush();
> ! 					} catch (IOException e) {
> ! 					    throw new PSQLException("postgresql.con.ioerror",e);
> ! 					}
> ! 				    fqp++;
> ! 				}
> ! 			    break;
> ! 			case 'D':	// Text Data Transfer
> ! 			    if (fields == null)
> ! 				throw new PSQLException("postgresql.con.tuple");
> ! 			    tup = pg_stream.ReceiveTuple(fields.length, false);
> ! 			    // This implements Statement.setMaxRows()
> ! 			    if(maxrows==0 || tuples.size() ! 				tuples.addElement(tup);
> ! 			    break;
> ! 			case 'E':	// Error Message
> !                             msg = pg_stream.ReceiveString(encoding);
> ! 			    final_error = new SQLException(msg);
> ! 			    hfr = true;
> ! 			    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
> !                             addWarning(pg_stream.ReceiveString(encoding));
> ! 			    break;
> ! 			case 'P':	// Portal Name
> !                             String pname = pg_stream.ReceiveString(encoding);
> ! 			    break;
> ! 			case 'T':	// MetaData Field Description
> ! 			    if (fields != null)
> ! 				throw new PSQLException("postgresql.con.multres");
> ! 			    fields = ReceiveFields();
> ! 			    break;
> ! 			case 'Z':       // backend ready for query, ignore for now :-)
> ! 			    break;
> ! 			default:
> ! 			    throw new PSQLException("postgresql.con.type",new Character((char)c));
> ! 			}
> ! 		}
> ! 	    if (final_error != null)
> ! 		throw final_error;
> ! 
> ! 	    return getResultSet(this, stat, fields, tuples, recv_status, update_count, insert_oid);
> ! 	}
> !     }
> ! 
> !     /**
> !      * Receive the field descriptions from the back end
> !      *
> !      * @return an array of the Field object describing the fields
> !      * @exception SQLException if a database error occurs
> !      */
> !     private Field[] ReceiveFields() throws SQLException
> !     {
> ! 	int nf = pg_stream.ReceiveIntegerR(2), i;
> ! 	Field[] fields = new Field[nf];
> ! 
> ! 	for (i = 0 ; i < nf ; ++i)
> ! 	    {
> !                 String typname = pg_stream.ReceiveString(encoding);
> ! 		int typid = pg_stream.ReceiveIntegerR(4);
> ! 		int typlen = pg_stream.ReceiveIntegerR(2);
> ! 		int typmod = pg_stream.ReceiveIntegerR(4);
> ! 		fields[i] = new Field(this, typname, typid, typlen, typmod);
> ! 	    }
> ! 	return fields;
>       }
>   
>       /**
> --- 348,356 ----
>        * @return a ResultSet holding the results
>        * @exception SQLException if a database error occurs
>        */
> !     public java.sql.ResultSet ExecSQL(String sql, java.sql.Statement stat) throws SQLException
>       {
> ! 	return new QueryExecutor(sql, stat, pg_stream, this).execute();
>       }
>   
>       /**
> ***************
> *** 793,799 ****
>        * This returns a resultset. It must be overridden, so that the correct
>        * version (from jdbc1 or jdbc2) are returned.
>        */
> !     protected abstract java.sql.ResultSet getResultSet(org.postgresql.Connection conn,java.sql.Statement stat, Field[] fields, Vector tuples, String status, int updateCount,int insertOID) throws SQLException;
>   
>       /**
>        * In some cases, it is desirable to immediately release a Connection's
> --- 636,642 ----
>        * This returns a resultset. It must be overridden, so that the correct
>        * version (from jdbc1 or jdbc2) are returned.
>        */
> !     public abstract java.sql.ResultSet getResultSet(org.postgresql.Connection conn,java.sql.Statement stat, Field[] fields, Vector tuples, String status, int updateCount,int insertOID) throws SQLException;
>   
>       /**
>        * In some cases, it is desirable to immediately release a Connection's
> Index: src/interfaces/jdbc/org/postgresql/jdbc1/Connection.java
> ===================================================================
> RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Connection.java,v
> retrieving revision 1.8
> diff -c -r1.8 Connection.java
> *** src/interfaces/jdbc/org/postgresql/jdbc1/Connection.java	2001/08/24 16:50:15	1.8
> --- src/interfaces/jdbc/org/postgresql/jdbc1/Connection.java	2001/08/26 18:33:48
> ***************
> *** 131,137 ****
>        * This overides the method in org.postgresql.Connection and returns a
>        * ResultSet.
>        */
> !     protected java.sql.ResultSet getResultSet(org.postgresql.Connection conn,java.sql.Statement stat, Field[] fields, Vector tuples, String status, int updateCount,int insertOID) throws SQLException
>       {
>         // in jdbc1 stat is ignored.
>   	return new org.postgresql.jdbc1.ResultSet((org.postgresql.jdbc1.Connection)conn,fields,tuples,status,updateCount,insertOID);
> --- 131,137 ----
>        * 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,int insertOID) throws SQLException
>       {
>         // in jdbc1 stat is ignored.
>   	return new org.postgresql.jdbc1.ResultSet((org.postgresql.jdbc1.Connection)conn,fields,tuples,status,updateCount,insertOID);
> Index: src/interfaces/jdbc/org/postgresql/jdbc2/Connection.java
> ===================================================================
> RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc2/Connection.java,v
> retrieving revision 1.10
> diff -c -r1.10 Connection.java
> *** src/interfaces/jdbc/org/postgresql/jdbc2/Connection.java	2001/08/24 16:50:16	1.10
> --- src/interfaces/jdbc/org/postgresql/jdbc2/Connection.java	2001/08/26 18:33:49
> ***************
> *** 204,210 ****
>        * This overides the method in org.postgresql.Connection and returns a
>        * ResultSet.
>        */
> !     protected java.sql.ResultSet getResultSet(org.postgresql.Connection conn, java.sql.Statement stat,Field[] fields, Vector tuples, String status, int updateCount, int insertOID) 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.
> --- 204,210 ----
>        * 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, int insertOID) 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.
> 
> 
> ------------------------------------------------------------------------
> 
> 
> package org.postgresql.core;
> 
> import java.util.Vector;
> import java.io.IOException;
> import java.sql.*;
> import org.postgresql.*;
> import org.postgresql.util.PSQLException;
> 
> /**
>  * Executes a query on the backend.
>  *
>  * 

The lifetime of a QueryExecutor object is from sending the query > * until the response has been received from the backend. > * > * $Id$ > */ > > 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 String status = null; > private int update_count = 1; > private int 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; > > 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 > throw new SQLException(pg_stream.ReceiveString(connection.getEncoding())); > 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)); > } > } > > return connection.getResultSet(connection, statement, fields, tuples, status, update_count, insert_oid); > } > } > > /** > * 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 (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 = Integer.parseInt(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); > } > } > } > > > ------------------------------------------------------------------------ > > > ---------------------------(end of broadcast)--------------------------- > TIP 2: you can get off all lists at once with the unregister command > (send "unregister YourEmailAddressHere" to majordomo@postgresql.org) > > execsql.patch > > Content-Type: > > TEXT/PLAIN > Content-Encoding: > > BASE64 > > > ------------------------------------------------------------------------ > QueryExecutor.java > > Content-Type: > > TEXT/PLAIN > Content-Encoding: > > BASE64 > > > ------------------------------------------------------------------------ > Part 1.4 > > Content-Type: > > text/plain > Content-Encoding: > > binary > >

В списке pgsql-patches по дате отправления
От: Martijn van Oosterhout
Дата:
От: Bruce Momjian
Дата:
FAQ