Commit ef7d7910 authored by Barry Lind's avatar Barry Lind

Changed QueryExecutor.java to correctly read responses from the backend.

Fixed NPE when database name was not passed on the jdbc connection URL
Fixed Connection.isClosed() to not hit the DB for every call
parent af3c3801
......@@ -11,7 +11,7 @@ import org.postgresql.util.*;
import org.postgresql.core.*;
/*
* $Id: Connection.java,v 1.44 2002/03/21 02:39:06 davec Exp $
* $Id: Connection.java,v 1.45 2002/03/26 05:52:48 barry Exp $
*
* This abstract class is used by org.postgresql.Driver to open either the JDBC1 or
* JDBC2 versions of the Connection class.
......@@ -59,10 +59,6 @@ public abstract class Connection
private static final int AUTH_REQ_CRYPT = 4;
private static final int AUTH_REQ_MD5 = 5;
public final static int PGASYNC_IDLE = 0; /* nothing's happening, dude */
public final static int PGASYNC_BUSY = 1; /* query in progress */
public final static int PGASYNC_READY = 2; /* result ready for PQgetResult */
// These are used to cache oids, PGTypes and SQLTypes
private static Hashtable sqlTypeCache = new Hashtable(); // oid -> SQLType
......@@ -81,7 +77,6 @@ public abstract class Connection
public int pid;
public int ckey;
public int asyncStatus = PGASYNC_READY;
/*
* This is called by Class.forName() from within org.postgresql.Driver
*/
......@@ -427,7 +422,7 @@ public abstract class Connection
*/
public java.sql.ResultSet ExecSQL(String sql, java.sql.Statement stat) throws SQLException
{
return new QueryExecutor2(sql, stat, pg_stream, this).execute();
return new QueryExecutor(sql, stat, pg_stream, this).execute();
}
/*
......
......@@ -419,7 +419,7 @@ public class Driver implements java.sql.Driver
*/
public String database()
{
return props.getProperty("PGDBNAME");
return props.getProperty("PGDBNAME", "");
}
/*
......
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.
*
* <p>The lifetime of a QueryExecutor object is from sending the query
* until the response has been received from the backend.
*
* $Id: QueryExecutor2.java,v 1.1 2002/03/21 03:20:29 davec Exp $
*/
public class QueryExecutor2
{
private final String sql;
private final java.sql.Statement statement;
private final PG_Stream pg_stream;
private final org.postgresql.Connection connection;
public QueryExecutor2(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);
connection.asyncStatus = org.postgresql.Connection.PGASYNC_BUSY;
while ( connection.asyncStatus != org.postgresql.Connection.PGASYNC_IDLE )
{
int c = pg_stream.ReceiveChar();
if ( c == 'A' )
{
int pid = pg_stream.ReceiveIntegerR(4);
String msg = pg_stream.ReceiveString(connection.getEncoding());
org.postgresql.Driver.debug(msg);
continue;
}
else if ( c == 'N' )
{
String notification = pg_stream.ReceiveString(connection.getEncoding());
org.postgresql.Driver.debug(notification);
connection.addWarning(notification);
continue;
}
else if ( connection.asyncStatus != org.postgresql.Connection.PGASYNC_BUSY )
{
if ( connection.asyncStatus != org.postgresql.Connection.PGASYNC_IDLE )
{
// only one possibility left which is PGASYNC_READY, so let's get out
break;
}
if ( c == 'E' ) {
String error = pg_stream.ReceiveString(connection.getEncoding());
org.postgresql.Driver.debug(error);
// no sense in creating this object until we really need it
if ( errorMessage == null ) {
errorMessage = new StringBuffer();
}
errorMessage.append(error);
break;
}
}else{
switch (c)
{
case 'C': // Command Status
receiveCommandStatus();
break;
case 'E': // Error Message
// it's possible to get multiple error messages from one query
// see libpq, there are some comments about a connection being closed
// by the backend after real error occurs, so append error messages here
// so append them and just remember that an error occured
// throw the exception at the end of processing
String error = pg_stream.ReceiveString(connection.getEncoding());
org.postgresql.Driver.debug(error);
// no sense in creating this object until we really need it
if ( errorMessage == null ) {
errorMessage = new StringBuffer();
}
errorMessage.append(error);
connection.asyncStatus = org.postgresql.Connection.PGASYNC_READY;
break;
case 'Z': // backend ready for query, ignore for now :-)
connection.asyncStatus = org.postgresql.Connection.PGASYNC_IDLE;
break;
case 'I': // Empty Query
int t = pg_stream.ReceiveChar();
if (t != 0)
throw new PSQLException("postgresql.con.garbled");
connection.asyncStatus = org.postgresql.Connection.PGASYNC_READY;
break;
case 'P': // Portal Name
String pname = pg_stream.ReceiveString(connection.getEncoding());
org.postgresql.Driver.debug(pname);
break;
case 'T': // MetaData Field Description
receiveFields();
break;
case 'B': // Binary Data Transfer
receiveTuple(true);
break;
case 'D': // Text Data Transfer
receiveTuple(false);
break;
default:
throw new PSQLException("postgresql.con.type",
new Character((char) c));
}
}
}
// did we get an error message?
if ( errorMessage != null ) {
// yes, throw an exception
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);
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment