diff --git a/doc/src/sgml/ecpg.sgml b/doc/src/sgml/ecpg.sgml
index ee386339286f8598a5888f1e8601109db8de9352..7c4de83b30b5d2ea0043bcccdc82060d9306e836 100644
--- a/doc/src/sgml/ecpg.sgml
+++ b/doc/src/sgml/ecpg.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ecpg.sgml,v 1.46 2003/08/01 03:10:04 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ecpg.sgml,v 1.47 2003/08/01 13:53:36 petere Exp $
 -->
 
 <chapter id="ecpg">
@@ -37,15 +37,16 @@ $Header: /cvsroot/pgsql/doc/src/sgml/ecpg.sgml,v 1.46 2003/08/01 03:10:04 momjia
    Embedded <acronym>SQL</acronym> has advantages over other methods
    for handling <acronym>SQL</acronym> commands from C code. First, it
    takes care of the tedious passing of information to and from
-   variables in your <acronym>C</acronym> program.  Secondly, embedded
-   <acronym>SQL</acronym> in C is defined in the
-   <acronym>SQL</acronym> standard and supported by many other
-   <acronym>SQL</acronym> databases.  The <productname>PostgreSQL</>
-   implementation is designed to match this standard as much as
-   possible, and it is usually possible to port embedded
-   <acronym>SQL</acronym> programs written for other
-   SQL databases to <productname>PostgreSQL</productname>
-   with relative ease.
+   variables in your <acronym>C</acronym> program.  Second, the SQL
+   code in the program is checked at build time for syntactical
+   correctness.  Third, embedded <acronym>SQL</acronym> in C is
+   specified in the <acronym>SQL</acronym> standard and supported by
+   many other <acronym>SQL</acronym> database systems.  The
+   <productname>PostgreSQL</> implementation is designed to match this
+   standard as much as possible, and it is usually possible to port
+   embedded <acronym>SQL</acronym> programs written for other SQL
+   databases to <productname>PostgreSQL</productname> with relative
+   ease.
   </para>
 
   <para>
@@ -100,13 +101,13 @@ EXEC SQL CONNECT TO <replaceable>target</replaceable> <optional>AS <replaceable>
  
     <listitem>
      <simpara>
-      <literal><replaceable>character variable</replaceable></literal>
+      an SQL string literal containing one of the above forms
      </simpara>
     </listitem>
 
     <listitem>
      <simpara>
-      <literal><replaceable>character string</replaceable></literal>
+      a reference to a character variable containing one of the above forms (see examples)
      </simpara>
     </listitem>
  
@@ -116,6 +117,16 @@ EXEC SQL CONNECT TO <replaceable>target</replaceable> <optional>AS <replaceable>
      </simpara>
     </listitem>
    </itemizedlist>
+
+   If you specify the connection target literally (that is, not
+   through a variable reference) and you don't quote the value, then
+   the case-insensitivity rules of normal SQL are applied.  In that
+   case you can also double-quote the individual parameters separately
+   as needed.  In practice, it is probably less error-prone to use a
+   (single-quoted) string literal or a variable reference.  The
+   connection target <literal>DEFAULT</literal> initiates a connection
+   to the default database under the default user name.  No separate
+   user name or connection name may be specified in that case.
   </para>
 
   <para>
@@ -146,15 +157,47 @@ EXEC SQL CONNECT TO <replaceable>target</replaceable> <optional>AS <replaceable>
      </simpara>
     </listitem>
    </itemizedlist>
-   The <replaceable>username</replaceable> and
-   <replaceable>password</replaceable> may be an SQL name, a
-   character variable, or a character string.
+
+   As above, the parameters <replaceable>username</replaceable> and
+   <replaceable>password</replaceable> may be an SQL identifier, an
+   SQL string literal, or a reference to a character variable.
   </para>
 
   <para>
    The <replaceable>connection-name</replaceable> is used to handle
    multiple connections in one program.  It can be omitted if a
-   program uses only one connection.
+   program uses only one connection.  The most recently opened
+   connection becomes the current connection, which is used by default
+   when an SQL statement is to be executed (see later in this
+   chapter).
+  </para>
+
+  <para>
+   Here are some examples of <command>CONNECT</command> statements:
+<programlisting>
+EXEC SQL CONNECT TO mydb@sql.mydomain.com;
+
+EXEC SQL CONNECT TO 'unix:postgresql://sql.mydomain.com/mydb' AS myconnection USER john;
+
+EXEC SQL BEGIN DECLARE SECTION;
+const char *target = "mydb@sql.mydomain.com";
+const char *user = "john";
+EXEC SQL END DECLARE SECTION;
+ ...
+EXEC SQL CONNECT TO :target USER :user;
+</programlisting>
+   The last form makes use of the variant referred to above as
+   character variable reference.  You will see in later sections how C
+   variables can be used in SQL statements when you prefix them with a
+   colon.
+  </para>
+
+  <para>
+   Be advised that the format of the connection target is not
+   specified in the SQL standard.  So if you want to develop portable
+   applications, you might want to use something based on the last
+   example above to encapsulate the connection target string
+   somewhere.
   </para>
  </sect1>
 
@@ -194,6 +237,14 @@ EXEC SQL DISCONNECT <optional><replaceable>connection</replaceable></optional>;
      </simpara>
     </listitem>
    </itemizedlist>
+
+   If no connection name is specified, the current connection is
+   closed.
+  </para>
+
+  <para>
+   It is good style that an application always explicitly disconnect
+   from every connection it opened.
   </para>
  </sect1>
 
@@ -231,14 +282,14 @@ EXEC SQL COMMIT;
   </para>
 
   <para>
-   Singleton Select:
+   Single-row Select:
 <programlisting>
 EXEC SQL SELECT foo INTO :FooBar FROM table1 WHERE ascii = 'doodad';
 </programlisting>
   </para>
 
   <para>
-   Select using Cursors:
+   Select using cursors:
 <programlisting>
 EXEC SQL DECLARE foo_bar CURSOR FOR
     SELECT number, ascii FROM foo
@@ -264,8 +315,8 @@ EXEC SQL COMMIT;
    The tokens of the form
    <literal>:<replaceable>something</replaceable></literal> are
    <firstterm>host variables</firstterm>, that is, they refer to
-   variables in the C program.  They are explained in the next
-   section.
+   variables in the C program.  They are explained in <xref
+   linkend="ecpg-variables">.
   </para>
 
   <para>
@@ -281,65 +332,512 @@ EXEC SQL COMMIT;
   </para>
  </sect1>
 
- <sect1 id="ecpg-variables">
-  <title>Passing Data</title>
+ <sect1 id="ecpg-set-connection">
+  <title>Choosing a Connection</title>
+
+  <para>
+   The SQL statements shown in the previous section are executed on
+   the current connection, that is, the most recently opened one.  If
+   an application needs to manage multiple connections, then there are
+   two ways to handle this.
+  </para>
+
+  <para>
+   The first option is to explicitly choose a connection for each SQL
+   statement, for example
+<programlisting>
+EXEC SQL AT <replaceable>connection-name</replaceable> SELECT ...;
+</programlisting>
+   This option is particularly suitable if the application needs to
+   use several connections in mixed order.
+  </para>
 
   <para>
-   To pass data from the program to the database, for example as
-   parameters in a query, or to pass data from the database back to
-   the program, the C variables that are intended to contain this data
-   need to be declared in a specially marked section, so the embedded
-   SQL preprocessor is made aware of them.
+   The second option is to execute a statement to switch the current
+   connection.  That statement is:
+<programlisting>
+SET CONNECTION <replaceable>connection-name</replaceable>;
+</programlisting>
+   This option is particularly convenient if many statements are to be
+   executed on the same connection.
   </para>
+ </sect1>
+
+ <sect1 id="ecpg-variables">
+  <title>Using Host Variables</title>
 
   <para>
-   This section starts with
+   In <xref linkend="ecpg-commands"> you saw how you can execute SQL
+   statements from an embedded SQL program.  Some of those statements
+   only used fixed values and did not provide a way to insert
+   user-supplied values into statements or have the program process
+   the values returned by the query.  Those kinds of statements are
+   not really useful in real applications.  This section explains in
+   detail how you can pass data between your C program and the
+   embedded SQL statements using a simple mechanism called
+   <firstterm>host variables</firstterm>.
+  </para>
+
+  <sect2>
+   <title>Overview</title>
+
+   <para>
+    Passing data between the C program and the SQL statements is
+    particularly simple in embedded SQL.  Instead of having the
+    program paste the data into the statement, which entails various
+    complications, such as properly quoting the value, you can simply
+    write the name of a C variable into the SQL statement, prefixed by
+    a colon.  For example:
+<programlisting>
+INSERT INTO sometable VALUES (:v1, 'foo', :v2);
+</programlisting>
+    This statements refers to two C variables named
+    <varname>v1</varname> and <varname>v2</varname> and also uses a
+    regular SQL string literal, to illustrate that you are not
+    restricted to use one kind of data or the other.
+   </para>
+
+   <para>
+    This style of inserting C variables in SQL statements works
+    anywhere a value expression is expected in an SQL statement.  In
+    the SQL environment we call the references to C variables
+    <firstterm>host variables</firstterm>.
+   </para>
+  </sect2>
+
+  <sect2>
+   <title>Declare Sections</title>
+
+   <para>
+    To pass data from the program to the database, for example as
+    parameters in a query, or to pass data from the database back to
+    the program, the C variables that are intended to contain this
+    data need to be declared in specially marked sections, so the
+    embedded SQL preprocessor is made aware of them.
+   </para>
+
+   <para>
+    This section starts with
 <programlisting>
 EXEC SQL BEGIN DECLARE SECTION;
 </programlisting>
-   and ends with
+    and ends with
 <programlisting>
 EXEC SQL END DECLARE SECTION;
 </programlisting>
-   Between those lines, there must be normal C variable declarations, such as
+    Between those lines, there must be normal C variable declarations,
+    such as
 <programlisting>
 int   x;
 char  foo[16], bar[16];
 </programlisting>
+    You can have as many declare sections in a program as you like.
+   </para>
+
+   <para>
+    The declarations are also echoed to the output file as a normal C
+    variables, so there's no need to declare them again.  Variables
+    that are not intended to be used with SQL commands can be declared
+    normally outside these special sections.
+   </para>
+
+   <para>
+    The definition of a structure or union also must be listed inside
+    a <literal>DECLARE</> section. Otherwise the preprocessor cannot
+    handle these types since it does not know the definition.
+   </para>
+
+   <para>
+    The special types <type>VARCHAR</type> and <type>VARCHAR2</type>
+    are converted into a named <type>struct</> for every variable. A
+    declaration like
+<programlisting>
+VARCHAR var[180];
+</programlisting>
+    is converted into
+<programlisting>
+struct varchar_var { int len; char arr[180]; } var;
+</programlisting>
+    This structure is suitable for interfacing with SQL datums of type
+    <type>varchar</type>.
+   </para>
+  </sect2>
+
+  <sect2>
+   <title><command>SELECT INTO</command> and <command>FETCH INTO</command></title>
+
+   <para>
+    Now you should be able to pass data generated by your program into
+    an SQL command.  But how do you retrieve the results of a query?
+    For that purpose, embedded SQL provides special variants of the
+    usual commands <command>SELECT</command> and
+    <command>FETCH</command>.  These commands have a special
+    <literal>INTO</literal> clause that specifies which host variables
+    the retrieved values are to be stored in.
+   </para>
+
+   <para>
+    Here is an example:
+<programlisting>
+/*
+ * assume this table:
+ * CREATE TABLE test1 (a int, b varchar(50));
+ */
+
+EXEC SQL BEGIN DECLARE SECTION;
+int v1;
+VARCHAR v2;
+EXEC SQL END DECLARE SECTION;
+
+ ...
+
+EXEC SQL SELECT a, b INTO :v1, :v2 FROM test;
+</programlisting>
+    So the <literal>INTO</literal> clause appears between the select
+    list and the <literal>FROM</literal> clause.  The number of
+    elements in the select list and the list after
+    <literal>INTO</literal> (also called the target list) must be
+    equal.
+   </para>
+
+   <para>
+    Here is an example using the command <command>FETCH</command>:
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+int v1;
+VARCHAR v2;
+EXEC SQL END DECLARE SECTION;
+
+ ...
+
+EXEC SQL DECLARE foo CURSOR FOR SELECT a, b FROM test;
+
+ ...
+
+do {
+    ...
+    EXEC SQL FETCH NEXT FROM foo INTO :v1, :v2;
+    ...
+} while (...);
+</programlisting>
+    Here the <literal>INTO</literal> clause appears after all the
+    normal clauses.
+   </para>
+
+   <para>
+    Both of these methods only allow retrieving one row at a time.  If
+    you need to process result sets that potentially contain more than
+    one row, you need to use a cursor, as shown in the second example.
+   </para>
+  </sect2>
+
+  <sect2>
+   <title>Indicators</title>
+
+   <para>
+    The examples above do not handle null values.  In fact, the
+    retrieval examples will raise an error if they fetch a null value
+    from the database.  To be able to pass null values to the database
+    or retrieve null values from the database, you need to append a
+    second host variable specification to each host variable that
+    contains data.  This second host variable is called the
+    <firstterm>indicator</firstterm> and contains a flag that tells
+    whether the datums is null, in which case the value of the real
+    host variable is ignored.  Here is an example that handles the
+    retrieval of null values correctly:
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+VARCHAR val;
+int val_ind;
+EXEC SQL END DECLARE SECTION:
+
+ ...
+
+EXEC SQL SELECT b INTO :val :val_ind FROM test1;
+</programlisting>
+    The indicator variable <varname>val_ind</varname> will be zero if
+    the value was not null, and it will be negative if the value was
+    null.
+   </para>
+
+   <para>
+    The indicator has another function: if the indicator value is
+    positive, it means that the value is not null, but it was
+    truncated when it was stored in the host variable.
+   </para>
+  </sect2>
+ </sect1>
+
+ <sect1 id="ecpg-dynamic">
+  <title>Dynamic SQL</title>
+
+  <para>
+   In many cases, the particular SQL statements that an application
+   has to execute are known at the time the application is written.
+   In some cases, however, the SQL statements are composed at run time
+   or provided by an external source.  In these cases you cannot embed
+   the SQL statements directly into the C source code, but there is a
+   facility that allows you to call arbitrary SQL statements that you
+   provide in a string variable.
   </para>
 
   <para>
-   The declarations are also echoed to the output file as a normal C
-   variables, so there's no need to declare them again.  Variables
-   that are not intended to be used with SQL commands can be declared
-   normally outside these special sections.
+   The simplest way to execute an arbitrary SQL statement is to use
+   the command <command>EXECUTE IMMEDIATE</command>.  For example:
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+const char *stmt = "CREATE TABLE test1 (...);";
+EXEC SQL END DECLARE SECTION;
+
+EXECUTE IMMEDIATE :stmt;
+</programlisting>
+   You may not execute statements that retrieve data (e.g.,
+   <command>SELECT</command>) this way.
   </para>
 
   <para>
-   The definition of a structure or union also must be listed inside a
-   <literal>DECLARE</> section. Otherwise the preprocessor cannot
-   handle these types since it does not know the definition.
+   A more powerful way to execute arbitrary SQL statements is to
+   prepare them once and execute the prepared statement as often as
+   you like.  It is also possible to prepare a generalized version of
+   a statement and then execute specific versions of it by
+   substituting parameters.  When preparing the statement, write
+   question marks where you want to substitute parameters later.  For
+   example:
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+const char *stmt = "INSERT INTO test1 VALUES(?, ?);";
+EXEC SQL END DECLARE SECTION;
+
+PREPARE mystmt FROM :stmt;
+ ...
+EXECUTE mystmt USING 42, 'foobar';
+</programlisting>
+   If the statement you are executing returns values, then add an
+   <literal>INTO</literal> clause:
+<programlisting>
+EXEC SQL BEGIN DECLARE SECTION;
+const char *stmt = "SELECT a, b, c FROM test1 WHERE a > ?";
+int v1, v2;
+VARCHAR v3;
+EXEC SQL END DECLARE SECTION;
+
+PREPARE mystmt FROM :stmt;
+ ...
+EXECUTE mystmt INTO v1, v2, v3 USING 37;
+</programlisting>
+   An <command>EXECUTE</command> command may have an
+   <literal>INTO</literal> clause, a <literal>USING</literal> clause,
+   both, or neither.
   </para>
 
   <para>
-   The special types <type>VARCHAR</type> and <type>VARCHAR2</type>
-   are converted into a named <type>struct</> for every variable. A
-   declaration like
+   When you don't need the prepared statement anymore, you should
+   deallocate it:
 <programlisting>
-VARCHAR var[180];
+EXEC SQL DEALLOCATE PREPARE <replaceable>name</replaceable>;
 </programlisting>
-   is converted into
+  </para>
+ </sect1>
+
+ <sect1 id="ecpg-descriptors">
+  <title>Using SQL Descriptor Areas</title>
+
+  <para>
+   An SQL descriptor area is a more sophisticated method for
+   processing the result of a <command>SELECT</command> or
+   <command>FETCH</command> statement.  An SQL descriptor area groups
+   the data of one row of data together with metadata items into one
+   data structure.  The metadata is particularly useful when executing
+   dynamic SQL statements, where the nature of the result columns may
+   not be known ahead of time.
+  </para>
+
+  <para>
+   An SQL descriptor area consists of a header, which contains
+   information concerning the entire descriptor, and one or more item
+   descriptor areas, which basically each describe one column in the
+   result row.
+  </para>
+
+  <para>
+   Before you can use an SQL descriptor area, you need to allocate one:
 <programlisting>
-struct varchar_var { int len; char arr[180]; } var;
+EXEC SQL ALLOCATE DESCRIPTOR <replaceable>identifier</replaceable>;
+</programlisting>
+   The identifier serves as the <quote>variable name</quote> of the
+   descriptor area.  The scope of the allocated descriptor is WHAT?.
+   When you don't need the descriptor anymore, you should deallocate
+   it:
+<programlisting>
+EXEC SQL DEALLOCATE DESCRIPTOR <replaceable>identifier</replaceable>;
 </programlisting>
-   This structure is suitable for interfacing with SQL datums of type
-   <type>varchar</type>.
   </para>
 
   <para>
-   To use a properly declared C variable in an SQL statement, write
-   <literal>:<replaceable>varname</></literal> where an expression is
-   expected.  See the previous section for some examples.
+   To use a descriptor area, specify it as the storage target in an
+   <literal>INTO</literal> clause, instead of listing host variables:
+<programlisting>
+EXEC SQL FETCH NEXT FROM mycursor INTO DESCRIPTOR mydesc;
+</programlisting>
+  </para>
+
+  <para>
+   Now how do you get the data out of the descriptor area?  You can
+   think of the descriptor area as a structure with named fields.  To
+   retrieve the value of a field from the header and store it into a
+   host variable, use the following command:
+<programlisting>
+EXEC SQL GET DESCRIPTOR <replaceable>name</replaceable> :<replaceable>hostvar</replaceable> = <replaceable>field</replaceable>;
+</programlisting>
+   Currently, there is only one header field defined:
+   <replaceable>COUNT</replaceable>, which tells how many item
+   descriptor areas exist (that is, how many columns are contained in
+   the result).  The host variable needs to be of an integer type.  To
+   get a field from the item descriptor area, use the following
+   command:
+<programlisting>
+EXEC SQL GET DESCRIPTOR <replaceable>name</replaceable> VALUE <replaceable>num</replaceable> :<replaceable>hostvar</replaceable> = <replaceable>field</replaceable>;
+</programlisting>
+   <replaceable>num</replaceable> can be a literal integer or a host
+   variable containing an integer. Possible fields are:
+
+   <variablelist>
+    <varlistentry>
+     <term><literal>CARDINALITY</literal> (integer)<term>
+     <listitem>
+      <para>
+       number of rows in the result set
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><literal>DATA</literal><term>
+     <listitem>
+      <para>
+       actual data item (therefore, the data type of this field
+       depends on the query)
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><literal>DATETIME_INTERVAL_CODE</literal> (integer)<term>
+     <listitem>
+      <para>
+       ?
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><literal>DATETIME_INTERVAL_PRECISION</literal> (integer)<term>
+     <listitem>
+      <para>
+       not implemented
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><literal>INDICATOR</literal> (integer)<term>
+     <listitem>
+      <para>
+       the indicator (indicating a null value or a value truncation)
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><literal>KEY_MEMBER</literal> (integer)<term>
+     <listitem>
+      <para>
+       not implemented
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><literal>LENGTH</literal> (integer)<term>
+     <listitem>
+      <para>
+       length of the datum in characters
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><literal>NAME</literal> (string)<term>
+     <listitem>
+      <para>
+       name of the column
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><literal>NULLABLE</literal> (integer)<term>
+     <listitem>
+      <para>
+       not implemented
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><literal>OCTET_LENGTH</literal> (integer)<term>
+     <listitem>
+      <para>
+       length of the character representation of the datum in bytes
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><literal>PRECISION</literal> (integer)<term>
+     <listitem>
+      <para>
+       precision (for type <type>numeric</type>)
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><literal>RETURNED_LENGTH</literal> (integer)<term>
+     <listitem>
+      <para>
+       length of the datum in characters
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><literal>RETURNED_OCTET_LENGTH</literal> (integer)<term>
+     <listitem>
+      <para>
+       length of the character representation of the datum in bytes
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><literal>SCALE</literal> (integer)<term>
+     <listitem>
+      <para>
+       scale (for type <type>numeric</type>)
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><literal>TYPE</literal> (integer)<term>
+     <listitem>
+      <para>
+       numeric code of the data type of the column
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
   </para>
  </sect1>
 
@@ -347,35 +845,198 @@ struct varchar_var { int len; char arr[180]; } var;
   <title>Error Handling</title>
 
   <para>
-   The embedded SQL interface provides a simplistic and a complex way
-   to handle exceptional conditions in a program.  The first method
-   causes a message to printed automatically when a certain condition
-   occurs.  For example:
+   This section describes how you can handle exceptional conditions
+   and warnings in an embedded SQL program.  There are several
+   nonexclusive facilities for this.
+  </para>
+
+  <sect2>
+   <title>Setting Callbacks</title>
+
+   <para>
+    One simple method to catch errors and warnings is to set a
+    specific action to be executed whenever a particular condition
+    occurs.  In general:
 <programlisting>
-EXEC SQL WHENEVER sqlerror sqlprint;
+EXEC SQL WHENEVER <replaceable>condition</replaceable> <replaceable>action</replaceable>;
 </programlisting>
-   or
+   </para>
+
+   <para>
+    <replaceable>condition</replaceable> can be one of the following:
+
+    <variablelist>
+     <varlistentry>
+      <term><literal>SQLERROR</literal></term>
+      <listitem>
+       <para>
+        The specified action is called whenever an error occurs during
+        the execution of an SQL statement.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><literal>SQLWARNING</literal></term>
+      <listitem>
+       <para>
+        The specified action is called whenever a warning occurs
+        during the execution of an SQL statement.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><literal>NOT FOUND</literal></term>
+      <listitem>
+       <para>
+        The specified action is called whenever an SQL statement
+        retrieves or affects zero rows.  (This condition is not an
+        error, but you might be interested in handling it specially.)
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
+   </para>
+
+   <para>
+    <replaceable>action</replaceable> can be one of the following:
+
+    <variablelist>
+     <varlistentry>
+      <term><literal>CONTINUE</literal></term>
+      <listitem>
+       <para>
+        This effectively means that the condition is ignored.  This is
+        the default.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><literal>GOTO <replaceable>label</replaceable></literal></term>
+      <term><literal>GO TO <replaceable>label</replaceable></literal></term>
+      <listitem>
+       <para>
+        Jump to the specified label (using a C <literal>goto</literal>
+        statement).
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><literal>SQLPRINT</literal></term>
+      <listitem>
+       <para>
+        Print a message to standard error.  This is useful for simple
+        programs or during prototyping.  The details of the message
+        cannot be configured.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><literal>STOP</literal></term>
+      <listitem>
+       <para>
+        Call <literal>exit(1)</literal>, which will terminate the
+        program.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><literal>BREAK</literal></term>
+      <listitem>
+       <para>
+        Execute the C statement <literal>break</literal>.  This should
+        only be used in loops or <literal>switch</literal> statements.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><literal>CALL <replaceable>name</replaceable> (<replaceable>args</replaceable>)</literal></term>
+      <term><literal>DO <replaceable>name</replaceable> (<replaceable>args</replaceable>)</literal></term>
+      <listitem>
+       <para>
+        Call the specified C functions with the specified arguments.
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
+
+    The SQL standard only provides for the actions
+    <literal>CONTINUE</literal> and <literal>GOTO</literal> (and
+    <literal>GO TO</literal>).
+   </para>
+
+   <para>
+    Here is an example that you might want to use in a simple program.
+    It prints a simple message when a warning occurs and aborts the
+    program when an error happens.
 <programlisting>
-EXEC SQL WHENEVER not found sqlprint;
+EXEC SQL WHENEVER SQLWARNING SQLPRINT;
+EXEC SQL WHENEVER SQLERROR STOP;
 </programlisting>
-   This error handling remains enabled throughout the entire program.
-  </para>
+   </para>
 
-  <note>
    <para>
-    This is <emphasis>not</emphasis> an exhaustive example of usage
-    for the <command>EXEC SQL WHENEVER</command> statement.  Further
-    examples of usage may be found in SQL manuals (e.g.,
-    <citetitle>The LAN TIMES Guide to SQL</> by Groff and Weinberg).
+    The statement <literal>EXEC SQL WHENEVER</literal> is a directive
+    of the SQL preprocessor, not a C statement.  The error or warning
+    actions that it sets apply to all embedded SQL statements that
+    appear below the point where the handler is set, unless a
+    different action was set for the same condition between the first
+    <literal>EXEC SQL WHENEVER</literal> and the SQL statement causing
+    the condition, regardless of the flow of control in the C program.
+    So neither of the two following C program exerpts will have the
+    desired effect.
+<programlisting>
+/*
+ * WRONG
+ */
+int main(int argc, char *argv[])
+{
+    ...
+    if (verbose) {
+        EXEC SQL WHENEVER SQLWARNING SQLPRINT;
+    }
+    ...
+    EXEC SQL SELECT ...;
+    ...
+}
+</programlisting>
+
+<programlisting>
+/*
+ * WRONG
+ */
+int main(int argc, char *argv[])
+{
+    ...
+    set_error_handler();
+    ...
+    EXEC SQL SELECT ...;
+    ...
+}
+
+static void set_error_handler(void)
+{
+    EXEC SQL WHENEVER SQLERROR STOP;
+}
+</programlisting>
    </para>
-  </note>
+  </sect2>
 
-  <para>
-   For a more powerful error handling, the embedded SQL interface
-   provides a <type>struct</> and a variable with the name
-   <varname>sqlca</varname> as follows:
+  <sect2>
+   <title>sqlca</title>
+
+   <para>
+    For a more powerful error handling, the embedded SQL interface
+    provides a global variable with the name <varname>sqlca</varname>
+    that has the following structure:
 <programlisting>
-struct sqlca
+struct
 {
     char sqlcaid[8];
     long sqlabc;
@@ -386,317 +1047,411 @@ struct sqlca
         char sqlerrmc[70];
     } sqlerrm;
     char sqlerrp[8];
-
     long sqlerrd[6];
-    /* 0: empty                                         */
-    /* 1: OID of processed row if applicable            */
-    /* 2: number of rows processed in an INSERT, UPDATE */
-    /*    or DELETE statement                           */
-    /* 3: empty                                         */
-    /* 4: empty                                         */
-    /* 5: empty                                         */
-
     char sqlwarn[8];
-    /* 0: set to 'W' if at least one other is 'W'       */
-    /* 1: if 'W' at least one character string          */
-    /*    value was truncated when it was               */
-    /*    stored into a host variable                   */
-    /* 2: empty                                         */
-    /* 3: empty                                         */
-    /* 4: empty                                         */
-    /* 5: empty                                         */
-    /* 6: empty                                         */
-    /* 7: empty                                         */
-
-    char sqlext[8];
+    char sqlstate[5];
 } sqlca;
 </programlisting>
-   (Many of the empty fields may be used in a future release.)
-  </para>
+    (In a multithreaded program, every thread automatically gets its
+    own copy of <varname>sqlca</varname>.  This works similar to the
+    handling of the standard C global variable
+    <varname>errno</varname>.)
+   </para>
 
-  <para>
-   If no error occurred in the last <acronym>SQL</acronym> statement,
-   <literal>sqlca.sqlcode</literal> will be 0
-   (<symbol>ECPG_NO_ERROR</>). If <literal>sqlca.sqlcode</literal> is
-   less than zero, this is a serious error, like the database
-   definition does not match the query. If it is greater than zero, it
-   is a normal error like the table did not contain the requested row.
-  </para>
+   <para>
+    <varname>sqlca</varname> covers both warnings and errors.  If
+    multiple warnings or errors occur during the execution of a
+    statement, then <varname>sqlca</varname> will only contain
+    information about the last one.
+   </para>
 
-  <para>
-   <literal>sqlca.sqlerrm.sqlerrmc</literal> will contain a string
-   that describes the error. The string ends with the line number in
-   the source file.
-  </para>
+   <para>
+    If no error occurred in the last <acronym>SQL</acronym> statement,
+    <literal>sqlca.sqlcode</literal> will be 0 and
+    <literal>sqlca.sqlstate</literal> will be
+    <literal>"00000"</literal>.  If a warning or error occured, then
+    <literal>sqlca.sqlcode</literal> will be negative and
+    <literal>sqlca.sqlstate</literal> will be different from
+    <literal>"00000"</literal>.  A positive
+    <literal>sqlca.sqlcode</literal> indicates a harmless condition,
+    such as that the last query returned zero rows.
+    <literal>sqlcode</literal> and <literal>sqlstate</literal> are two
+    different error code schemes; details appear below.
+   </para>
 
-  <para>
-   These are the errors that can occur:
+   <para>
+    If the last SQL statement was successful, then
+    <literal>sqlca.sqlerrd[1]</literal> contains the OID of the
+    processed row, if applicable, and
+    <literal>sqlca.sqlerrd[2]</literal> contains the number of
+    processed or returned rows, if applicable to the command.
+   </para>
 
-   <variablelist>
-    <varlistentry>
-     <term><computeroutput>-12: Out of memory in line %d.</computeroutput></term>
-     <listitem>
-      <para>
-       Should not normally occur. This indicates your virtual memory
-       is exhausted.
+   <para>
+    In case of an error or warning,
+    <literal>sqlca.sqlerrm.sqlerrmc</literal> will contain a string
+    that describes the error.  The field
+    <literal>sqlca.sqlerrm.sqlerrml</literal> contains the length of
+    the error message that is stored in
+    <literal>sqlca.sqlerrm.sqlerrmc</literal> (the result of
+    <function>strlen()</function>, not really interesting for a C
+    programmer).
+   </para>
+
+   <para>
+    In case of a warning, <literal>sqlca.sqlwarn[2]</literal> is set
+    to <literal>W</literal>.  (In all other cases, it is set to
+    something different from <literal>W</literal>.)  If
+    <literal>sqlca.sqlwarn[1]</literal> is set to
+    <literal>W</literal>, then a value was truncated when it was
+    stored in a host variable.  <literal>sqlca.sqlwarn[0]</literal> is
+    set to <literal>W</literal> if any of the other elements are set
+    to indicate a warning.
+   </para>
+
+   <para>
+    The fields <structfield>sqlcaid</structfield>,
+    <structfield>sqlcabc</structfield>,
+    <structfield>sqlerrp</structfield>, and the remaining elements of
+    <structfield>sqlerrd</structfield> and
+    <structfield>sqlwarn</structfield> currently contain no useful
+    information.
+   </para>
+
+   <para>
+    The structure <varname>sqlca</varname> is not defined in the SQL
+    standard, but is implemented in several other SQL database
+    systems.  The definitions are similar in the core, but if you want
+    to write portable applications, then you should investigate the
+    different implementations carefully.
+   </para>
+  </sect2>
+
+  <sect2>
+   <title><literal>SQLSTATE</literal> vs <literal>SQLCODE</literal></title>
+
+   <para>
+    The fields <literal>sqlca.sqlstate</literal> and
+    <literal>sqlca.sqlcode</literal> are two different schemes that
+    provide error codes.  Both are specified in the SQL standard, but
+    <literal>SQLCODE</literal> has been marked deprecated in the 1992
+    edition of the standard and has been dropped in the 1999 edition.
+    Therefore, new applications are strongly encouraged to use
+    <literal>SQLSTATE</literal>.
+   </para>
+
+   <para>
+    <literal>SQLSTATE</literal> is a five-character array.  The five
+    characters contain digits or upper-case letters that represent
+    codes of various error and warning conditions.
+    <literal>SQLSTATE</literal> has a hierarchical scheme: the first
+    two characters indicate the general class of the condition, the
+    last three characters indicate a subclass of the general
+    condition.  A successful state is indicated by the code
+    <literal>00000</literal>.  Further information about the codes can
+    be found XXX.  The <literal>SQLSTATE</literal> codes are for the
+    most part defined in the SQL standard.  The PostgreSQL server
+    natively supports <literal>SQLSTATE</literal> error codes;
+    therefore a high degree of consistency can be achieved by using
+    this error code scheme throughout all applications.
+   </para>
+
+   <para>
+    <literal>SQLCODE</literal>, the deprecated error code scheme, is a
+    simple integer.  A value of 0 indicates success, a positive value
+    indicates success with additional information, a negative value
+    indicates an error.  The SQL standard only defines the positive
+    value +100, which indicates that the last command returned or
+    affected zero rows, and no specific negative values.  Therefore,
+    this scheme can only achieve poor portability and does not have a
+    hierarchical code assignment.  Historically, the embedded SQL
+    processor for PostgreSQL has assigned some specific
+    <literal>SQLCODE</literal> values for its use, which are listed
+    below with their numeric value and their symbolic name.  Remember
+    that these are not portable to other SQL implementations.  To
+    simplify the porting of applications to the
+    <literal>SQLSTATE</literal> scheme, the corresponding
+    <literal>SQLSTATE</literal> is also listed.  There is, however, no
+    one-to-one or one-to-many mapping between the two schemes (indeed
+    it is many-to-many), so you should consult the global
+    <literal>SQLSTATE</literal> listing in XXX in each case.
+   </para>
+
+   <para>
+    These are the assigned <literal>SQLCODE</literal> values:
+
+    <variablelist>
+     <varlistentry>
+      <term>-12 (<symbol>ECPG_OUT_OF_MEMORY</symbol>)</term>
+      <listitem>
+       <para>
+        Indicates that your virtual memory is exhausted. (SQLSTATE
+        YE001)
       </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><computeroutput>-200 (ECPG_UNSUPPORTED): Unsupported type %s on line %d.</computeroutput></term>
+     <term>-200 (<symbol>ECPG_UNSUPPORTED</symbol>)</term>
      <listitem>
       <para>
-       Should not normally occur. This indicates the preprocessor has
-       generated something that the library does not know about.
-       Perhaps you are running incompatible versions of the
-       preprocessor and the library.
+       Indicates the preprocessor has generated something that the
+       library does not know about.  Perhaps you are running
+       incompatible versions of the preprocessor and the
+       library. (SQLSTATE YE002)
       </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><computeroutput>-201 (ECPG_TOO_MANY_ARGUMENTS): Too many arguments line %d.</computeroutput></term>
+     <term>-201 (<symbol>ECPG_TOO_MANY_ARGUMENTS</symbol>)</term>
      <listitem>
       <para>
-       This means that the server has returned more arguments than we
-       have matching variables.  Perhaps you have forgotten a couple
-       of the host variables in the <command>INTO
-       :var1, :var2</command> list.
+       This means that the command specified more host variables than
+       the command expected.  (SQLSTATE 07001 or 07002)
       </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><computeroutput>-202 (ECPG_TOO_FEW_ARGUMENTS): Too few arguments line %d.</computeroutput></term>
+     <term>-202 (<symbol>ECPG_TOO_FEW_ARGUMENTS</symbol>)</term>
      <listitem>
       <para>
-       This means that the server has returned fewer arguments than we
-       have host variables. Perhaps you have too many host variables
-       in the <command>INTO :var1,:var2</command> list.  </para>
+       This means that the command specified fewer host variables than
+       the command expected.  (SQLSTATE 07001 or 07002)
+      </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><computeroutput>-203 (ECPG_TOO_MANY_MATCHES): Too many matches line %d.</computeroutput></term>
+     <term>-203 (<symbol>ECPG_TOO_MANY_MATCHES</symbol>)</term>
      <listitem>
       <para>
-       This means the query has returned multiple rows but the
-       variables specified are not arrays. The
-       <command>SELECT</command> command was not unique.
+       This means a query has returned multiple rows but the statement
+       was only prepared to store one result row (for example, because
+       the specified variables are not arrays).  (SQLSTATE 21000)
       </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><computeroutput>-204 (ECPG_INT_FORMAT): Not correctly formatted int type: %s line %d.</computeroutput></term>
+     <term>-204 (<symbol>ECPG_INT_FORMAT</symbol>)</term>
      <listitem>
       <para>
-       This means the host variable is of type <type>int</type> and
-       the field in the <productname>PostgreSQL</productname> database
-       is of another type and contains a value that cannot be
-       interpreted as an <type>int</type>. The library uses
-       <function>strtol()</function> for this conversion.
+       The host variable is of type <type>int</type> and the datum in
+       the database is of a different type and contains a value that
+       cannot be interpreted as an <type>int</type>.  The library uses
+       <function>strtol()</function> for this conversion.  (SQLSTATE
+       42804)
       </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><computeroutput>-205 (ECPG_UINT_FORMAT): Not correctly formatted unsigned type: %s line %d.</computeroutput></term>
+     <term>-205 (<symbol>ECPG_UINT_FORMAT</symbol>)</term>
      <listitem>
       <para>
-       This means the host variable is of type <type>unsigned
-       int</type> and the field in the
-       <productname>PostgreSQL</productname> database is of another
-       type and contains a value that cannot be interpreted as an
-       <type>unsigned int</type>. The library uses
-       <function>strtoul()</function> for this conversion.
+       The host variable is of type <type>unsigned int</type> and the
+       datum in the database is of a different type and contains a
+       value that cannot be interpreted as an <type>unsigned
+       int</type>.  The library uses <function>strtoul()</function>
+       for this conversion.  (SQLSTATE 42804)
       </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><computeroutput>-206 (ECPG_FLOAT_FORMAT): Not correctly formatted floating-point type: %s line %d.</computeroutput></term>
+     <term>-206 (<symbol>ECPG_FLOAT_FORMAT</symbol>)</term>
      <listitem>
       <para>
-       This means the host variable is of type <type>float</type> and
-       the field in the <productname>PostgreSQL</productname> database
-       is of another type and contains a value that cannot be
-       interpreted as a <type>float</type>. The library uses
-       <function>strtod()</function> for this conversion.
+       The host variable is of type <type>float</type> and the datum
+       in the database is of another type and contains a value that
+       cannot be interpreted as a <type>float</type>.  The library
+       uses <function>strtod()</function> for this conversion.
+       (SQLSTATE 42804)
       </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><computeroutput>-207 (ECPG_CONVERT_BOOL): Unable to convert %s to bool on line %d.</computeroutput></term>
+     <term>-207 (<symbol>ECPG_CONVERT_BOOL</symbol>)</term>
      <listitem>
       <para>
        This means the host variable is of type <type>bool</type> and
-       the field in the <productname>PostgreSQL</productname> database
-       is neither <literal>'t'</> nor <literal>'f'</>.
+       the datum in the database is neither <literal>'t'</> nor
+       <literal>'f'</>.  (SQLSTATE 42804)
       </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><computeroutput>-208 (ECPG_EMPTY): Empty query line %d.</computeroutput></term>
+     <term>-208 (<symbol>ECPG_EMPTY</symbol>)</term>
      <listitem>
       <para>
-       The query was empty.  (This cannot normally happen in an
-       embedded SQL program, so it may point to an internal error.)
+       The statement sent to the PostgreSQL server was empty.  (This
+       cannot normally happen in an embedded SQL program, so it may
+       point to an internal error.)  (SQLSTATE YE002)
       </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><computeroutput>-209 (ECPG_MISSING_INDICATOR): NULL value without indicator in line %d.</computeroutput></term>
+     <term>-209 (<symbol>ECPG_MISSING_INDICATOR</symbol>)</term>
      <listitem>
       <para>
        A null value was returned and no null indicator variable was
-       supplied.
+       supplied.  (SQLSTATE 22002)
       </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><computeroutput>-210 (ECPG_NO_ARRAY): Variable is not an array in line %d.</computeroutput></term>
+     <term>-210 (<symbol>ECPG_NO_ARRAY</symbol>)</term>
      <listitem>
       <para>
        An ordinary variable was used in a place that requires an
-       array.
+       array.  (SQLSTATE 42804)
       </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><computeroutput>-211 (ECPG_DATA_NOT_ARRAY): Data read from backend is not an array in line %d.</computeroutput></term>
+     <term>-211 (<symbol>ECPG_DATA_NOT_ARRAY</symbol>)</term>
      <listitem>
       <para>
        The database returned an ordinary variable in a place that
-       requires array value.
+       requires array value.  (SQLSTATE 42804)
       </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><computeroutput>-220 (ECPG_NO_CONN): No such connection %s in line %d.</computeroutput></term>
+     <term>-220 (<symbol>ECPG_NO_CONN</symbol>)</term>
      <listitem>
       <para>
        The program tried to access a connection that does not exist.
+       (SQLSTATE 08003)
       </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><computeroutput>-221 (ECPG_NOT_CONN): Not connected in line %d.</computeroutput></term>
+     <term>-221 (<symbol>ECPG_NOT_CONN</symbol>)</term>
      <listitem>
       <para>
        The program tried to access a connection that does exist but is
-       not open.
+       not open.  (This is an internal error.)  (SQLSTATE YE002)
       </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><computeroutput>-230 (ECPG_INVALID_STMT): Invalid statement name %s in line %d.</computeroutput></term>
+     <term>-230 (<symbol>ECPG_INVALID_STMT</symbol>)</term>
      <listitem>
       <para>
        The statement you are trying to use has not been prepared.
+       (SQLSTATE 26000)
       </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><computeroutput>-240 (ECPG_UNKNOWN_DESCRIPTOR): Descriptor %s not found in line %d.</computeroutput></term>
+     <term>-240 (<symbol>ECPG_UNKNOWN_DESCRIPTOR</symbol>)</term>
      <listitem>
       <para>
-       The descriptor specified was not found. The statement you are
-       trying to use has not been prepared.
+       The descriptor specified was not found.  The statement you are
+       trying to use has not been prepared.  (SQLSTATE 33000)
       </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><computeroutput>-241 (ECPG_INVALID_DESCRIPTOR_INDEX): Descriptor index out of range in line %d.</computeroutput></term>
+     <term>-241 (<symbol>ECPG_INVALID_DESCRIPTOR_INDEX</symbol>)</term>
      <listitem>
       <para>
-       The descriptor index specified was out of range.
+       The descriptor index specified was out of range.  (SQLSTATE
+       07009)
       </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><computeroutput>-242 (ECPG_UNKNOWN_DESCRIPTOR_ITEM): Unknown descriptor item %s in line %d.</computeroutput></term>
+     <term>-242 (<symbol>ECPG_UNKNOWN_DESCRIPTOR_ITEM</symbol>)</term>
      <listitem>
       <para>
-       The descriptor specified was not found. The statement you are trying to use has not been prepared.
+       An invalid descriptor item was requested.  (This is an internal
+       error.)  (SQLSTATE YE002)
       </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><computeroutput>-243 (ECPG_VAR_NOT_NUMERIC): Variable is not a numeric type in line %d.</computeroutput></term>
+     <term>-243 (<symbol>ECPG_VAR_NOT_NUMERIC</symbol>)</term>
      <listitem>
       <para>
-       The database returned a numeric value and the variable was not
-       numeric.
+       During the execution of a dynamic statement, the database
+       returned a numeric value and the host variable was not numeric.
+       (SQLSTATE 07006)
       </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><computeroutput>-244 (ECPG_VAR_NOT_CHAR): Variable is not a character type in line %d.</computeroutput></term>
+     <term>-244 (<symbol>ECPG_VAR_NOT_CHAR</symbol>)</term>
      <listitem>
       <para>
-       The database returned a non-numeric value and the variable was
-       numeric.
+       During the execution of a dynamic statement, the database
+       returned a non-numeric value and the host variable was numeric.
+       (SQLSTATE 07006)
       </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><computeroutput>-400 (ECPG_PGSQL): '%s' in line %d.</computeroutput></term>
+     <term>-400 (<symbol>ECPG_PGSQL</symbol>)</term>
      <listitem>
       <para>
-       Some <productname>PostgreSQL</productname> error. The message
-       contains the error message from the
+       Some error caused by the <productname>PostgreSQL</productname>
+       server.  The message contains the error message from the
        <productname>PostgreSQL</productname> server.
       </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><computeroutput>-401 (ECPG_TRANS): Error in transaction processing line %d.</computeroutput></term>
+     <term>-401 (<symbol>ECPG_TRANS</symbol>)</term>
      <listitem>
       <para>
-       The <productname>PostgreSQL</productname> server signaled that we cannot
-       start, commit, or rollback the transaction.
+       The <productname>PostgreSQL</productname> server signaled that
+       we cannot start, commit, or rollback the transaction.
+       (SQLSTATE 08007)
       </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><computeroutput>-402 (ECPG_CONNECT): Could not connect to database %s in line %d.</computeroutput></term>
+     <term>-402 (<symbol>ECPG_CONNECT</symbol>)</term>
      <listitem>
       <para>
-       The connection attempt to the database did not work.
+       The connection attempt to the database did not succeed.
+       (SQLSTATE 08001)
       </para>
      </listitem>
     </varlistentry>
 
     <varlistentry>
-     <term><computeroutput>100 (ECPG_NOT_FOUND): Data not found line %d.</computeroutput></term>
+     <term>100 (<symbol>ECPG_NOT_FOUND</symbol>)</term>
      <listitem>
       <para>
-       This is a <quote>normal</quote> error that tells you that what
-       you are querying cannot be found or you are at the end of the
-       cursor.
+       This is a harmless condition indicating that the last command
+       retrieved or processed zero rows, or that you are at the end of
+       the cursor.  (SQLSTATE 02000)
       </para>
      </listitem>
     </varlistentry>
    </variablelist>
   </para>
+  </sect2>
  </sect1>
 
  <sect1 id="ecpg-include">
@@ -749,13 +1504,6 @@ EXEC SQL INCLUDE <replaceable>filename</replaceable>;
    in the arguments specified for output.
   </para>
 
-  <para>
-   <application>ecpg</application> is thread-safe if it is compiled using
-   the <literal>--enable-thread-safety</> <filename>configure</filename>
-   command-line option.  (You might need to use other threading
-   command-line options to compile your client code.)
-  </para>
-
   <para>
    The preprocessor program is called <filename>ecpg</filename> and is
    included in a normal <productname>PostgreSQL</> installation.
@@ -811,6 +1559,13 @@ ECPG = ecpg
    The complete syntax of the <command>ecpg</command> command is
    detailed in <xref linkend="app-ecpg">.
   </para>
+
+  <para>
+   <application>ecpg</application> is thread-safe if it is compiled using
+   the <option>--enable-thread-safety</> <filename>configure</filename>
+   command-line option.  (You might need to use other threading
+   command-line options to compile your client code.)
+  </para>
  </sect1>
 
  <sect1 id="ecpg-library">
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 978f05ef928dd7118486d89e37699ce67c7fc988..cf4a0638717a452a65e1855508c20b914704ff40 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.18 2003/07/28 00:09:14 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.19 2003/08/01 13:53:36 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -169,8 +169,7 @@ PerformPortalFetch(FetchStmt *stmt,
 		/* FIXME: shouldn't this be an ERROR? */
 		ereport(WARNING,
 				(errcode(ERRCODE_UNDEFINED_CURSOR),
-				 errmsg("portal \"%s\" does not exist", stmt->portalname),
-				 errfunction("PerformPortalFetch"))); /* for ecpg */
+				 errmsg("portal \"%s\" does not exist", stmt->portalname)));
 		if (completionTag)
 			strcpy(completionTag, stmt->ismove ? "MOVE 0" : "FETCH 0");
 		return;
diff --git a/src/interfaces/ecpg/compatlib/Makefile b/src/interfaces/ecpg/compatlib/Makefile
index 5bd527d98a85b3866ab597162a76d6771dd78d50..83d9193dfd4d0c268eb6f24817fee6fa2191ac78 100644
--- a/src/interfaces/ecpg/compatlib/Makefile
+++ b/src/interfaces/ecpg/compatlib/Makefile
@@ -4,7 +4,7 @@
 #
 # Copyright (c) 1994, Regents of the University of California
 #
-# $Header: /cvsroot/pgsql/src/interfaces/ecpg/compatlib/Makefile,v 1.5 2003/06/24 14:45:46 momjian Exp $
+# $Header: /cvsroot/pgsql/src/interfaces/ecpg/compatlib/Makefile,v 1.6 2003/08/01 13:53:36 petere Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -16,7 +16,7 @@ NAME= ecpg_compat
 SO_MAJOR_VERSION= 1
 SO_MINOR_VERSION= 0.0
 
-override CPPFLAGS := -I$(top_srcdir)/src/interfaces/ecpg/include -I$(top_srcdir)/src/include/utils $(CPPFLAGS)
+override CPPFLAGS := -I$(top_srcdir)/src/interfaces/ecpg/include -I$(libpq_srcdir) -I$(top_srcdir)/src/include/utils $(CPPFLAGS)
 SHLIB_LINK = -L../pgtypeslib -lpgtypes -L../ecpglib -lecpg
 
 OBJS= informix.o
diff --git a/src/interfaces/ecpg/ecpglib/connect.c b/src/interfaces/ecpg/ecpglib/connect.c
index 44b088481c7a8deacc1f780b9623b07cc144f1fe..1030b041ab19b4b9bcb48f66b4802f5449738cb1 100644
--- a/src/interfaces/ecpg/ecpglib/connect.c
+++ b/src/interfaces/ecpg/ecpglib/connect.c
@@ -1,4 +1,4 @@
-/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/connect.c,v 1.13 2003/08/01 08:21:04 meskes Exp $ */
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/connect.c,v 1.14 2003/08/01 13:53:36 petere Exp $ */
 
 #define POSTGRES_ECPG_INTERNAL
 #include "postgres_fe.h"
@@ -116,7 +116,7 @@ ECPGsetcommit(int lineno, const char *mode, const char *connection_name)
 		{
 			if ((results = PQexec(con->connection, "begin transaction")) == NULL)
 			{
-				ECPGraise(lineno, ECPG_TRANS, NULL, ECPG_COMPAT_PGSQL);
+				ECPGraise(lineno, ECPG_TRANS, ECPG_SQLSTATE_TRANSACTION_RESOLUTION_UNKNOWN, NULL);
 				return false;
 			}
 			PQclear(results);
@@ -130,7 +130,7 @@ ECPGsetcommit(int lineno, const char *mode, const char *connection_name)
 		{
 			if ((results = PQexec(con->connection, "commit")) == NULL)
 			{
-				ECPGraise(lineno, ECPG_TRANS, NULL, ECPG_COMPAT_PGSQL);
+				ECPGraise(lineno, ECPG_TRANS, ECPG_SQLSTATE_TRANSACTION_RESOLUTION_UNKNOWN, NULL);
 				return false;
 			}
 			PQclear(results);
@@ -154,151 +154,47 @@ ECPGsetconn(int lineno, const char *connection_name)
 	return true;
 }
 
-static void
-ECPGnoticeProcessor_raise(int code, const char *message)
-{
-	struct sqlca_t *sqlca = ECPGget_sqlca();
-	sqlca->sqlcode = code;
-	strncpy(sqlca->sqlerrm.sqlerrmc, message, sizeof(sqlca->sqlerrm.sqlerrmc));
-	sqlca->sqlerrm.sqlerrmc[sizeof(sqlca->sqlerrm.sqlerrmc) - 1] = 0;
-	sqlca->sqlerrm.sqlerrml = strlen(sqlca->sqlerrm.sqlerrmc);
-
-	/* remove trailing newline */
-	if (sqlca->sqlerrm.sqlerrml
-		&& sqlca->sqlerrm.sqlerrmc[sqlca->sqlerrm.sqlerrml - 1] == '\n')
-	{
-		sqlca->sqlerrm.sqlerrmc[sqlca->sqlerrm.sqlerrml - 1] = 0;
-		sqlca->sqlerrm.sqlerrml--;
-	}
-
-	ECPGlog("raising sqlcode %d\n", code);
-}
-
-/*
- * I know this is a mess, but we can't redesign the backend
- */
 
 static void
-ECPGnoticeProcessor(void *arg, const char *message)
+ECPGnoticeReceiver(void *arg, const PGresult *result)
 {
+	char *sqlstate = PQresultErrorField(result, 'C');
+	char *message = PQresultErrorField(result, 'M');
 	struct sqlca_t *sqlca = ECPGget_sqlca();
 
-	/* these notices raise an error */
-	if (strncmp(message, "WARNING: ", 9) && strncmp(message, "NOTICE: ", 8))
-	{
-		ECPGlog("ECPGnoticeProcessor: strange warning '%s'\n", message);
-		ECPGnoticeProcessor_raise(ECPG_WARNING_UNRECOGNIZED, message);
-		return;
-	}
-
-	message += 8;
-	while (*message == ' ')
-		message++;
-	ECPGlog("WARNING: %s", message);
-
-	/* WARNING: (transaction aborted): queries ignored until END */
-
-	/*
-	 * WARNING: current transaction is aborted, queries ignored until end
-	 * of transaction block
-	 */
-	if (strstr(message, "queries ignored") && strstr(message, "transaction")
-		&& strstr(message, "aborted"))
-	{
-		ECPGnoticeProcessor_raise(ECPG_WARNING_QUERY_IGNORED, message);
-		return;
-	}
+	int sqlcode;
 
-	/* WARNING: PerformPortalClose: portal "*" not found */
-	if ((!strncmp(message, "PerformPortalClose: portal", 26)
-		 || !strncmp(message, "PerformPortalFetch: portal", 26))
-		&& strstr(message + 26, "not found"))
-	{
-		ECPGnoticeProcessor_raise(ECPG_WARNING_UNKNOWN_PORTAL, message);
+	/* these are not warnings */
+	if (strncmp(sqlstate, "00", 2)==0)
 		return;
-	}
 
-	/* WARNING: BEGIN: already a transaction in progress */
-	if (!strncmp(message, "BEGIN: already a transaction in progress", 40))
-	{
-		ECPGnoticeProcessor_raise(ECPG_WARNING_IN_TRANSACTION, message);
-		return;
-	}
-
-	/* WARNING: AbortTransaction and not in in-progress state */
-	/* WARNING: COMMIT: no transaction in progress */
-	/* WARNING: ROLLBACK: no transaction in progress */
-	if (!strncmp(message, "AbortTransaction and not in in-progress state", 45)
-		|| !strncmp(message, "COMMIT: no transaction in progress", 34)
-		|| !strncmp(message, "ROLLBACK: no transaction in progress", 36))
-	{
-		ECPGnoticeProcessor_raise(ECPG_WARNING_NO_TRANSACTION, message);
-		return;
-	}
-
-	/* WARNING: BlankPortalAssignName: portal * already exists */
-	if (!strncmp(message, "BlankPortalAssignName: portal", 29)
-		&& strstr(message + 29, "already exists"))
-	{
-		ECPGnoticeProcessor_raise(ECPG_WARNING_PORTAL_EXISTS, message);
-		return;
-	}
-
-	/* these are harmless - do nothing */
-
-	/*
-	 * WARNING: CREATE TABLE / PRIMARY KEY will create implicit index '*'
-	 * for table '*'
-	 */
-
-	/*
-	 * WARNING: ALTER TABLE ... ADD CONSTRAINT will create implicit
-	 * trigger(s) for FOREIGN KEY check(s)
-	 */
-
-	/*
-	 * WARNING: CREATE TABLE will create implicit sequence '*' for SERIAL
-	 * column '*.*'
-	 */
-
-	/*
-	 * WARNING: CREATE TABLE will create implicit trigger(s) for FOREIGN
-	 * KEY check(s)
-	 */
-	if ((!strncmp(message, "CREATE TABLE", 12) || !strncmp(message, "ALTER TABLE", 11))
-		&& strstr(message + 11, "will create implicit"))
-		return;
-
-	/* WARNING: QUERY PLAN: */
-	if (!strncmp(message, "QUERY PLAN:", 11))	/* do we really see these? */
-		return;
-
-	/*
-	 * WARNING: DROP TABLE implicitly drops referential integrity trigger
-	 * from table "*"
-	 */
-	if (!strncmp(message, "DROP TABLE implicitly drops", 27))
-		return;
-
-	/*
-	 * WARNING: Caution: DROP INDEX cannot be rolled back, so don't abort
-	 * now
-	 */
-	if (strstr(message, "cannot be rolled back"))
-		return;
+	ECPGlog("%s", message);
+
+	/* map to SQLCODE for backward compatibility */
+	if (strcmp(sqlstate, ECPG_SQLSTATE_INVALID_CURSOR_NAME)==0)
+		sqlcode = ECPG_WARNING_UNKNOWN_PORTAL;
+	else if (strcmp(sqlstate, ECPG_SQLSTATE_ACTIVE_SQL_TRANSACTION)==0)
+		sqlcode = ECPG_WARNING_IN_TRANSACTION;
+	else if (strcmp(sqlstate, ECPG_SQLSTATE_NO_ACTIVE_SQL_TRANSACTION)==0)
+		sqlcode = ECPG_WARNING_NO_TRANSACTION;
+	else if (strcmp(sqlstate, ECPG_SQLSTATE_DUPLICATE_CURSOR)==0)
+		sqlcode = ECPG_WARNING_PORTAL_EXISTS;
+	else
+		sqlcode = 0;
 
-	/* these and other unmentioned should set sqlca->sqlwarn[2] */
-	/* WARNING: The ':' operator is deprecated.  Use exp(x) instead. */
-	/* WARNING: Rel *: Uninitialized page 0 - fixing */
-	/* WARNING: PortalHeapMemoryFree: * not in alloc set! */
-	/* WARNING: Too old parent tuple found - can't continue vc_repair_frag */
-	/* WARNING: identifier "*" will be truncated to "*" */
-	/* WARNING: InvalidateSharedInvalid: cache state reset */
-	/* WARNING: RegisterSharedInvalid: SI buffer overflow */
+	strncpy(sqlca->sqlstate, sqlstate, sizeof(sqlca->sqlstate));
+	sqlca->sqlcode = sqlcode;
 	sqlca->sqlwarn[2] = 'W';
 	sqlca->sqlwarn[0] = 'W';
+
+	strncpy(sqlca->sqlerrm.sqlerrmc, message, sizeof(sqlca->sqlerrm.sqlerrmc));
+	sqlca->sqlerrm.sqlerrmc[sizeof(sqlca->sqlerrm.sqlerrmc) - 1] = 0;
+	sqlca->sqlerrm.sqlerrml = strlen(sqlca->sqlerrm.sqlerrmc);
+
+	ECPGlog("raising sqlcode %d\n", sqlcode);
 }
 
+
 /* this contains some quick hacks, needs to be cleaned up, but it works */
 bool
 ECPGconnect(int lineno, int c, const char *name, const char *user, const char *passwd, const char *connection_name, int autocommit)
@@ -406,7 +302,7 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
 					if (strncmp(dbname, "unix:", 5) != 0)
 					{
 						ECPGlog("connect: socketname %s given for TCP connection in line %d\n", host, lineno);
-						ECPGraise(lineno, ECPG_CONNECT, realname ? realname : "<DEFAULT>", ECPG_COMPAT_PGSQL);
+						ECPGraise(lineno, ECPG_CONNECT, ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, realname ? realname : "<DEFAULT>");
 						if (host)
 							ECPGfree(host);
 						if (port)
@@ -429,7 +325,7 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
 				if (strcmp(dbname + offset, "localhost") != 0 && strcmp(dbname + offset, "127.0.0.1") != 0)
 				{
 					ECPGlog("connect: non-localhost access via sockets in line %d\n", lineno);
-					ECPGraise(lineno, ECPG_CONNECT, realname ? realname : "<DEFAULT>", ECPG_COMPAT_PGSQL);
+					ECPGraise(lineno, ECPG_CONNECT, ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, realname ? realname : "<DEFAULT>");
 					if (host)
 						ECPGfree(host);
 					if (port)
@@ -497,7 +393,7 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
 				user ? "for user " : "", user ? user : "",
 				lineno, errmsg);
         
-		ECPGraise(lineno, ECPG_CONNECT, db, ECPG_COMPAT_PGSQL);
+		ECPGraise(lineno, ECPG_CONNECT, ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, db);
 		if (host)
 			ECPGfree(host);
 		if (port)
@@ -528,7 +424,7 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
 	this->committed = true;
 	this->autocommit = autocommit;
 
-	PQsetNoticeProcessor(this->connection, &ECPGnoticeProcessor, (void *) this);
+	PQsetNoticeReceiver(this->connection, &ECPGnoticeReceiver, (void *) this);
 
 	return true;
 }
diff --git a/src/interfaces/ecpg/ecpglib/data.c b/src/interfaces/ecpg/ecpglib/data.c
index 361dfa73a4a6676cb8773bd05cf0f507fb7e6c3e..14e459d1b60a08f274b359ff4bcc8f290671beba 100644
--- a/src/interfaces/ecpg/ecpglib/data.c
+++ b/src/interfaces/ecpg/ecpglib/data.c
@@ -1,4 +1,4 @@
-/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/data.c,v 1.14 2003/08/01 08:21:04 meskes Exp $ */
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/data.c,v 1.15 2003/08/01 13:53:36 petere Exp $ */
 
 #define POSTGRES_ECPG_INTERNAL
 #include "postgres_fe.h"
@@ -34,7 +34,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 	{
 		if (*pval != '{')
 		{
-			ECPGraise(lineno, ECPG_DATA_NOT_ARRAY, NULL, compat);
+			ECPGraise(lineno, ECPG_DATA_NOT_ARRAY, ECPG_SQLSTATE_DATATYPE_MISMATCH, NULL);
 			return (false);
 		}
 
@@ -90,13 +90,13 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 				}
 				else
 				{
-					ECPGraise(lineno, ECPG_MISSING_INDICATOR, NULL, compat);
+					ECPGraise(lineno, ECPG_MISSING_INDICATOR, ECPG_SQLSTATE_NULL_VALUE_NO_INDICATOR_PARAMETER, NULL);
 					return (false);
 				}
 			}
 			break;
 		default:
-			ECPGraise(lineno, ECPG_UNSUPPORTED, ECPGtype_name(ind_type), compat);
+			ECPGraise(lineno, ECPG_UNSUPPORTED, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, ECPGtype_name(ind_type));
 			return (false);
 			break;
 	}
@@ -126,7 +126,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 					if ((isarray && *scan_length != ',' && *scan_length != '}')
 						|| (!isarray && *scan_length != '\0' && *scan_length != ' '))	/* Garbage left */
 					{
-						ECPGraise(lineno, ECPG_INT_FORMAT, pval, compat);
+						ECPGraise(lineno, ECPG_INT_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
 						return (false);
 					}
 				}
@@ -159,7 +159,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 					if ((isarray && *scan_length != ',' && *scan_length != '}')
 						|| (!isarray && *scan_length != '\0' && *scan_length != ' '))	/* Garbage left */
 					{
-						ECPGraise(lineno, ECPG_UINT_FORMAT, pval, compat);
+						ECPGraise(lineno, ECPG_UINT_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
 						return (false);
 					}
 				}
@@ -192,7 +192,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 					if ((isarray && *scan_length != ',' && *scan_length != '}')
 						|| (!isarray && *scan_length != '\0' && *scan_length != ' '))	/* Garbage left */
 					{
-						ECPGraise(lineno, ECPG_INT_FORMAT, pval, compat);
+						ECPGraise(lineno, ECPG_INT_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
 						return (false);
 					}
 				}
@@ -209,7 +209,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 					if ((isarray && *scan_length != ',' && *scan_length != '}')
 						|| (!isarray && *scan_length != '\0' && *scan_length != ' '))	/* Garbage left */
 					{
-						ECPGraise(lineno, ECPG_UINT_FORMAT, pval, compat);
+						ECPGraise(lineno, ECPG_UINT_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
 						return (false);
 					}
 				}
@@ -235,7 +235,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 					if ((isarray && *scan_length != ',' && *scan_length != '}')
 						|| (!isarray && *scan_length != '\0' && *scan_length != ' '))	/* Garbage left */
 					{
-						ECPGraise(lineno, ECPG_FLOAT_FORMAT, pval, compat);
+						ECPGraise(lineno, ECPG_FLOAT_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
 						return (false);
 					}
 				}
@@ -266,7 +266,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 						else if (offset == sizeof(int))
 							*((int *) (var + offset * act_tuple)) = false;
 						else
-							ECPGraise(lineno, ECPG_CONVERT_BOOL, "different size", compat);
+							ECPGraise(lineno, ECPG_CONVERT_BOOL, ECPG_SQLSTATE_DATATYPE_MISMATCH, "different size");
 						break;
 					}
 					else if (pval[0] == 't' && pval[1] == '\0')
@@ -276,7 +276,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 						else if (offset == sizeof(int))
 							*((int *) (var + offset * act_tuple)) = true;
 						else
-							ECPGraise(lineno, ECPG_CONVERT_BOOL, "different size", compat);
+							ECPGraise(lineno, ECPG_CONVERT_BOOL, ECPG_SQLSTATE_DATATYPE_MISMATCH, "different size");
 						break;
 					}
 					else if (pval[0] == '\0' && PQgetisnull(results, act_tuple, act_field))
@@ -286,7 +286,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 					}
 				}
 
-				ECPGraise(lineno, ECPG_CONVERT_BOOL, pval, compat);
+				ECPGraise(lineno, ECPG_CONVERT_BOOL, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
 				return (false);
 				break;
 
@@ -396,7 +396,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 					if ((isarray && *scan_length != ',' && *scan_length != '}')
 						|| (!isarray && *scan_length != '\0' && *scan_length != ' '))	/* Garbage left */
 					{
-						ECPGraise(lineno, ECPG_NUMERIC_FORMAT, pval, compat);
+						ECPGraise(lineno, ECPG_NUMERIC_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
 						return (false);
 					}
 				}
@@ -423,7 +423,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 					if ((isarray && *scan_length != ',' && *scan_length != '}')
 						|| (!isarray && *scan_length != '\0' && *scan_length != ' '))	/* Garbage left */
 					{
-						ECPGraise(lineno, ECPG_INTERVAL_FORMAT, pval, compat);
+						ECPGraise(lineno, ECPG_INTERVAL_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
 						return (false);
 					}
 				}
@@ -446,7 +446,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 					if ((isarray && *scan_length != ',' && *scan_length != '}')
 						|| (!isarray && *scan_length != '\0' && *scan_length != ' '))	/* Garbage left */
 					{
-						ECPGraise(lineno, ECPG_DATE_FORMAT, pval, compat);
+						ECPGraise(lineno, ECPG_DATE_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
 						return (false);
 					}
 
@@ -468,7 +468,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 					if ((isarray && *scan_length != ',' && *scan_length != '}')
 						|| (!isarray && *scan_length != '\0' && *scan_length != ' '))	/* Garbage left */
 					{
-						ECPGraise(lineno, ECPG_TIMESTAMP_FORMAT, pval, compat);
+						ECPGraise(lineno, ECPG_TIMESTAMP_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
 						return (false);
 					}
 
@@ -477,7 +477,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 				break;
 				
 			default:
-				ECPGraise(lineno, ECPG_UNSUPPORTED, ECPGtype_name(type), compat);
+				ECPGraise(lineno, ECPG_UNSUPPORTED, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, ECPGtype_name(type));
 				return (false);
 				break;
 		}
diff --git a/src/interfaces/ecpg/ecpglib/descriptor.c b/src/interfaces/ecpg/ecpglib/descriptor.c
index bdb308810da428913b2869d34ae21fa8c718f307..0beae2d93ab9bf336e01bc20f53e9ea2994683cc 100644
--- a/src/interfaces/ecpg/ecpglib/descriptor.c
+++ b/src/interfaces/ecpg/ecpglib/descriptor.c
@@ -1,6 +1,6 @@
 /* dynamic SQL support routines
  *
- * $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/descriptor.c,v 1.4 2003/08/01 08:21:04 meskes Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/descriptor.c,v 1.5 2003/08/01 13:53:36 petere Exp $
  */
 
 #define POSTGRES_ECPG_INTERNAL
@@ -103,7 +103,7 @@ get_int_item(int lineno, void *var, enum ECPGttype vartype, int value)
 			*(double *) var = (double) value;
 			break;
 		default:
-			ECPGraise(lineno, ECPG_VAR_NOT_NUMERIC, NULL, ECPG_COMPAT_PGSQL);
+			ECPGraise(lineno, ECPG_VAR_NOT_NUMERIC, ECPG_SQLSTATE_RESTRICTED_DATA_TYPE_ATTRIBUTE_VIOLATION, NULL);
 			return (false);
 	}
 
@@ -135,7 +135,7 @@ get_char_item(int lineno, void *var, enum ECPGttype vartype, char *value, int va
 			}
 			break;
 		default:
-			ECPGraise(lineno, ECPG_VAR_NOT_CHAR, NULL, ECPG_COMPAT_PGSQL);
+			ECPGraise(lineno, ECPG_VAR_NOT_CHAR, ECPG_SQLSTATE_RESTRICTED_DATA_TYPE_ATTRIBUTE_VIOLATION, NULL);
 			return (false);
 	}
 
@@ -162,13 +162,13 @@ ECPGget_desc(int lineno, char *desc_name, int index,...)
 	ntuples = PQntuples(ECPGresult);
 	if (ntuples < 1)
 	{
-		ECPGraise(lineno, ECPG_NOT_FOUND, NULL, ECPG_COMPAT_PGSQL);
+		ECPGraise(lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL);
 		return (false);
 	}
 
 	if (index < 1 || index > PQnfields(ECPGresult))
 	{
-		ECPGraise(lineno, ECPG_INVALID_DESCRIPTOR_INDEX, NULL, ECPG_COMPAT_PGSQL);
+		ECPGraise(lineno, ECPG_INVALID_DESCRIPTOR_INDEX, ECPG_SQLSTATE_INVALID_DESCRIPTOR_INDEX, NULL);
 		return (false);
 	}
 
@@ -300,7 +300,7 @@ ECPGget_desc(int lineno, char *desc_name, int index,...)
 				{
 					ECPGlog("ECPGget_desc line %d: Incorrect number of matches: %d don't fit into array of %d\n",
 							lineno, ntuples, arrsize);
-					ECPGraise(lineno, ECPG_TOO_MANY_MATCHES, NULL, ECPG_COMPAT_PGSQL);
+					ECPGraise(lineno, ECPG_TOO_MANY_MATCHES, ECPG_SQLSTATE_CARDINALITY_VIOLATION, NULL);
 					return false;
 				}
 				/* allocate storage if needed */
@@ -324,7 +324,7 @@ ECPGget_desc(int lineno, char *desc_name, int index,...)
 
 			default:
 				snprintf(type_str, sizeof(type_str), "%d", type);
-				ECPGraise(lineno, ECPG_UNKNOWN_DESCRIPTOR_ITEM, type_str, ECPG_COMPAT_PGSQL);
+				ECPGraise(lineno, ECPG_UNKNOWN_DESCRIPTOR_ITEM, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, type_str);
 				return (false);
 		}
 
@@ -361,7 +361,7 @@ ECPGget_desc(int lineno, char *desc_name, int index,...)
 		{
 			ECPGlog("ECPGget_desc line %d: Incorrect number of matches (indicator): %d don't fit into array of %d\n",
 					lineno, ntuples, data_var.ind_arrsize);
-			ECPGraise(lineno, ECPG_TOO_MANY_MATCHES, NULL, ECPG_COMPAT_PGSQL);
+			ECPGraise(lineno, ECPG_TOO_MANY_MATCHES, ECPG_SQLSTATE_CARDINALITY_VIOLATION, NULL);
 			return false;
 		}
 		/* allocate storage if needed */
@@ -404,7 +404,7 @@ ECPGdeallocate_desc(int line, const char *name)
 			return true;
 		}
 	}
-	ECPGraise(line, ECPG_UNKNOWN_DESCRIPTOR, name, ECPG_COMPAT_PGSQL);
+	ECPGraise(line, ECPG_UNKNOWN_DESCRIPTOR, ECPG_SQLSTATE_INVALID_SQL_DESCRIPTOR_NAME, name);
 	return false;
 }
 
@@ -430,7 +430,7 @@ ECPGallocate_desc(int line, const char *name)
 	{
 		ECPGfree(new->name);
 		ECPGfree(new);
-		ECPGraise(line, ECPG_OUT_OF_MEMORY, NULL, ECPG_COMPAT_PGSQL);
+		ECPGraise(line, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
 		return false;
 	}
 	strcpy(new->name, name);
@@ -449,7 +449,7 @@ ECPGdescriptor_lvalue(int line, const char *descriptor)
 			return &i->result;
 	}
 
-	ECPGraise(line, ECPG_UNKNOWN_DESCRIPTOR, (char *) descriptor, ECPG_COMPAT_PGSQL);
+	ECPGraise(line, ECPG_UNKNOWN_DESCRIPTOR, ECPG_SQLSTATE_INVALID_SQL_DESCRIPTOR_NAME, (char *) descriptor);
 	return NULL;
 }
 
diff --git a/src/interfaces/ecpg/ecpglib/error.c b/src/interfaces/ecpg/ecpglib/error.c
index 0b4b563d85a3bcb3252effaa82676a3f4ee27eb4..cfeb4e65b0b4cbc849fe25b1bfabc87c22985b1d 100644
--- a/src/interfaces/ecpg/ecpglib/error.c
+++ b/src/interfaces/ecpg/ecpglib/error.c
@@ -1,4 +1,4 @@
-/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/error.c,v 1.4 2003/08/01 08:21:04 meskes Exp $ */
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/error.c,v 1.5 2003/08/01 13:53:36 petere Exp $ */
 
 #define POSTGRES_ECPG_INTERNAL
 #include "postgres_fe.h"
@@ -11,11 +11,13 @@
 #include "extern.h"
 #include "sqlca.h"
 
+
 void
-ECPGraise(int line, int code, const char *str, int compat)
+ECPGraise(int line, int code, const char * sqlstate, const char *str)
 {
 	struct sqlca_t *sqlca = ECPGget_sqlca();
 	sqlca->sqlcode = code;
+	strncpy(sqlca->sqlstate, sqlstate, sizeof(sqlca->sqlstate));
 
 	switch (code)
 	{
@@ -129,23 +131,6 @@ ECPGraise(int line, int code, const char *str, int compat)
 				   "Variable is not a character type in line %d.", line);
 			break;
 
-		case ECPG_PGSQL:
-			{
-				int			slen = strlen(str);
-
-				/* strip trailing newline */
-				if (slen > 0 && str[slen - 1] == '\n')
-					slen--;
-				snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
-						 "'%.*s' in line %d.", slen, str, line);
-				if (strncmp(str, "ERROR:  Cannot insert a duplicate key", strlen("ERROR:  Cannot insert a duplicate key")) == 0)
-					sqlca->sqlcode = INFORMIX_MODE(compat) ? ECPG_INFORMIX_DUPLICATE_KEY : ECPG_DUPLICATE_KEY;
-				else if (strncmp(str, "ERROR:  More than one tuple returned by a subselect", strlen("ERROR:  More than one tuple returned by a subselect")) == 0)
-					sqlca->sqlcode = INFORMIX_MODE(compat) ? ECPG_INFORMIX_SUBSELECT_NOT_ONE : ECPG_SUBSELECT_NOT_ONE;
-				
-				break;
-			}
-
 		case ECPG_TRANS:
 			snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
 					 "Error in transaction processing in line %d.", line);
@@ -169,6 +154,38 @@ ECPGraise(int line, int code, const char *str, int compat)
 	ECPGfree_auto_mem();
 }
 
+void
+ECPGraise_backend(int line, PGresult *result, PGconn *conn, int compat)
+{
+	struct sqlca_t *sqlca = ECPGget_sqlca();
+
+	/* copy error message */
+	snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+			 "'%s' in line %d.", 
+			 result ? PQresultErrorField(result, 'M') : PQerrorMessage(conn),
+			 line);
+	sqlca->sqlerrm.sqlerrml = strlen(sqlca->sqlerrm.sqlerrmc);
+
+	/* copy SQLSTATE */
+	strncpy(sqlca->sqlstate,
+			result ? PQresultErrorField(result, 'C') : ECPG_SQLSTATE_ECPG_INTERNAL_ERROR,
+			sizeof(sqlca->sqlstate));
+
+	/* assign SQLCODE for backward compatibility */
+	if (strncmp(sqlca->sqlstate, "23505", sizeof(sqlca->sqlstate))==0)
+		sqlca->sqlcode = INFORMIX_MODE(compat) ? ECPG_INFORMIX_DUPLICATE_KEY : ECPG_DUPLICATE_KEY;
+	if (strncmp(sqlca->sqlstate, "21000", sizeof(sqlca->sqlstate))==0)
+		sqlca->sqlcode = INFORMIX_MODE(compat) ? ECPG_INFORMIX_SUBSELECT_NOT_ONE : ECPG_SUBSELECT_NOT_ONE;
+	else
+		sqlca->sqlcode = ECPG_PGSQL;
+
+	ECPGlog("raising sqlstate %.*s in line %d, '%s'.\n",
+			sqlca->sqlstate, sizeof(sqlca->sqlstate), line, sqlca->sqlerrm.sqlerrmc);
+
+	/* free all memory we have allocated for the user */
+	ECPGfree_auto_mem();
+}
+
 /* print out an error message */
 void
 sqlprint(void)
diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c
index 8e9545f0ab4b96826bd3b3f57d8108d32c3fc3cd..0d4247897d59b2049209c4306d14103e6d1474f2 100644
--- a/src/interfaces/ecpg/ecpglib/execute.c
+++ b/src/interfaces/ecpg/ecpglib/execute.c
@@ -1,4 +1,4 @@
-/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/execute.c,v 1.21 2003/08/01 08:21:04 meskes Exp $ */
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/execute.c,v 1.22 2003/08/01 13:53:36 petere Exp $ */
 
 /*
  * The aim is to get a simpler inteface to the database routines.
@@ -124,7 +124,7 @@ create_statement(int lineno, int compat, int force_indicator, struct connection
 			/* if variable is NULL, the statement hasn't been prepared */
 			if (var->pointer == NULL)
 			{
-				ECPGraise(lineno, ECPG_INVALID_STMT, NULL, compat);
+				ECPGraise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, NULL);
 				ECPGfree(var);
 				return false;
 			}
@@ -351,7 +351,7 @@ ECPGstore_result(const PGresult *results, int act_field,
 		{
 			ECPGlog("ECPGexecute line %d: Incorrect number of matches: %d don't fit into array of %d\n",
 					stmt->lineno, ntuples, var->arrsize);
-			ECPGraise(stmt->lineno, ECPG_TOO_MANY_MATCHES, NULL, ECPG_COMPAT_PGSQL);
+			ECPGraise(stmt->lineno, ECPG_TOO_MANY_MATCHES, ECPG_SQLSTATE_CARDINALITY_VIOLATION, NULL);
 			return false;
 		}
 	}
@@ -362,7 +362,7 @@ ECPGstore_result(const PGresult *results, int act_field,
 		 */
 		if (var->arrsize == 0)
 		{
-			ECPGraise(stmt->lineno, ECPG_NO_ARRAY, NULL, ECPG_COMPAT_PGSQL);
+			ECPGraise(stmt->lineno, ECPG_NO_ARRAY, ECPG_SQLSTATE_DATATYPE_MISMATCH, NULL);
 			return false;
 		}
 	}
@@ -481,12 +481,13 @@ ECPGstore_input(const struct statement * stmt, const struct variable * var,
 	 * arrays are not possible unless the attribute is an array too FIXME:
 	 * we do not know if the attribute is an array here
 	 */
-
-/*	 if (var->arrsize > 1 && ...)
+#if 0
+	 if (var->arrsize > 1 && ...)
 	 {
-		ECPGraise(stmt->lineno, ECPG_ARRAY_INSERT, NULL, compat);
+		ECPGraise(stmt->lineno, ECPG_ARRAY_INSERT, ECPG_SQLSTATE_DATATYPE_MISMATCH, NULL);
 		return false;
-	 }*/
+	 }
+#endif
 
 	/*
 	 * Some special treatment is needed for records since we want their
@@ -757,7 +758,7 @@ ECPGstore_input(const struct statement * stmt, const struct variable * var,
 						for (element = 0; element < var->arrsize; element++)
 							sprintf(mallocedval + strlen(mallocedval), "%c,", (((int *) var->value)[element]) ? 't' : 'f');
 					else
-						ECPGraise(stmt->lineno, ECPG_CONVERT_BOOL, "different size", ECPG_COMPAT_PGSQL);
+						ECPGraise(stmt->lineno, ECPG_CONVERT_BOOL, ECPG_SQLSTATE_DATATYPE_MISMATCH, "different size");
 
 					strcpy(mallocedval + strlen(mallocedval) - 1, "]");
 				}
@@ -768,7 +769,7 @@ ECPGstore_input(const struct statement * stmt, const struct variable * var,
 					else if (var->offset == sizeof(int))
 						sprintf(mallocedval, "'%c'", (*((int *) var->value)) ? 't' : 'f');
 					else
-						ECPGraise(stmt->lineno, ECPG_CONVERT_BOOL, "different size", ECPG_COMPAT_PGSQL);
+						ECPGraise(stmt->lineno, ECPG_CONVERT_BOOL, ECPG_SQLSTATE_DATATYPE_MISMATCH, "different size");
 				}
 
 				*tobeinserted_p = mallocedval;
@@ -1021,7 +1022,7 @@ ECPGstore_input(const struct statement * stmt, const struct variable * var,
 				
 			default:
 				/* Not implemented yet */
-				ECPGraise(stmt->lineno, ECPG_UNSUPPORTED, (char *) ECPGtype_name(var->type), ECPG_COMPAT_PGSQL);
+				ECPGraise(stmt->lineno, ECPG_UNSUPPORTED, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, (char *) ECPGtype_name(var->type));
 				return false;
 				break;
 		}
@@ -1034,7 +1035,7 @@ ECPGexecute(struct statement * stmt)
 {
 	bool		status = false;
 	char	   *copiedquery;
-	char	   *errmsg, *cmdstat;
+	char	   *cmdstat;
 	PGresult   *results;
 	PGnotify   *notify;
 	struct variable *var;
@@ -1073,7 +1074,7 @@ ECPGexecute(struct statement * stmt)
 			 * We have an argument but we dont have the matched up string
 			 * in the string
 			 */
-			ECPGraise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, NULL, stmt->compat);
+			ECPGraise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS, NULL);
 			return false;
 		}
 		else
@@ -1111,7 +1112,7 @@ ECPGexecute(struct statement * stmt)
 	/* Check if there are unmatched things left. */
 	if (next_insert(copiedquery) != NULL)
 	{
-		ECPGraise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, NULL, stmt->compat);
+		ECPGraise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS, NULL);
 		return false;
 	}
 
@@ -1121,7 +1122,7 @@ ECPGexecute(struct statement * stmt)
 	{
 		if ((results = PQexec(stmt->connection->connection, "begin transaction")) == NULL)
 		{
-			ECPGraise(stmt->lineno, ECPG_TRANS, NULL, stmt->compat);
+			ECPGraise(stmt->lineno, ECPG_TRANS, ECPG_SQLSTATE_TRANSACTION_RESOLUTION_UNKNOWN, NULL);
 			return false;
 		}
 		PQclear(results);
@@ -1134,9 +1135,8 @@ ECPGexecute(struct statement * stmt)
 
 	if (results == NULL)
 	{
-		errmsg = PQerrorMessage(stmt->connection->connection);
-		ECPGlog("ECPGexecute line %d: error: %s", stmt->lineno, errmsg);
-		ECPGraise(stmt->lineno, ECPG_PGSQL, errmsg, stmt->compat);
+		ECPGlog("ECPGexecute line %d: error: %s", stmt->lineno, PQerrorMessage(stmt->connection->connection));
+		ECPGraise_backend(stmt->lineno, NULL, stmt->connection->connection, stmt->compat);
 	}
 	else
 
@@ -1148,8 +1148,6 @@ ECPGexecute(struct statement * stmt)
 		bool		clear_result = TRUE;
 		struct sqlca_t *sqlca = ECPGget_sqlca();
 
-		errmsg = PQresultErrorMessage(results);
-		
 		var = stmt->outlist;
 		switch (PQresultStatus(results))
 		{
@@ -1167,7 +1165,7 @@ ECPGexecute(struct statement * stmt)
 					if (ntuples)
 						ECPGlog("ECPGexecute line %d: Incorrect number of matches: %d\n",
 								stmt->lineno, ntuples);
-					ECPGraise(stmt->lineno, ECPG_NOT_FOUND, NULL, stmt->compat);
+					ECPGraise(stmt->lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL);
 					status = false;
 					break;
 				}
@@ -1198,21 +1196,21 @@ ECPGexecute(struct statement * stmt)
 						}
 						else if (!INFORMIX_MODE(stmt->compat))
 						{
-							ECPGraise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, NULL, stmt->compat);
+							ECPGraise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_TARGETS, NULL);
 							return (false);
 						}
 					}
 
 				if (status && var != NULL)
 				{
-					ECPGraise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, NULL, stmt->compat);
+					ECPGraise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_TARGETS, NULL);
 					status = false;
 				}
 
 				break;
 			case PGRES_EMPTY_QUERY:
 				/* do nothing */
-				ECPGraise(stmt->lineno, ECPG_EMPTY, NULL, stmt->compat);
+				ECPGraise(stmt->lineno, ECPG_EMPTY, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, NULL);
 				break;
 			case PGRES_COMMAND_OK:
 				status = true;
@@ -1225,13 +1223,13 @@ ECPGexecute(struct statement * stmt)
 							( !strncmp(cmdstat, "UPDATE", 6)
 							  || !strncmp(cmdstat, "INSERT", 6)
 							  || !strncmp(cmdstat, "DELETE", 6)))
-					ECPGraise(stmt->lineno, ECPG_NOT_FOUND, NULL, stmt->compat);
+					ECPGraise(stmt->lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL);
 				break;
 			case PGRES_NONFATAL_ERROR:
 			case PGRES_FATAL_ERROR:
 			case PGRES_BAD_RESPONSE:
-				ECPGlog("ECPGexecute line %d: Error: %s", stmt->lineno, errmsg);
-				ECPGraise(stmt->lineno, ECPG_PGSQL, errmsg, stmt->compat);
+				ECPGlog("ECPGexecute line %d: Error: %s", stmt->lineno, PQresultErrorMessage(results));
+				ECPGraise_backend(stmt->lineno, results, stmt->connection->connection, stmt->compat);
 				status = false;
 				break;
 			case PGRES_COPY_OUT:
@@ -1245,7 +1243,7 @@ ECPGexecute(struct statement * stmt)
 			default:
 				ECPGlog("ECPGexecute line %d: Got something else, postgres error.\n",
 						stmt->lineno);
-				ECPGraise(stmt->lineno, ECPG_PGSQL, errmsg, stmt->compat);
+				ECPGraise_backend(stmt->lineno, results, stmt->connection->connection, stmt->compat);
 				status = false;
 				break;
 		}
@@ -1300,7 +1298,7 @@ ECPGdo(int lineno, int compat, int force_indicator, const char *connection_name,
 	if (con == NULL || con->connection == NULL)
 	{
 		free_statement(stmt);
-		ECPGraise(lineno, ECPG_NOT_CONN, (con) ? con->name : "<empty>", stmt->compat);
+		ECPGraise(lineno, ECPG_NOT_CONN, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, (con) ? con->name : "<empty>");
 		setlocale(LC_NUMERIC, oldlocale);
 		ECPGfree(oldlocale);
 		return false;
diff --git a/src/interfaces/ecpg/ecpglib/extern.h b/src/interfaces/ecpg/ecpglib/extern.h
index 9f16fc24efaf49386d3cce1027967e4dd9723062..d98f158bacc0cb52473940c8ddd0b0a6a4ac78c9 100644
--- a/src/interfaces/ecpg/ecpglib/extern.h
+++ b/src/interfaces/ecpg/ecpglib/extern.h
@@ -98,4 +98,30 @@ PGresult  **
 bool ECPGstore_result(const PGresult *results, int act_field,
 				 const struct statement * stmt, struct variable * var);
 
+/* SQLSTATE values generated or processed by ecpglib (intentionally
+ * not exported -- users should refer to the codes directly) */
+
+#define ECPG_SQLSTATE_NO_DATA				"02000"
+#define ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS	"07001"
+#define ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_TARGETS		"07002"
+#define ECPG_SQLSTATE_RESTRICTED_DATA_TYPE_ATTRIBUTE_VIOLATION	"07006"
+#define ECPG_SQLSTATE_INVALID_DESCRIPTOR_INDEX		"07009"
+#define ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION	"08001"
+#define ECPG_SQLSTATE_CONNECTION_DOES_NOT_EXIST		"08003"
+#define ECPG_SQLSTATE_TRANSACTION_RESOLUTION_UNKNOWN	"08007"
+#define ECPG_SQLSTATE_CARDINALITY_VIOLATION	"21000"
+#define ECPG_SQLSTATE_NULL_VALUE_NO_INDICATOR_PARAMETER "22002"
+#define ECPG_SQLSTATE_ACTIVE_SQL_TRANSACTION		"25001"
+#define ECPG_SQLSTATE_NO_ACTIVE_SQL_TRANSACTION		"25P01"
+#define ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME	"26000"
+#define ECPG_SQLSTATE_INVALID_SQL_DESCRIPTOR_NAME	"33000"
+#define ECPG_SQLSTATE_INVALID_CURSOR_NAME	"34000"
+#define ECPG_SQLSTATE_SYNTAX_ERROR			"42601"
+#define ECPG_SQLSTATE_DATATYPE_MISMATCH		"42804"
+#define ECPG_SQLSTATE_DUPLICATE_CURSOR		"42P03"
+
+/* implementation-defined internal errors of ecpg */
+#define ECPG_SQLSTATE_ECPG_INTERNAL_ERROR	"YE000"
+#define ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY	"YE001"
+
 #endif /* _ECPG_LIB_EXTERN_H */
diff --git a/src/interfaces/ecpg/ecpglib/memory.c b/src/interfaces/ecpg/ecpglib/memory.c
index 0a77da4f09528760a55bd849b54e522521f34faa..524192d7ca8891922deb04396b9c636f0ad529a9 100644
--- a/src/interfaces/ecpg/ecpglib/memory.c
+++ b/src/interfaces/ecpg/ecpglib/memory.c
@@ -1,4 +1,4 @@
-/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/memory.c,v 1.3 2003/08/01 08:21:04 meskes Exp $ */
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/memory.c,v 1.4 2003/08/01 13:53:36 petere Exp $ */
 
 #define POSTGRES_ECPG_INTERNAL
 #include "postgres_fe.h"
@@ -21,7 +21,7 @@ ECPGalloc(long size, int lineno)
 
 	if (!new)
 	{
-		ECPGraise(lineno, ECPG_OUT_OF_MEMORY, NULL, ECPG_COMPAT_PGSQL);
+		ECPGraise(lineno, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
 		return NULL;
 	}
 
@@ -36,7 +36,7 @@ ECPGrealloc(void *ptr, long size, int lineno)
 
 	if (!new)
 	{
-		ECPGraise(lineno, ECPG_OUT_OF_MEMORY, NULL, ECPG_COMPAT_PGSQL);
+		ECPGraise(lineno, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
 		return NULL;
 	}
 
@@ -50,7 +50,7 @@ ECPGstrdup(const char *string, int lineno)
 
 	if (!new)
 	{
-		ECPGraise(lineno, ECPG_OUT_OF_MEMORY, NULL, ECPG_COMPAT_PGSQL);
+		ECPGraise(lineno, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
 		return NULL;
 	}
 
diff --git a/src/interfaces/ecpg/ecpglib/misc.c b/src/interfaces/ecpg/ecpglib/misc.c
index 4334844d61d6a2b66b609d6ab945a03c1cbd1edc..f42b8f721e79a70b3d96fe862bd511a34b1ab2ff 100644
--- a/src/interfaces/ecpg/ecpglib/misc.c
+++ b/src/interfaces/ecpg/ecpglib/misc.c
@@ -1,4 +1,4 @@
-/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/misc.c,v 1.11 2003/08/01 08:21:04 meskes Exp $ */
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/misc.c,v 1.12 2003/08/01 13:53:36 petere Exp $ */
 
 #define POSTGRES_ECPG_INTERNAL
 #include "postgres_fe.h"
@@ -47,7 +47,7 @@ static struct sqlca_t sqlca_init =
 		0, 0, 0, 0, 0, 0, 0, 0
 	},
 	{
-		0, 0, 0, 0, 0, 0, 0, 0
+		'0', '0', '0', '0', '0'
 	}
 };
 
@@ -78,7 +78,7 @@ static struct sqlca_t sqlca =
 		0, 0, 0, 0, 0, 0, 0, 0
 	},
 	{
-		0, 0, 0, 0, 0, 0, 0, 0
+		'0', '0', '0', '0', '0'
 	}
 };
 #endif
@@ -103,7 +103,8 @@ ECPGinit(const struct connection * con, const char *connection_name, const int l
 	ECPGinit_sqlca(sqlca);
 	if (con == NULL)
 	{
-		ECPGraise(lineno, ECPG_NO_CONN, connection_name ? connection_name : "NULL", ECPG_COMPAT_PGSQL);
+		ECPGraise(lineno, ECPG_NO_CONN, ECPG_SQLSTATE_CONNECTION_DOES_NOT_EXIST,
+				  connection_name ? connection_name : "NULL");
 		return (false);
 	}
 
@@ -150,7 +151,7 @@ ECPGstatus(int lineno, const char *connection_name)
 	/* are we connected? */
 	if (con->connection == NULL)
 	{
-		ECPGraise(lineno, ECPG_NOT_CONN, con->name, ECPG_COMPAT_PGSQL);
+		ECPGraise(lineno, ECPG_NOT_CONN, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, con->name);
 		return false;
 	}
 
@@ -179,7 +180,7 @@ ECPGtrans(int lineno, const char *connection_name, const char *transaction)
 		{
 			if ((res = PQexec(con->connection, transaction)) == NULL)
 			{
-				ECPGraise(lineno, ECPG_TRANS, NULL, ECPG_COMPAT_PGSQL);
+				ECPGraise(lineno, ECPG_TRANS, ECPG_SQLSTATE_TRANSACTION_RESOLUTION_UNKNOWN, NULL);
 				return FALSE;
 			}
 			PQclear(res);
diff --git a/src/interfaces/ecpg/ecpglib/prepare.c b/src/interfaces/ecpg/ecpglib/prepare.c
index 76c58d1238cb3c9c599b8c8dff52b83e7012dc42..5569d36da07d9d28c28f747c3de6b411adc03b3e 100644
--- a/src/interfaces/ecpg/ecpglib/prepare.c
+++ b/src/interfaces/ecpg/ecpglib/prepare.c
@@ -1,4 +1,4 @@
-/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/prepare.c,v 1.7 2003/08/01 08:21:04 meskes Exp $ */
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/prepare.c,v 1.8 2003/08/01 13:53:36 petere Exp $ */
 
 #define POSTGRES_ECPG_INTERNAL
 #include "postgres_fe.h"
@@ -120,7 +120,7 @@ ECPGdeallocate(int lineno, int c, char *name)
 	}
 	
 	if (!ret) 
-		ECPGraise(lineno, ECPG_INVALID_STMT, name, ECPG_COMPAT_PGSQL);
+		ECPGraise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, name);
 
 	return ret;
 }
diff --git a/src/interfaces/ecpg/include/ecpglib.h b/src/interfaces/ecpg/include/ecpglib.h
index e99c50bc8664cc15afa118ce97bb20eb9a535d2d..3f510fbfa600ec40f6351d79bc3c2b0c15a9d69e 100644
--- a/src/interfaces/ecpg/include/ecpglib.h
+++ b/src/interfaces/ecpg/include/ecpglib.h
@@ -6,7 +6,8 @@
 #ifndef _ECPGLIB_H
 #define _ECPGLIB_H
 
-#include <stdio.h>
+#include "postgres_fe.h"
+#include "libpq-fe.h"
 
 #ifndef __BEOS__
 #ifndef __cplusplus
@@ -71,7 +72,8 @@ bool ECPGdo_descriptor(int line, const char *connection,
 				  const char *descriptor, const char *query);
 bool		ECPGdeallocate_desc(int line, const char *name);
 bool		ECPGallocate_desc(int line, const char *name);
-void		ECPGraise(int line, int code, const char *str, int);
+void		ECPGraise(int line, int code, const char *sqlstate, const char *str);
+void		ECPGraise_backend(int line, PGresult *result, PGconn *conn, int compat);
 bool		ECPGget_desc_header(int, char *, int *);
 bool		ECPGget_desc(int, char *, int,...);
 
diff --git a/src/interfaces/ecpg/include/sqlca.h b/src/interfaces/ecpg/include/sqlca.h
index 8af23921d78a8d66fea8f211e70be5427cfce5b9..37370b6f60589540f27ef31e5ff76dacbd6465d6 100644
--- a/src/interfaces/ecpg/include/sqlca.h
+++ b/src/interfaces/ecpg/include/sqlca.h
@@ -50,7 +50,7 @@ struct sqlca_t
 	/* 6: empty						*/
 	/* 7: empty						*/
 
-	char		sqlext[8];
+	char		sqlstate[5];
 };
 
 struct sqlca_t *ECPGget_sqlca(void);
diff --git a/src/interfaces/ecpg/test/Makefile b/src/interfaces/ecpg/test/Makefile
index 7e7f1fee07da05416358de8bac949ef3d2ad725d..357779863d0aa905386e9a0f9e6016e6cd0aab44 100644
--- a/src/interfaces/ecpg/test/Makefile
+++ b/src/interfaces/ecpg/test/Makefile
@@ -1,10 +1,10 @@
-# $Header: /cvsroot/pgsql/src/interfaces/ecpg/test/Makefile,v 1.38 2003/07/01 12:40:52 meskes Exp $
+# $Header: /cvsroot/pgsql/src/interfaces/ecpg/test/Makefile,v 1.39 2003/08/01 13:53:36 petere Exp $
 
 subdir = src/interfaces/ecpg/test
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-override CPPFLAGS := -I$(srcdir)/../include $(CPPFLAGS)
+override CPPFLAGS := -I$(srcdir)/../include -I$(libpq_srcdir) $(CPPFLAGS)
 
 ECPG = ../preproc/ecpg -I$(srcdir)/../include