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

Reply via email to