I've added parsing of the V3 protocol's notice and error messages into its
various fields. This replaces the current error message format of:
SERRORC42P01Mrelation "this_table_doesnt_exist" does not
existFnamespace.cL193RRangeVarGetRelid
with the correct message and allows the programmer to retrieve the
SQLState via the standard getSQLState call.
One thing that I am concerned about is that the message fields are
separated by null bytes, but what happens when we are using an encoding
that allows embedded nulls?
Kris Jurka
? src/interfaces/jdbc/org/postgresql/core/PGErrorMessage.java
? src/interfaces/jdbc/org/postgresql/jdbc1/.AbstractJdbc1Statement.java.swp
Index: src/interfaces/jdbc/org/postgresql/core/BaseStatement.java
===================================================================
RCS file:
/projects/cvsroot/pgsql-server/src/interfaces/jdbc/org/postgresql/core/BaseStatement.java,v
retrieving revision 1.5
diff -c -r1.5 BaseStatement.java
*** src/interfaces/jdbc/org/postgresql/core/BaseStatement.java 24 Aug 2003 22:10:09
-0000 1.5
--- src/interfaces/jdbc/org/postgresql/core/BaseStatement.java 8 Sep 2003 12:11:12
-0000
***************
*** 28,34 ****
* any ResultSet can contain. If the limit is exceeded, the
* excess rows are silently dropped.
*/
! public void addWarning(String p_warning) throws SQLException;
public void close() throws SQLException;
public int getFetchSize() throws SQLException;
public int getMaxFieldSize() throws SQLException;
--- 28,34 ----
* any ResultSet can contain. If the limit is exceeded, the
* excess rows are silently dropped.
*/
! public void addWarning(SQLWarning p_warning) throws SQLException;
public void close() throws SQLException;
public int getFetchSize() throws SQLException;
public int getMaxFieldSize() throws SQLException;
Index: src/interfaces/jdbc/org/postgresql/core/QueryExecutor.java
===================================================================
RCS file:
/projects/cvsroot/pgsql-server/src/interfaces/jdbc/org/postgresql/core/QueryExecutor.java,v
retrieving revision 1.23
diff -c -r1.23 QueryExecutor.java
*** src/interfaces/jdbc/org/postgresql/core/QueryExecutor.java 11 Aug 2003 21:18:47
-0000 1.23
--- src/interfaces/jdbc/org/postgresql/core/QueryExecutor.java 8 Sep 2003 12:11:13
-0000
***************
*** 107,114 ****
private BaseResultSet executeV3() throws SQLException
{
!
! StringBuffer errorMessage = null;
if (pgStream == null)
{
--- 107,113 ----
private BaseResultSet executeV3() throws SQLException
{
! SQLException sqlException = null;
if (pgStream == null)
{
***************
*** 145,158 ****
// 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();
! int l_elen =
pgStream.ReceiveIntegerR(4);
!
errorMessage.append(connection.getEncoding().decode(pgStream.Receive(l_elen-4)));
// keep processing
break;
case 'I': // Empty Query
--- 144,160 ----
// it's possible to get more than one
error message for a query
// see libpq comments wrt backend
closing a connection
! // so, chain together the exceptions
and throw the top level
! // one at the end of the loop.
! int l_elen =
pgStream.ReceiveIntegerR(4);
! PGErrorMessage error = new
PGErrorMessage(pgStream.Receive(l_elen-4),connection.getEncoding());
! SQLException exception = new
SQLException(error.getMessage(),error.getSQLState());
! if (sqlException == null) {
! sqlException = exception;
! } else {
!
sqlException.setNextException(exception);
! }
// keep processing
break;
case 'I': // Empty Query
***************
*** 160,171 ****
break;
case 'N': // Error Notification
int l_nlen =
pgStream.ReceiveIntegerR(4);
!
statement.addWarning(connection.getEncoding().decode(pgStream.Receive(l_nlen-4)));
break;
case 'P': // Portal Name
String pname =
pgStream.ReceiveString(connection.getEncoding());
break;
! case 'S':
//TODO: handle parameter status
messages
int l_len =
pgStream.ReceiveIntegerR(4);
String l_pStatus =
connection.getEncoding().decode(pgStream.Receive(l_len-4));
--- 162,175 ----
break;
case 'N': // Error Notification
int l_nlen =
pgStream.ReceiveIntegerR(4);
! PGErrorMessage notice = new
PGErrorMessage(pgStream.Receive(l_nlen-4),connection.getEncoding());
! SQLWarning warning = new
SQLWarning(notice.getMessage(), notice.getSQLState());
! statement.addWarning(warning);
break;
case 'P': // Portal Name
String pname =
pgStream.ReceiveString(connection.getEncoding());
break;
! case 'S':
//TODO: handle parameter status
messages
int l_len =
pgStream.ReceiveIntegerR(4);
String l_pStatus =
connection.getEncoding().decode(pgStream.Receive(l_len-4));
***************
*** 191,198 ****
}
// did we get an error during this query?
! if ( errorMessage != null )
! throw new SQLException( errorMessage.toString().trim()
);
//if an existing result set was passed in reuse it, else
--- 195,202 ----
}
// did we get an error during this query?
! if ( sqlException != null)
! throw sqlException;
//if an existing result set was passed in reuse it, else
***************
*** 263,269 ****
int t = pgStream.ReceiveChar();
break;
case 'N': // Error Notification
!
statement.addWarning(pgStream.ReceiveString(connection.getEncoding()));
break;
case 'P': // Portal Name
String pname =
pgStream.ReceiveString(connection.getEncoding());
--- 267,273 ----
int t = pgStream.ReceiveChar();
break;
case 'N': // Error Notification
! statement.addWarning(new
SQLWarning(pgStream.ReceiveString(connection.getEncoding())));
break;
case 'P': // Portal Name
String pname =
pgStream.ReceiveString(connection.getEncoding());
Index: src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java
===================================================================
RCS file:
/projects/cvsroot/pgsql-server/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java,v
retrieving revision 1.33
diff -c -r1.33 AbstractJdbc1Statement.java
*** src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java 26 Aug
2003 06:50:39 -0000 1.33
--- src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java 8 Sep
2003 12:11:17 -0000
***************
*** 597,610 ****
/**
* This adds a warning to the warning chain.
! * @param msg message to add
*/
! public void addWarning(String msg)
{
if (warnings != null)
! warnings.setNextWarning(new SQLWarning(msg));
else
! warnings = new SQLWarning(msg);
}
/*
--- 597,610 ----
/**
* This adds a warning to the warning chain.
! * @param warn warning to add
*/
! public void addWarning(SQLWarning warn)
{
if (warnings != null)
! warnings.setNextWarning(warn);
else
! warnings = warn;
}
/*
package org.postgresql.core;
import java.util.Hashtable;
import java.sql.SQLException;
/**
* This class parses raw error and notice messages from the V3 Protocol into
* the various fields they may contain. More information on these fields is
* containted in the main PostgreSQL documentation under Internals,
* Frontend/Backend Protocol, Error and Notice Message Fields.
*
* The different fields are separated by an embedded null (0) byte. I'm not
* sure how this will handle messages in encodings that allow embedded nulls.
*
*/
class PGErrorMessage {
private String severity;
private String sqlState;
private String message;
private String detail;
private String hint;
private String position;
private String where;
private String file;
private String line;
private String routine;
public PGErrorMessage(byte rawMessage[], Encoding encoding) throws
SQLException {
Hashtable fields = split(rawMessage,encoding);
severity = (String)fields.get("S");
sqlState = (String)fields.get("C");
message = (String)fields.get("M");
detail = (String)fields.get("D");
hint = (String)fields.get("H");
position = (String)fields.get("P");
where = (String)fields.get("W");
file = (String)fields.get("F");
line = (String)fields.get("L");
routine = (String)fields.get("R");
}
public String getSeverity() {
return severity;
}
public String getSQLState() {
return sqlState;
}
public String getMessage() {
return message;
}
public String getDetail() {
return detail;
}
public String getHint() {
return hint;
}
public String getPosition() {
return position;
}
public String getWhere() {
return where;
}
public String getFile() {
return file;
}
public String getLine() {
return line;
}
public String getRoutine() {
return routine;
}
/**
* Split the fields in the raw message into their label and content.
*/
private static Hashtable split(byte message[], Encoding encoding) throws
SQLException {
Hashtable fields = new Hashtable();
int start = 0;
for (int i=0; i<message.length; i++) {
if (message[i] == 0 && i>start) {
byte label[] = new byte[1];
byte content[] = new byte[i-start-1];
label[0] = message[start];
for (int j=start+1; j<i; j++) {
content[j-start-1] = message[j];
}
fields.put(encoding.decode(label),encoding.decode(content));
start = i+1;
}
}
return fields;
}
public String toString() {
StringBuffer sb = new StringBuffer();
String eol = System.getProperty("line.separator");
sb.append("Severity: ");
sb.append(severity);
sb.append(eol);
sb.append("SQLState: ");
sb.append(sqlState);
sb.append(eol);
sb.append("Message: ");
sb.append(message);
sb.append(eol);
sb.append("Detail: ");
sb.append(detail);
sb.append(eol);
sb.append("Hint: ");
sb.append(hint);
sb.append(eol);
sb.append("Position: ");
sb.append(position);
sb.append(eol);
sb.append("Where: ");
sb.append(where);
sb.append(eol);
sb.append("File: ");
sb.append(file);
sb.append(eol);
sb.append("Line: ");
sb.append(line);
sb.append(eol);
sb.append("Routine: ");
sb.append(routine);
return sb.toString();
}
}
---------------------------(end of broadcast)---------------------------
TIP 4: Don't 'kill -9' the postmaster