diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index 52cb7c09150dcd941d138cabb49426d1f1374c4c..5d72e1377b5b425dbea7c6290511f771be88ec10 100644
--- a/config/c-compiler.m4
+++ b/config/c-compiler.m4
@@ -1,5 +1,5 @@
 # Macros to detect C compiler features
-# $Header: /cvsroot/pgsql/config/c-compiler.m4,v 1.7 2003/04/06 22:45:22 petere Exp $
+# $Header: /cvsroot/pgsql/config/c-compiler.m4,v 1.8 2003/04/24 21:16:42 tgl Exp $
 
 
 # PGAC_C_SIGNED
@@ -94,3 +94,29 @@ AC_DEFINE_UNQUOTED(AS_TR_CPP(alignof_$1),
                    [$AS_TR_SH([pgac_cv_alignof_$1])],
                    [The alignment requirement of a `$1'.])
 ])# PGAC_CHECK_ALIGNOF
+
+
+# PGAC_C_FUNCNAME_SUPPORT
+# -------------
+# Check if the C compiler understands __func__ (C99) or __FUNCTION__ (gcc).
+# Define HAVE_FUNCNAME__FUNC or HAVE_FUNCNAME__FUNCTION accordingly.
+AC_DEFUN([PGAC_C_FUNCNAME_SUPPORT],
+[AC_CACHE_CHECK(for __func__, pgac_cv_funcname_func_support,
+[AC_TRY_COMPILE([#include <stdio.h>],
+[printf("%s\n", __func__);],
+[pgac_cv_funcname_func_support=yes],
+[pgac_cv_funcname_func_support=no])])
+if test x"$pgac_cv_funcname_func_support" = xyes ; then
+AC_DEFINE(HAVE_FUNCNAME__FUNC, 1,
+          [Define to 1 if your compiler understands __func__.])
+else
+AC_CACHE_CHECK(for __FUNCTION__, pgac_cv_funcname_function_support,
+[AC_TRY_COMPILE([#include <stdio.h>],
+[printf("%s\n", __FUNCTION__);],
+[pgac_cv_funcname_function_support=yes],
+[pgac_cv_funcname_function_support=no])])
+if test x"$pgac_cv_funcname_function_support" = xyes ; then
+AC_DEFINE(HAVE_FUNCNAME__FUNCTION, 1,
+          [Define to 1 if your compiler understands __FUNCTION__.])
+fi
+fi])# PGAC_C_FUNCNAME_SUPPORT
diff --git a/configure b/configure
index 150c958ca09372c65b09de48027ca3c54645f8d0..85873ff60f052b902318a6abe7981a7dd5c1542f 100755
--- a/configure
+++ b/configure
@@ -9051,6 +9051,111 @@ _ACEOF
 
 fi
 
+echo "$as_me:$LINENO: checking for __func__" >&5
+echo $ECHO_N "checking for __func__... $ECHO_C" >&6
+if test "${pgac_cv_funcname_func_support+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+#line $LINENO "configure"
+#include "confdefs.h"
+#include <stdio.h>
+#ifdef F77_DUMMY_MAIN
+#  ifdef __cplusplus
+     extern "C"
+#  endif
+   int F77_DUMMY_MAIN() { return 1; }
+#endif
+int
+main ()
+{
+printf("%s\n", __func__);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+         { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  pgac_cv_funcname_func_support=yes
+else
+  echo "$as_me: failed program was:" >&5
+cat conftest.$ac_ext >&5
+pgac_cv_funcname_func_support=no
+fi
+rm -f conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $pgac_cv_funcname_func_support" >&5
+echo "${ECHO_T}$pgac_cv_funcname_func_support" >&6
+if test x"$pgac_cv_funcname_func_support" = xyes ; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_FUNCNAME__FUNC 1
+_ACEOF
+
+else
+echo "$as_me:$LINENO: checking for __FUNCTION__" >&5
+echo $ECHO_N "checking for __FUNCTION__... $ECHO_C" >&6
+if test "${pgac_cv_funcname_function_support+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+#line $LINENO "configure"
+#include "confdefs.h"
+#include <stdio.h>
+#ifdef F77_DUMMY_MAIN
+#  ifdef __cplusplus
+     extern "C"
+#  endif
+   int F77_DUMMY_MAIN() { return 1; }
+#endif
+int
+main ()
+{
+printf("%s\n", __FUNCTION__);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+         { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  pgac_cv_funcname_function_support=yes
+else
+  echo "$as_me: failed program was:" >&5
+cat conftest.$ac_ext >&5
+pgac_cv_funcname_function_support=no
+fi
+rm -f conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $pgac_cv_funcname_function_support" >&5
+echo "${ECHO_T}$pgac_cv_funcname_function_support" >&6
+if test x"$pgac_cv_funcname_function_support" = xyes ; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_FUNCNAME__FUNCTION 1
+_ACEOF
+
+fi
+fi
 echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5
 echo $ECHO_N "checking whether struct tm is in sys/time.h or time.h... $ECHO_C" >&6
 if test "${ac_cv_struct_tm+set}" = set; then
diff --git a/configure.in b/configure.in
index 9e50e487c9415e84e2fe75a81ddb81113655318c..10843597a59e2cb6be01526a896f19df952f544c 100644
--- a/configure.in
+++ b/configure.in
@@ -1,5 +1,5 @@
 dnl Process this file with autoconf to produce a configure script.
-dnl $Header: /cvsroot/pgsql/configure.in,v 1.243 2003/04/22 02:18:09 momjian Exp $
+dnl $Header: /cvsroot/pgsql/configure.in,v 1.244 2003/04/24 21:16:42 tgl Exp $
 dnl
 dnl Developers, please strive to achieve this order:
 dnl
@@ -733,6 +733,7 @@ AC_C_INLINE
 AC_C_STRINGIZE
 PGAC_C_SIGNED
 AC_C_VOLATILE
+PGAC_C_FUNCNAME_SUPPORT
 AC_STRUCT_TIMEZONE
 PGAC_UNION_SEMUN
 PGAC_STRUCT_SOCKADDR_UN
diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index 52d2a60c3b2bc0169463cd587ca7e58ebaff41a0..7b5f9593a985fbb4517c91488dff3e546423f418 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -1,4 +1,4 @@
-<!-- $Header: /cvsroot/pgsql/doc/src/sgml/protocol.sgml,v 1.29 2003/04/22 00:08:06 tgl Exp $ -->
+<!-- $Header: /cvsroot/pgsql/doc/src/sgml/protocol.sgml,v 1.30 2003/04/24 21:16:42 tgl Exp $ -->
 
 <chapter id="protocol">
  <title>Frontend/Backend Protocol</title>
@@ -3862,6 +3862,14 @@ byte (except for startup packets, which have no type byte).  Also note that
 PasswordMessage now has a type byte.
 </para>
 
+<para>
+ErrorResponse and NoticeResponse ('<literal>E</>' and '<literal>N</>')
+messages now contain multiple fields, from which the client code may
+assemble an error message of the desired level of verbosity.  Note that
+individual fields will typically not end with a newline, whereas the single
+string sent in the older protocol always did.
+</para>
+
 <para>
 COPY data is now encapsulated into CopyData and CopyDone messages.  There
 is a well-defined way to recover from errors during COPY.  The special
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 90c26288f52903c9f1866681d94b37fc06850943..842a36c57bfbd305dfa65614c1bff679c56cdc39 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -13,7 +13,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.49 2003/03/20 03:34:55 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.50 2003/04/24 21:16:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1280,7 +1280,7 @@ NameListToString(List *names)
 	{
 		if (l != names)
 			appendStringInfoChar(&string, '.');
-		appendStringInfo(&string, "%s", strVal(lfirst(l)));
+		appendStringInfoString(&string, strVal(lfirst(l)));
 	}
 
 	return string.data;
@@ -1305,7 +1305,7 @@ NameListToQuotedString(List *names)
 	{
 		if (l != names)
 			appendStringInfoChar(&string, '.');
-		appendStringInfo(&string, "%s", quote_identifier(strVal(lfirst(l))));
+		appendStringInfoString(&string, quote_identifier(strVal(lfirst(l))));
 	}
 
 	return string.data;
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 40948e3a3b51c17df85a403ff5bbcf61a0d3a208..7cb530a3cda5239470aa3a479aa3b6ddf4961b12 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.195 2003/04/22 00:08:06 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.196 2003/04/24 21:16:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -100,13 +100,13 @@ static const char BinarySignature[12] = "PGBCOPY\n\377\r\n\0";
  * Static communication variables ... pretty grotty, but COPY has
  * never been reentrant...
  */
-int			copy_lineno = 0;	/* exported for use by elog() -- dz */
-
 static CopyDest copy_dest;
 static FILE *copy_file;			/* if copy_dest == COPY_FILE */
 static StringInfo copy_msgbuf;	/* if copy_dest == COPY_NEW_FE */
 static bool fe_eof;				/* true if detected end of copy data */
-static EolType eol_type;
+static EolType eol_type;		/* EOL type of input */
+static int	copy_lineno;		/* line number for error messages */
+
 
 /*
  * These static variables are used to avoid incurring overhead for each
@@ -1000,6 +1000,16 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
 }
 
 
+/*
+ * error context callback for COPY FROM
+ */
+static void
+copy_in_error_callback(void *arg)
+{
+	errcontext("COPY FROM, line %d", copy_lineno);
+}
+
+
 /*
  * Copy FROM file to relation.
  */
@@ -1032,6 +1042,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
 	ExprState **defexprs;		/* array of default att expressions */
 	ExprContext *econtext;		/* used for ExecEvalExpr for default atts */
 	MemoryContext oldcontext = CurrentMemoryContext;
+	ErrorContextCallback errcontext;
 
 	tupDesc = RelationGetDescr(rel);
 	attr = tupDesc->attrs;
@@ -1188,16 +1199,22 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
 	values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
 	nulls = (char *) palloc(num_phys_attrs * sizeof(char));
 
-	/* Initialize static variables */
-	copy_lineno = 0;
-	eol_type = EOL_UNKNOWN;
-	fe_eof = false;
-
 	/* Make room for a PARAM_EXEC value for domain constraint checks */
 	if (hasConstraints)
 		econtext->ecxt_param_exec_vals = (ParamExecData *)
 			palloc0(sizeof(ParamExecData));
 
+	/* Initialize static variables */
+	fe_eof = false;
+	eol_type = EOL_UNKNOWN;
+	copy_lineno = 0;
+
+	/* Set up callback to identify error line number */
+	errcontext.callback = copy_in_error_callback;
+	errcontext.arg = NULL;
+	errcontext.previous = error_context_stack;
+	error_context_stack = &errcontext;
+
 	while (!done)
 	{
 		bool		skip_tuple;
@@ -1502,7 +1519,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
 	/*
 	 * Done, clean up
 	 */
-	copy_lineno = 0;
+	error_context_stack = errcontext.previous;
 
 	MemoryContextSwitchTo(oldcontext);
 
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 09d422f85c055259782c3f1eca4be836491ed4f3..d117d2e9a23e05f4fb967fb8c37cad3e1db291bd 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.105 2003/04/03 22:35:48 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.106 2003/04/24 21:16:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -320,7 +320,7 @@ explain_outNode(StringInfo str,
 
 	if (plan == NULL)
 	{
-		appendStringInfo(str, "\n");
+		appendStringInfoChar(str, '\n');
 		return;
 	}
 
@@ -476,13 +476,13 @@ explain_outNode(StringInfo str,
 			break;
 	}
 
-	appendStringInfo(str, pname);
+	appendStringInfoString(str, pname);
 	switch (nodeTag(plan))
 	{
 		case T_IndexScan:
 			if (ScanDirectionIsBackward(((IndexScan *) plan)->indxorderdir))
-				appendStringInfo(str, " Backward");
-			appendStringInfo(str, " using ");
+				appendStringInfoString(str, " Backward");
+			appendStringInfoString(str, " using ");
 			i = 0;
 			foreach(l, ((IndexScan *) plan)->indxid)
 			{
@@ -590,7 +590,7 @@ explain_outNode(StringInfo str,
 			appendStringInfo(str, " (never executed)");
 		}
 	}
-	appendStringInfo(str, "\n");
+	appendStringInfoChar(str, '\n');
 
 	/* quals, sort keys, etc */
 	switch (nodeTag(plan))
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 7184fbf9ba173f19838aea7953dcc68cf0153b1d..faceb1ec7388290a3de6d98de54beb820a481e3f 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.89 2003/03/27 16:51:28 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.90 2003/04/24 21:16:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -995,7 +995,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
 	 * Parse the request string into a list of raw parse trees.
 	 */
 	initStringInfo(&stri);
-	appendStringInfo(&stri, "%s", src);
+	appendStringInfoString(&stri, src);
 
 	raw_parsetree_list = pg_parse_query(&stri, argtypes, nargs);
 
diff --git a/src/backend/lib/stringinfo.c b/src/backend/lib/stringinfo.c
index 0f758b1bd2dda4c8a168e35853f2365719be1ac2..03251beed907b7c93bc49f2acb86b84a99d2d9a1 100644
--- a/src/backend/lib/stringinfo.c
+++ b/src/backend/lib/stringinfo.c
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	  $Id: stringinfo.c,v 1.33 2003/04/19 00:02:29 tgl Exp $
+ *	  $Id: stringinfo.c,v 1.34 2003/04/24 21:16:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -56,61 +56,102 @@ initStringInfo(StringInfo str)
 /*
  * appendStringInfo
  *
- * Format text data under the control of fmt (an sprintf-like format string)
+ * Format text data under the control of fmt (an sprintf-style format string)
  * and append it to whatever is already in str.  More space is allocated
  * to str if necessary.  This is sort of like a combination of sprintf and
  * strcat.
  */
 void
-appendStringInfo(StringInfo str, const char *fmt,...)
+appendStringInfo(StringInfo str, const char *fmt, ...)
+{
+	for (;;)
+	{
+		va_list		args;
+		bool		success;
+
+		/* Try to format the data. */
+		va_start(args, fmt);
+		success = appendStringInfoVA(str, fmt, args);
+		va_end(args);
+
+		if (success)
+			break;
+
+		/* Double the buffer size and try again. */
+		enlargeStringInfo(str, str->maxlen);
+	}
+}
+
+/*
+ * appendStringInfoVA
+ *
+ * Attempt to format text data under the control of fmt (an sprintf-style
+ * format string) and append it to whatever is already in str.  If successful
+ * return true; if not (because there's not enough space), return false
+ * without modifying str.  Typically the caller would enlarge str and retry
+ * on false return --- see appendStringInfo for standard usage pattern.
+ *
+ * XXX This API is ugly, but there seems no alternative given the C spec's
+ * restrictions on what can portably be done with va_list arguments: you have
+ * to redo va_start before you can rescan the argument list, and we can't do
+ * that from here.
+ */
+bool
+appendStringInfoVA(StringInfo str, const char *fmt, va_list args)
 {
-	va_list		args;
 	int			avail,
 				nprinted;
 
 	Assert(str != NULL);
 
-	for (;;)
-	{
-		/*
-		 * Try to format the given string into the available space; but if
-		 * there's hardly any space, don't bother trying, just fall
-		 * through to enlarge the buffer first.
-		 */
-		avail = str->maxlen - str->len - 1;
-		if (avail > 16)
-		{
-			/*
-			 * Assert check here is to catch buggy vsnprintf that overruns
-			 * the specified buffer length.  Solaris 7 in 64-bit mode is
-			 * an example of a platform with such a bug.
-			 */
+	/*
+	 * If there's hardly any space, don't bother trying, just fail to make
+	 * the caller enlarge the buffer first.
+	 */
+	avail = str->maxlen - str->len - 1;
+	if (avail < 16)
+		return false;
+
+	/*
+	 * Assert check here is to catch buggy vsnprintf that overruns
+	 * the specified buffer length.  Solaris 7 in 64-bit mode is
+	 * an example of a platform with such a bug.
+	 */
 #ifdef USE_ASSERT_CHECKING
-			str->data[str->maxlen - 1] = '\0';
+	str->data[str->maxlen - 1] = '\0';
 #endif
 
-			va_start(args, fmt);
-			nprinted = vsnprintf(str->data + str->len, avail,
-								 fmt, args);
-			va_end(args);
-
-			Assert(str->data[str->maxlen - 1] == '\0');
-
-			/*
-			 * Note: some versions of vsnprintf return the number of chars
-			 * actually stored, but at least one returns -1 on failure. Be
-			 * conservative about believing whether the print worked.
-			 */
-			if (nprinted >= 0 && nprinted < avail - 1)
-			{
-				/* Success.  Note nprinted does not include trailing null. */
-				str->len += nprinted;
-				break;
-			}
-		}
-		/* Double the buffer size and try again. */
-		enlargeStringInfo(str, str->maxlen);
+	nprinted = vsnprintf(str->data + str->len, avail, fmt, args);
+
+	Assert(str->data[str->maxlen - 1] == '\0');
+
+	/*
+	 * Note: some versions of vsnprintf return the number of chars
+	 * actually stored, but at least one returns -1 on failure. Be
+	 * conservative about believing whether the print worked.
+	 */
+	if (nprinted >= 0 && nprinted < avail - 1)
+	{
+		/* Success.  Note nprinted does not include trailing null. */
+		str->len += nprinted;
+		return true;
 	}
+
+	/* Restore the trailing null so that str is unmodified. */
+	str->data[str->len] = '\0';
+	return false;
+}
+
+/*
+ * appendStringInfoString
+ *
+ * Append a null-terminated string to str.
+ * Like appendStringInfo(str, "%s", s) but faster.
+ */
+void
+appendStringInfoString(StringInfo str, const char *s)
+{
+	appendBinaryStringInfo(str, s, strlen(s));
 }
 
 /*
@@ -163,8 +204,8 @@ appendBinaryStringInfo(StringInfo str, const char *data, int datalen)
  * Make sure there is enough space for 'needed' more bytes
  * ('needed' does not include the terminating null).
  *
- * External callers need not concern themselves with this, since all
- * stringinfo.c routines do it automatically.  However, if a caller
+ * External callers usually need not concern themselves with this, since
+ * all stringinfo.c routines do it automatically.  However, if a caller
  * knows that a StringInfo will eventually become X bytes large, it
  * can save some palloc overhead by enlarging the buffer before starting
  * to store data in it.
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index f5d285ee09348880bfdd140063fbd72826c7e71e..45c7a2a301b67464e44dece5d3f6fdb9e096c401 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.202 2003/04/08 23:20:01 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.203 2003/04/24 21:16:43 tgl Exp $
  *
  * NOTES
  *	  Every node type that can appear in stored rules' parsetrees *must*
@@ -39,7 +39,7 @@
 
 /* Write the label for the node type */
 #define WRITE_NODE_TYPE(nodelabel) \
-	appendStringInfo(str, nodelabel)
+	appendStringInfoString(str, nodelabel)
 
 /* Write an integer field (anything written as ":fldname %d") */
 #define WRITE_INT_FIELD(fldname) \
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 59690f4aefbf61d0b9492228d1ec75510bf059c3..7cbef96536989aac2b059e30d4d174479417cef8 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.145 2003/04/08 23:20:02 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.146 2003/04/24 21:16:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1288,8 +1288,8 @@ func_error(const char *caller, List *funcname,
 	for (i = 0; i < nargs; i++)
 	{
 		if (i)
-			appendStringInfo(&argbuf, ", ");
-		appendStringInfo(&argbuf, format_type_be(argtypes[i]));
+			appendStringInfoString(&argbuf, ", ");
+		appendStringInfoString(&argbuf, format_type_be(argtypes[i]));
 	}
 
 	if (caller == NULL)
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 1f0b7639d5ffc778616c6d8cdf7904489ec44e09..6d038080ea0d4a1c69406569f6d35045dda43972 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.54 2003/03/10 03:53:51 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.55 2003/04/24 21:16:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -153,13 +153,13 @@ TypeNameToString(const TypeName *typename)
 		{
 			if (l != typename->names)
 				appendStringInfoChar(&string, '.');
-			appendStringInfo(&string, "%s", strVal(lfirst(l)));
+			appendStringInfoString(&string, strVal(lfirst(l)));
 		}
 	}
 	else
 	{
 		/* Look up internally-specified type */
-		appendStringInfo(&string, "%s", format_type_be(typename->typeid));
+		appendStringInfoString(&string, format_type_be(typename->typeid));
 	}
 
 	/*
@@ -167,10 +167,10 @@ TypeNameToString(const TypeName *typename)
 	 * LookupTypeName
 	 */
 	if (typename->pct_type)
-		appendStringInfo(&string, "%%TYPE");
+		appendStringInfoString(&string, "%TYPE");
 
 	if (typename->arrayBounds != NIL)
-		appendStringInfo(&string, "[]");
+		appendStringInfoString(&string, "[]");
 
 	return string.data;
 }
diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l
index 9fc28a859efc275de45c27c96c5b60c0fdf86b34..05b488643e389fd587f10cfd65a80c65d92c486d 100644
--- a/src/backend/parser/scan.l
+++ b/src/backend/parser/scan.l
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/scan.l,v 1.103 2002/11/11 03:33:38 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/scan.l,v 1.104 2003/04/24 21:16:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -33,7 +33,7 @@
 #define YY_READ_BUF_SIZE 16777216
 
 /* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */
-#define fprintf(file, fmt, msg)  elog(FATAL, "%s", (msg))
+#define fprintf(file, fmt, msg)  ereport(FATAL, (errmsg_internal("%s", msg)))
 
 extern YYSTYPE yylval;
 
@@ -575,12 +575,19 @@ void
 yyerror(const char *message)
 {
 	const char *loc = token_start ? token_start : yytext;
+	int			cursorpos;
+
+	/* in multibyte encodings, return index in characters not bytes */
+	cursorpos = pg_mbstrlen_with_len(scanbuf, loc - scanbuf) + 1;
 
 	if (*loc == YY_END_OF_BUFFER_CHAR)
-		elog(ERROR, "parser: %s at end of input", message);
+		ereport(ERROR,
+				(errmsg("parser: %s at end of input", message),
+				 errposition(cursorpos)));
 	else
-		elog(ERROR, "parser: %s at or near \"%s\" at character %d",
-			 message, loc, (int) (loc - scanbuf + 1));
+		ereport(ERROR,
+				(errmsg("parser: %s at or near \"%s\"", message, loc),
+				 errposition(cursorpos)));
 }
 
 
@@ -591,7 +598,7 @@ void
 scanner_init(StringInfo str)
 {
 	/*
-	 * Might be left over after elog()
+	 * Might be left over after ereport()
 	 */
 	if (YY_CURRENT_BUFFER)
 		yy_delete_buffer(YY_CURRENT_BUFFER);
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 5c51a1056a266b582042a7114c9c51403a2d039e..bc884906ca04cd8a9cc63e638b8c3b1f015bf254 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.323 2003/04/22 00:08:07 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.324 2003/04/24 21:16:43 tgl Exp $
  *
  * NOTES
  *	  this is the "main" module of the postgres backend and
@@ -349,7 +349,7 @@ pg_parse_and_rewrite(char *query_string,		/* string to execute */
 	StringInfoData stri;
 
 	initStringInfo(&stri);
-	appendStringInfo(&stri, "%s", query_string);
+	appendStringInfoString(&stri, query_string);
 
 	/*
 	 * (1) parse the request string into a list of raw parse trees.
@@ -1831,7 +1831,7 @@ PostgresMain(int argc, char *argv[], const char *username)
 	if (!IsUnderPostmaster)
 	{
 		puts("\nPOSTGRES backend interactive interface ");
-		puts("$Revision: 1.323 $ $Date: 2003/04/22 00:08:07 $\n");
+		puts("$Revision: 1.324 $ $Date: 2003/04/24 21:16:43 $\n");
 	}
 
 	/*
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 31135ff97fbba48180cfc56b0d97c4bfdceee890..581c5b20ecf59944e437ada28931256f309eb967 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3,7 +3,7 @@
  *				back to source text
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.138 2003/04/08 23:20:02 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.139 2003/04/24 21:16:43 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -921,7 +921,7 @@ pg_get_constraintdef(PG_FUNCTION_ARGS)
 						 constraintId);
 
 				/* Append the constraint source */
-				appendStringInfo(&buf, DatumGetCString(DirectFunctionCall1(textout, val))); 
+				appendStringInfoString(&buf, DatumGetCString(DirectFunctionCall1(textout, val))); 
 
 				break;
 			}
@@ -2846,7 +2846,7 @@ get_const_expr(Const *constval, deparse_context *context)
 				 */
 				if (strspn(extval, "0123456789+-eE.") == strlen(extval))
 				{
-					appendStringInfo(buf, extval);
+					appendStringInfoString(buf, extval);
 					if (strcspn(extval, "eE.") != strlen(extval))
 						isfloat = true;	/* it looks like a float */
 				}
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 974e7f8fc676db8aaadfb4a185396080f59930a0..7f928ec8acdd64f717561023832874a01814d723 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.95 2003/03/10 22:28:18 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.96 2003/04/24 21:16:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1696,8 +1696,8 @@ replace_text(PG_FUNCTION_ARGS)
 		left_text = LEFT(buf_text, from_sub_text);
 		right_text = RIGHT(buf_text, from_sub_text, from_sub_text_len);
 
-		appendStringInfo(str, PG_TEXT_GET_STR(left_text));
-		appendStringInfo(str, to_sub_str);
+		appendStringInfoString(str, PG_TEXT_GET_STR(left_text));
+		appendStringInfoString(str, to_sub_str);
 
 		pfree(buf_text);
 		pfree(left_text);
@@ -1705,7 +1705,7 @@ replace_text(PG_FUNCTION_ARGS)
 		curr_posn = TEXTPOS(buf_text, from_sub_text);
 	}
 
-	appendStringInfo(str, PG_TEXT_GET_STR(buf_text));
+	appendStringInfoString(str, PG_TEXT_GET_STR(buf_text));
 	pfree(buf_text);
 
 	ret_text = PG_STR_GET_TEXT(str->data);
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 01250f9a2f06495998b119783ab2e3b46f9244ba..6ca9f384017483238135303b8cc8c22adaed757b 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -1,14 +1,43 @@
 /*-------------------------------------------------------------------------
  *
  * elog.c
- *	  error logger
+ *	  error logging and reporting
+ *
+ * Some notes about recursion and errors during error processing:
+ *
+ * We need to be robust about recursive-error scenarios --- for example,
+ * if we run out of memory, it's important to be able to report that fact.
+ * There are a number of considerations that go into this.
+ *
+ * First, distinguish between re-entrant use and actual recursion.  It
+ * is possible for an error or warning message to be emitted while the
+ * parameters for an error message are being computed.  In this case
+ * errstart has been called for the outer message, and some field values
+ * may have already been saved, but we are not actually recursing.  We handle
+ * this by providing a (small) stack of ErrorData records.  The inner message
+ * can be computed and sent without disturbing the state of the outer message.
+ * (If the inner message is actually an error, this isn't very interesting
+ * because control won't come back to the outer message generator ... but
+ * if the inner message is only debug or log data, this is critical.)
+ *
+ * Second, actual recursion will occur if an error is reported by one of
+ * the elog.c routines or something they call.  By far the most probable
+ * scenario of this sort is "out of memory"; and it's also the nastiest
+ * to handle because we'd likely also run out of memory while trying to
+ * report this error!  Our escape hatch for this condition is to force any
+ * such messages up to ERROR level if they aren't already (so that we will
+ * not need to return to the outer elog.c call), and to reset the ErrorContext
+ * to empty before trying to process the inner message.  Since ErrorContext
+ * is guaranteed to have at least 8K of space in it (see mcxt.c), we should
+ * be able to process an "out of memory" message successfully.
+ *
  *
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/error/elog.c,v 1.108 2003/04/22 00:08:07 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/error/elog.c,v 1.109 2003/04/24 21:16:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,17 +54,22 @@
 #include <syslog.h>
 #endif
 
-#include "commands/copy.h"
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
+#include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "storage/ipc.h"
-#include "storage/proc.h"
 #include "tcop/tcopprot.h"
 #include "utils/memutils.h"
 #include "utils/guc.h"
 
-#include "mb/pg_wchar.h"
+
+/* Global variables */
+ErrorContextCallback *error_context_stack = NULL;
+
+/* GUC parameters */
+bool		Log_timestamp;		/* show timestamps in stderr output */
+bool		Log_pid;			/* show PIDs in stderr output */
 
 #ifdef HAVE_SYSLOG
 /*
@@ -45,108 +79,118 @@
  * ... in theory anyway
  */
 int			Use_syslog = 0;
-char	   *Syslog_facility;
+char	   *Syslog_facility;	/* openlog() parameters */
 char	   *Syslog_ident;
 
 static void write_syslog(int level, const char *line);
 
 #else
+
 #define Use_syslog 0
-#endif
 
-bool		Log_timestamp;
-bool		Log_pid;
+#endif /* HAVE_SYSLOG */
+
 
-#define TIMESTAMP_SIZE 20		/* format `YYYY-MM-DD HH:MM:SS ' */
-#define PID_SIZE 9				/* format `[123456] ' */
+/*
+ * ErrorData holds the data accumulated during any one ereport() cycle.
+ * Any non-NULL pointers must point to palloc'd data in ErrorContext.
+ * (The const pointers are an exception; we assume they point at non-freeable
+ * constant strings.)
+ */
 
+typedef struct ErrorData
+{
+	int			elevel;			/* error level */
+	bool		output_to_server; /* will report to server log? */
+	bool		output_to_client; /* will report to client? */
+	bool		show_funcname;	/* true to force funcname inclusion */
+	const char *filename;		/* __FILE__ of ereport() call */
+	int			lineno;			/* __LINE__ of ereport() call */
+	const char *funcname;		/* __func__ of ereport() call */
+	int			sqlerrcode;		/* encoded ERRSTATE */
+	char	   *message;		/* primary error message */
+	char	   *detail;			/* detail error message */
+	char	   *hint;			/* hint message */
+	char	   *context;		/* context message */
+	int			cursorpos;		/* cursor index into query string */
+	int			saved_errno;	/* errno at entry */
+} ErrorData;
+
+/* We provide a small stack of ErrorData records for re-entrant cases */
+#define ERRORDATA_STACK_SIZE  5
+
+static ErrorData errordata[ERRORDATA_STACK_SIZE];
+
+static int	errordata_stack_depth = -1; /* index of topmost active frame */
+
+static int	recursion_depth = 0;		/* to detect actual recursion */
+
+
+/* Macro for checking errordata_stack_depth is reasonable */
+#define CHECK_STACK_DEPTH() \
+	do { \
+		if (errordata_stack_depth < 0) \
+		{ \
+			errordata_stack_depth = -1; \
+			ereport(ERROR, (errmsg_internal("errstart was not called"))); \
+		} \
+	} while (0)
+
+
+static void send_message_to_server_log(ErrorData *edata);
+static void send_message_to_frontend(ErrorData *edata);
+static char *expand_fmt_string(const char *fmt, ErrorData *edata);
+static const char *useful_strerror(int errnum);
+static const char *error_severity(int elevel);
 static const char *print_timestamp(void);
 static const char *print_pid(void);
-static void send_message_to_frontend(int type, const char *msg);
-static const char *useful_strerror(int errnum);
-static const char *elog_message_prefix(int lev);
-
-static int	Debugfile = -1;
 
 
-/*--------------------
- * elog
- *		Primary error logging function.
- *
- * 'lev': error level; indicates recovery action to take, if any.
- * 'fmt': a printf-style string.
- * Additional arguments, if any, are formatted per %-escapes in 'fmt'.
- *
- * In addition to the usual %-escapes recognized by printf, "%m" in
- * fmt is replaced by the error message for the current value of errno.
+/*
+ * errstart --- begin an error-reporting cycle
  *
- * Note: no newline is needed at the end of the fmt string, since
- * elog will provide one for the output methods that need it.
+ * Create a stack entry and store the given parameters in it.  Subsequently,
+ * errmsg() and perhaps other routines will be called to further populate
+ * the stack entry.  Finally, errfinish() will be called to actually process
+ * the error report.
  *
- * If 'lev' is ERROR or worse, control does not return to the caller.
- * See elog.h for the error level definitions.
- *--------------------
+ * Returns TRUE in normal case.  Returns FALSE to short-circuit the error
+ * report (if it's a warning or lower and not to be reported anywhere).
  */
-void
-elog(int lev, const char *fmt,...)
+bool
+errstart(int elevel, const char *filename, int lineno,
+		 const char *funcname)
 {
-	va_list		ap;
-
-	/*
-	 * The expanded format and final output message are dynamically
-	 * allocated if necessary, but not if they fit in the "reasonable
-	 * size" buffers shown here.  In extremis, we'd rather depend on
-	 * having a few hundred bytes of stack space than on malloc() still
-	 * working (since memory-clobber errors often take out malloc first).
-	 * Don't make these buffers unreasonably large though, on pain of
-	 * having to chase a bug with no error message.
-	 *
-	 * Note that we use malloc() not palloc() because we want to retain
-	 * control if we run out of memory.  palloc() would recursively call
-	 * elog(ERROR), which would be all right except if we are working on a
-	 * FATAL or PANIC error.	We'd lose track of the fatal condition and
-	 * report a mere ERROR to outer loop, which would be a Bad Thing. So,
-	 * we substitute an appropriate message in-place, without downgrading
-	 * the level if it's above ERROR.
-	 */
-	char		fmt_fixedbuf[128];
-	char		msg_fixedbuf[256];
-	char	   *fmt_buf = fmt_fixedbuf;
-	char	   *msg_buf = msg_fixedbuf;
-	char		copylineno_buf[32];		/* for COPY line numbers */
-	const char *errorstr;
-	const char *prefix;
-	const char *cp;
-	char	   *bp;
-	size_t		space_needed;
-	size_t		timestamp_size; /* prefix len for timestamp+pid */
+	ErrorData  *edata;
 	bool		output_to_server = false;
 	bool		output_to_client = false;
 
-	/* Check for old elog calls.  Codes were renumbered in 7.3. 2002-02-24 */
-	if (lev < DEBUG5)
-		elog(FATAL, "Pre-7.3 object file made an elog() call.  Recompile.");
+	/*
+	 * First decide whether we need to process this report at all;
+	 * if it's warning or less and not enabled for logging, just
+	 * return FALSE without starting up any error logging machinery.
+	 */
 
 	/*
 	 * Convert initialization errors into fatal errors. This is probably
 	 * redundant, because Warn_restart_ready won't be set anyway.
 	 */
-	if (lev == ERROR && IsInitProcessingMode())
-		lev = FATAL;
+	if (elevel == ERROR && IsInitProcessingMode())
+		elevel = FATAL;
 
 	/*
 	 * If we are inside a critical section, all errors become PANIC
 	 * errors.	See miscadmin.h.
 	 */
-	if (lev >= ERROR)
+	if (elevel >= ERROR)
 	{
 		if (CritSectionCount > 0)
-			lev = PANIC;
+			elevel = PANIC;
 	}
 
 	/* Determine whether message is enabled for server log output */
 	/* Complicated because LOG is sorted out-of-order for this purpose */
-	if (lev == LOG || lev == COMMERROR)
+	if (elevel == LOG || elevel == COMMERROR)
 	{
 		if (log_min_messages == LOG)
 			output_to_server = true;
@@ -155,19 +199,19 @@ elog(int lev, const char *fmt,...)
 	}
 	else
 	{
-		/* lev != LOG */
+		/* elevel != LOG */
 		if (log_min_messages == LOG)
 		{
-			if (lev >= FATAL)
+			if (elevel >= FATAL)
 				output_to_server = true;
 		}
 		/* Neither is LOG */
-		else if (lev >= log_min_messages)
+		else if (elevel >= log_min_messages)
 			output_to_server = true;
 	}
 
 	/* Determine whether message is enabled for client output */
-	if (whereToSendOutput == Remote && lev != COMMERROR)
+	if (whereToSendOutput == Remote && elevel != COMMERROR)
 	{
 		/*
 		 * client_min_messages is honored only after we complete the
@@ -176,271 +220,173 @@ elog(int lev, const char *fmt,...)
 		 * during authentication.
 		 */
 		if (ClientAuthInProgress)
-			output_to_client = (lev >= ERROR);
+			output_to_client = (elevel >= ERROR);
 		else
-			output_to_client = (lev >= client_min_messages || lev == INFO);
+			output_to_client = (elevel >= client_min_messages ||
+								elevel == INFO);
 	}
 
-	/* Skip formatting effort if non-error message will not be output */
-	if (lev < ERROR && !output_to_server && !output_to_client)
-		return;
-
-	/* Save error str before calling any function that might change errno */
-	errorstr = useful_strerror(errno);
-
-	/* Internationalize the error format string */
-	fmt = gettext(fmt);
-
-	/* Begin formatting by determining prefix information */
-	prefix = elog_message_prefix(lev);
-
-	timestamp_size = 0;
-	if (Log_timestamp)
-		timestamp_size += TIMESTAMP_SIZE;
-	if (Log_pid)
-		timestamp_size += PID_SIZE;
+	/* Skip processing effort if non-error message will not be output */
+	if (elevel < ERROR && !output_to_server && !output_to_client)
+		return false;
 
 	/*
-	 * Set up the expanded format, consisting of the prefix string plus
-	 * input format, with any %m replaced by strerror() string (since
-	 * vsnprintf won't know what to do with %m).  To keep space
-	 * calculation simple, we only allow one %m.
+	 * Okay, crank up a stack entry to store the info in.
 	 */
-	space_needed = timestamp_size + strlen(prefix) +
-		strlen(fmt) + strlen(errorstr) + 1;
 
-	if (copy_lineno)
+	if (recursion_depth++ > 0)
 	{
 		/*
-		 * Prints the failure line of the COPY.  Wow, what a hack!	bjm
-		 * Translator:	Error message will be truncated at 31 characters.
+		 * Ooops, error during error processing.  Clear ErrorContext and force
+		 * level up to ERROR or greater, as discussed at top of file.  Adjust
+		 * output decisions too.
+		 */
+		MemoryContextReset(ErrorContext);
+		output_to_server = true;
+		if (whereToSendOutput == Remote && elevel != COMMERROR)
+			output_to_client = true;
+		elevel = Max(elevel, ERROR);
+		/*
+		 * If we recurse more than once, the problem might be something
+		 * broken in a context traceback routine.  Abandon them too.
 		 */
-		snprintf(copylineno_buf, sizeof(copylineno_buf),
-				 gettext("copy: line %d, "), copy_lineno);
-		space_needed += strlen(copylineno_buf);
+		if (recursion_depth > 2)
+			error_context_stack = NULL;
 	}
-
-	if (space_needed > sizeof(fmt_fixedbuf))
+	if (++errordata_stack_depth >= ERRORDATA_STACK_SIZE)
 	{
-		fmt_buf = malloc(space_needed);
-		if (fmt_buf == NULL)
-		{
-			/* We're up against it, convert to out-of-memory error */
-			fmt_buf = fmt_fixedbuf;
-			if (lev < ERROR)
-			{
-				lev = ERROR;
-				prefix = elog_message_prefix(lev);
-			}
+		/* Wups, stack not big enough */
+		int		i;
 
-			/*
-			 * gettext doesn't allocate memory, except in the very first
-			 * call (which this isn't), so it's safe to translate here.
-			 * Worst case we get the untranslated string back.
-			 */
-			/* translator: This must fit in fmt_fixedbuf. */
-			fmt = gettext("elog: out of memory");
-		}
+		elevel = Max(elevel, ERROR);
+		/*
+		 * Don't forget any FATAL/PANIC status on the stack (see comments
+		 * in errfinish)
+		 */
+		for (i = 0; i < errordata_stack_depth; i++)
+			elevel = Max(elevel, errordata[i].elevel);
+		/* Clear the stack and try again */
+		errordata_stack_depth = -1;
+		ereport(elevel, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));
 	}
 
-	fmt_buf[0] = '\0';
+	/* Initialize data for this error frame */
+	edata = &errordata[errordata_stack_depth];
+	MemSet(edata, 0, sizeof(ErrorData));
+	edata->elevel = elevel;
+	edata->output_to_server = output_to_server;
+	edata->output_to_client = output_to_client;
+	edata->filename = filename;
+	edata->lineno = lineno;
+	edata->funcname = funcname;
+	edata->sqlerrcode = ERRCODE_INTERNAL_ERROR; /* default errcode */
+	/* errno is saved here so that error parameter eval can't change it */
+	edata->saved_errno = errno;
+
+	recursion_depth--;
+	return true;
+}
 
-	if (Log_timestamp)
-		strcat(fmt_buf, print_timestamp());
-	if (Log_pid)
-		strcat(fmt_buf, print_pid());
+/*
+ * errfinish --- end an error-reporting cycle
+ *
+ * Produce the appropriate error report(s) and pop the error stack.
+ *
+ * If elevel is ERROR or worse, control does not return to the caller.
+ * See elog.h for the error level definitions.
+ */
+void
+errfinish(int dummy, ...)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+	int			elevel = edata->elevel;
+	MemoryContext oldcontext;
+	ErrorContextCallback *econtext;
 
-	strcat(fmt_buf, prefix);
+	CHECK_STACK_DEPTH();
 
-	/* If error was in CopyFrom() print the offending line number -- dz */
-	if (copy_lineno)
+	/*
+	 * Call any context callback functions.  We can treat ereports occuring
+	 * in callback functions as re-entrant rather than recursive case, so
+	 * don't increment recursion_depth yet.
+	 */
+	for (econtext = error_context_stack;
+		 econtext != NULL;
+		 econtext = econtext->previous)
 	{
-		strcat(fmt_buf, copylineno_buf);
-		if (lev >= ERROR)
-			copy_lineno = 0;
+		(*econtext->callback) (econtext->arg);
 	}
 
-	bp = fmt_buf + strlen(fmt_buf);
-
-	for (cp = fmt; *cp; cp++)
-	{
-		if (cp[0] == '%' && cp[1] != '\0')
-		{
-			if (cp[1] == 'm')
-			{
-				/*
-				 * XXX If there are any %'s in errorstr then vsnprintf
-				 * will do the Wrong Thing; do we need to cope? Seems
-				 * unlikely that % would appear in system errors.
-				 */
-				strcpy(bp, errorstr);
-
-				/*
-				 * copy the rest of fmt literally, since we can't afford
-				 * to insert another %m.
-				 */
-				strcat(bp, cp + 2);
-				bp += strlen(bp);
-				break;
-			}
-			else
-			{
-				/* copy % and next char --- this avoids trouble with %%m */
-				*bp++ = *cp++;
-				*bp++ = *cp;
-			}
-		}
-		else
-			*bp++ = *cp;
-	}
-	*bp = '\0';
+	/* Now we are ready to process the error. */
+	recursion_depth++;
 
 	/*
-	 * Now generate the actual output text using vsnprintf(). Be sure to
-	 * leave space for \n added later as well as trailing null.
+	 * Do processing in ErrorContext, which we hope has enough reserved space
+	 * to report an error.
 	 */
-	space_needed = sizeof(msg_fixedbuf);
-	for (;;)
-	{
-		int			nprinted;
-
-		va_start(ap, fmt);
-		nprinted = vsnprintf(msg_buf, space_needed - 2, fmt_buf, ap);
-		va_end(ap);
-
-		/*
-		 * Note: some versions of vsnprintf return the number of chars
-		 * actually stored, but at least one returns -1 on failure. Be
-		 * conservative about believing whether the print worked.
-		 */
-		if (nprinted >= 0 && nprinted < space_needed - 3)
-			break;
-		/* It didn't work, try to get a bigger buffer */
-		if (msg_buf != msg_fixedbuf)
-			free(msg_buf);
-		space_needed *= 2;
-		msg_buf = malloc(space_needed);
-		if (msg_buf == NULL)
-		{
-			/* We're up against it, convert to out-of-memory error */
-			msg_buf = msg_fixedbuf;
-			if (lev < ERROR)
-			{
-				lev = ERROR;
-				prefix = elog_message_prefix(lev);
-			}
-			msg_buf[0] = '\0';
-			if (Log_timestamp)
-				strcat(msg_buf, print_timestamp());
-			if (Log_pid)
-				strcat(msg_buf, print_pid());
-			strcat(msg_buf, prefix);
-			strcat(msg_buf, gettext("elog: out of memory"));
-			break;
-		}
-	}
+	oldcontext = MemoryContextSwitchTo(ErrorContext);
 
+	/* Send to server log, if enabled */
+	if (edata->output_to_server)
+		send_message_to_server_log(edata);
 
 	/*
-	 * Message prepared; send it where it should go
+	 * Abort any old-style COPY OUT in progress when an error is detected.
+	 * This hack is necessary because of poor design of old-style copy
+	 * protocol.  Note we must do this even if client is fool enough to
+	 * have set client_min_messages above ERROR, so don't look at
+	 * output_to_client.
 	 */
+	if (elevel >= ERROR && whereToSendOutput == Remote)
+		pq_endcopyout(true);
 
-#ifdef HAVE_SYSLOG
-	/* Write to syslog, if enabled */
-	if (output_to_server && Use_syslog >= 1)
-	{
-		int			syslog_level;
-
-		switch (lev)
-		{
-			case DEBUG5:
-			case DEBUG4:
-			case DEBUG3:
-			case DEBUG2:
-			case DEBUG1:
-				syslog_level = LOG_DEBUG;
-				break;
-			case LOG:
-			case COMMERROR:
-			case INFO:
-				syslog_level = LOG_INFO;
-				break;
-			case NOTICE:
-			case WARNING:
-				syslog_level = LOG_NOTICE;
-				break;
-			case ERROR:
-				syslog_level = LOG_WARNING;
-				break;
-			case FATAL:
-				syslog_level = LOG_ERR;
-				break;
-			case PANIC:
-			default:
-				syslog_level = LOG_CRIT;
-				break;
-		}
+	/* Send to client, if enabled */
+	if (edata->output_to_client)
+		send_message_to_frontend(edata);
 
-		write_syslog(syslog_level, msg_buf + timestamp_size);
-	}
-#endif   /* HAVE_SYSLOG */
+	/* Now free up subsidiary data attached to stack entry, and release it */
+	if (edata->message)
+		pfree(edata->message);
+	if (edata->detail)
+		pfree(edata->detail);
+	if (edata->hint)
+		pfree(edata->hint);
+	if (edata->context)
+		pfree(edata->context);
 
-	/* syslog doesn't want a trailing newline, but other destinations do */
-	strcat(msg_buf, "\n");
+	MemoryContextSwitchTo(oldcontext);
 
-	/* Write to stderr, if enabled */
-	if (output_to_server && (Use_syslog <= 1 || whereToSendOutput == Debug))
-		write(2, msg_buf, strlen(msg_buf));
+	errordata_stack_depth--;
+	recursion_depth--;
 
-	/* Send to client, if enabled */
-	if (output_to_client)
+	/*
+	 * If the error level is ERROR or more, we are not going to return to
+	 * caller; therefore, if there is any stacked error already in progress
+	 * it will be lost.  This is more or less okay, except we do not want
+	 * to have a FATAL or PANIC error downgraded because the reporting process
+	 * was interrupted by a lower-grade error.  So check the stack and make
+	 * sure we panic if panic is warranted.
+	 */
+	if (elevel >= ERROR)
 	{
-		/* Send IPC message to the front-end program */
-		MemoryContext oldcxt;
+		int		i;
+
+		for (i = 0; i <= errordata_stack_depth; i++)
+			elevel = Max(elevel, errordata[i].elevel);
 
 		/*
-		 * Since backend libpq may call palloc(), switch to a context
-		 * where there's fairly likely to be some free space.  After all
-		 * the pushups above, we don't want to drop the ball by running
-		 * out of space now...
+		 * Also, be sure to reset the stack to empty.  We do not clear
+		 * ErrorContext here, though; PostgresMain does that later on.
 		 */
-		oldcxt = MemoryContextSwitchTo(ErrorContext);
-
-		if (lev >= ERROR)
-		{
-			/*
-			 * Abort any COPY OUT in progress when an error is detected.
-			 * This hack is necessary because of poor design of old-style
-			 * copy protocol.
-			 */
-			pq_endcopyout(true);
-		}
-
-		/* Exclude the timestamp from msg sent to frontend */
-		send_message_to_frontend(lev, msg_buf + timestamp_size);
-
-		MemoryContextSwitchTo(oldcxt);
+		errordata_stack_depth = -1;
+		recursion_depth = 0;
+		error_context_stack = NULL;
 	}
 
-	/* done with the message, release space */
-	if (fmt_buf != fmt_fixedbuf)
-		free(fmt_buf);
-	if (msg_buf != msg_fixedbuf)
-		free(msg_buf);
-
-	/*
-	 * If the user wants this elog() generating query logged, do so. We
-	 * only want to log if the query has been written to
-	 * debug_query_string. Also, avoid infinite loops.
-	 */
-
-	if (lev != LOG && lev >= log_min_error_statement && debug_query_string)
-		elog(LOG, "statement: %s", debug_query_string);
-
 	/*
-	 * Perform error recovery action as specified by lev.
+	 * Perform error recovery action as specified by elevel.
 	 */
-	if (lev == ERROR || lev == FATAL)
+	if (elevel == ERROR || elevel == FATAL)
 	{
 		/* Prevent immediate interrupt while entering error recovery */
 		ImmediateInterruptOK = false;
@@ -467,7 +413,7 @@ elog(int lev, const char *fmt,...)
 		 * postmaster (for example, if we are being run from the initdb
 		 * script, we'd better return an error status).
 		 */
-		if (lev == FATAL || !Warn_restart_ready || proc_exit_inprogress)
+		if (elevel == FATAL || !Warn_restart_ready || proc_exit_inprogress)
 		{
 			/*
 			 * fflush here is just to improve the odds that we get to see
@@ -482,10 +428,10 @@ elog(int lev, const char *fmt,...)
 		}
 
 		/*
-		 * Guard against infinite loop from elog() during error recovery.
+		 * Guard against infinite loop from errors during error recovery.
 		 */
 		if (InError)
-			elog(PANIC, "elog: error during error recovery, giving up!");
+			ereport(PANIC, (errmsg("error during error recovery, giving up")));
 		InError = true;
 
 		/*
@@ -494,7 +440,7 @@ elog(int lev, const char *fmt,...)
 		siglongjmp(Warn_restart, 1);
 	}
 
-	if (lev == PANIC)
+	if (elevel >= PANIC)
 	{
 		/*
 		 * Serious crash time. Postmaster will observe nonzero process
@@ -509,18 +455,281 @@ elog(int lev, const char *fmt,...)
 		abort();
 	}
 
-	/* We reach here if lev <= WARNING. OK to return to caller. */
+	/* We reach here if elevel <= WARNING. OK to return to caller. */
+}
+
+
+/*
+ * errcode --- add SQLSTATE error code to the current error
+ *
+ * The code is expected to be represented as per MAKE_SQLSTATE().
+ */
+int
+errcode(int sqlerrcode)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+
+	/* we don't bother incrementing recursion_depth */
+	CHECK_STACK_DEPTH();
+
+	edata->sqlerrcode = sqlerrcode;
+
+	return 0;					/* return value does not matter */
+}
+
+
+/*
+ * This macro handles expansion of a format string and associated parameters;
+ * it's common code for errmsg(), errdetail(), etc.  Must be called inside
+ * a routine that is declared like "const char *fmt, ..." and has an edata
+ * pointer set up.  The message is assigned to edata->targetfield, or
+ * appended to it if appendval is true.
+ *
+ * Note: we pstrdup the buffer rather than just transferring its storage
+ * to the edata field because the buffer might be considerably larger than
+ * really necessary.
+ */
+#define EVALUATE_MESSAGE(targetfield, appendval)  \
+	{ \
+		char		   *fmtbuf; \
+		StringInfoData	buf; \
+		/* Internationalize the error format string */ \
+		fmt = gettext(fmt); \
+		/* Expand %m in format string */ \
+		fmtbuf = expand_fmt_string(fmt, edata); \
+		initStringInfo(&buf); \
+		if ((appendval) && edata->targetfield) \
+			appendStringInfo(&buf, "%s\n", edata->targetfield); \
+		/* Generate actual output --- have to use appendStringInfoVA */ \
+		for (;;) \
+		{ \
+			va_list		args; \
+			bool		success; \
+			va_start(args, fmt); \
+			success = appendStringInfoVA(&buf, fmtbuf, args); \
+			va_end(args); \
+			if (success) \
+				break; \
+			enlargeStringInfo(&buf, buf.maxlen); \
+		} \
+		/* Done with expanded fmt */ \
+		pfree(fmtbuf); \
+		/* Save the completed message into the stack item */ \
+		if (edata->targetfield) \
+			pfree(edata->targetfield); \
+		edata->targetfield = pstrdup(buf.data); \
+		pfree(buf.data); \
+	}
+
+
+/*
+ * errmsg --- add a primary error message text to the current error
+ *
+ * In addition to the usual %-escapes recognized by printf, "%m" in
+ * fmt is replaced by the error message for the caller's value of errno.
+ *
+ * Note: no newline is needed at the end of the fmt string, since
+ * ereport will provide one for the output methods that need it.
+ */
+int
+errmsg(const char *fmt, ...)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+	MemoryContext oldcontext;
+
+	recursion_depth++;
+	CHECK_STACK_DEPTH();
+	oldcontext = MemoryContextSwitchTo(ErrorContext);
+
+	EVALUATE_MESSAGE(message, false);
+
+	MemoryContextSwitchTo(oldcontext);
+	recursion_depth--;
+	return 0;					/* return value does not matter */
+}
+
+
+/*
+ * errmsg_internal --- add a primary error message text to the current error
+ *
+ * This is exactly like errmsg() except that strings passed to errmsg_internal
+ * are customarily left out of the internationalization message dictionary.
+ * This should be used for "can't happen" cases that are probably not worth
+ * spending translation effort on.
+ */
+int
+errmsg_internal(const char *fmt, ...)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+	MemoryContext oldcontext;
+
+	recursion_depth++;
+	CHECK_STACK_DEPTH();
+	oldcontext = MemoryContextSwitchTo(ErrorContext);
+
+	EVALUATE_MESSAGE(message, false);
+
+	MemoryContextSwitchTo(oldcontext);
+	recursion_depth--;
+	return 0;					/* return value does not matter */
+}
+
+
+/*
+ * errdetail --- add a detail error message text to the current error
+ */
+int
+errdetail(const char *fmt, ...)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+	MemoryContext oldcontext;
+
+	recursion_depth++;
+	CHECK_STACK_DEPTH();
+	oldcontext = MemoryContextSwitchTo(ErrorContext);
+
+	EVALUATE_MESSAGE(detail, false);
+
+	MemoryContextSwitchTo(oldcontext);
+	recursion_depth--;
+	return 0;					/* return value does not matter */
 }
 
 
+/*
+ * errhint --- add a hint error message text to the current error
+ */
 int
+errhint(const char *fmt, ...)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+	MemoryContext oldcontext;
+
+	recursion_depth++;
+	CHECK_STACK_DEPTH();
+	oldcontext = MemoryContextSwitchTo(ErrorContext);
+
+	EVALUATE_MESSAGE(hint, false);
+
+	MemoryContextSwitchTo(oldcontext);
+	recursion_depth--;
+	return 0;					/* return value does not matter */
+}
+
+
+/*
+ * errcontext --- add a context error message text to the current error
+ *
+ * Unlike other cases, multiple calls are allowed to build up a stack of
+ * context information.  We assume earlier calls represent more-closely-nested
+ * states.
+ */
+int
+errcontext(const char *fmt, ...)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+	MemoryContext oldcontext;
+
+	recursion_depth++;
+	CHECK_STACK_DEPTH();
+	oldcontext = MemoryContextSwitchTo(ErrorContext);
+
+	EVALUATE_MESSAGE(context, true);
+
+	MemoryContextSwitchTo(oldcontext);
+	recursion_depth--;
+	return 0;					/* return value does not matter */
+}
+
+
+/*
+ * errfunction --- add reporting function name to the current error
+ *
+ * This is used when backwards compatibility demands that the function
+ * name appear in messages sent to old-protocol clients.  Note that the
+ * passed string is expected to be a non-freeable constant string.
+ */
+int
+errfunction(const char *funcname)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+
+	/* we don't bother incrementing recursion_depth */
+	CHECK_STACK_DEPTH();
+
+	edata->funcname = funcname;
+	edata->show_funcname = true;
+
+	return 0;					/* return value does not matter */
+}
+
+/*
+ * errposition --- add cursor position to the current error
+ */
+int
+errposition(int cursorpos)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+
+	/* we don't bother incrementing recursion_depth */
+	CHECK_STACK_DEPTH();
+
+	edata->cursorpos = cursorpos;
+
+	return 0;					/* return value does not matter */
+}
+
+
+/*
+ * elog_finish --- finish up for old-style API
+ *
+ * The elog() macro already called errstart, but with ERROR rather than
+ * the true elevel.
+ */
+void
+elog_finish(int elevel, const char *fmt, ...)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+	MemoryContext oldcontext;
+
+	CHECK_STACK_DEPTH();
+
+	/*
+	 * We need to redo errstart() because the elog macro had to call it
+	 * with bogus elevel.
+	 */
+	errordata_stack_depth--;
+	errno = edata->saved_errno;
+	if (!errstart(elevel, edata->filename, edata->lineno, edata->funcname))
+		return;					/* nothing to do */
+
+	/*
+	 * Format error message just like errmsg().
+	 */
+	recursion_depth++;
+	oldcontext = MemoryContextSwitchTo(ErrorContext);
+
+	EVALUATE_MESSAGE(message, false);
+
+	MemoryContextSwitchTo(oldcontext);
+	recursion_depth--;
+
+	/*
+	 * And let errfinish() finish up.
+	 */
+	errfinish(0);
+}
+
+
+/*
+ * Initialization of error output file
+ */
+void
 DebugFileOpen(void)
 {
 	int			fd,
 				istty;
 
-	Debugfile = -1;
-
 	if (OutputFileName[0])
 	{
 		/*
@@ -530,8 +739,8 @@ DebugFileOpen(void)
 		 */
 		if ((fd = open(OutputFileName, O_CREAT | O_APPEND | O_WRONLY,
 					   0666)) < 0)
-			elog(FATAL, "DebugFileOpen: open of %s: %m",
-				 OutputFileName);
+			ereport(FATAL,
+					(errmsg("failed to open %s: %m", OutputFileName)));
 		istty = isatty(fd);
 		close(fd);
 
@@ -539,9 +748,9 @@ DebugFileOpen(void)
 		 * Redirect our stderr to the debug output file.
 		 */
 		if (!freopen(OutputFileName, "a", stderr))
-			elog(FATAL, "DebugFileOpen: %s reopen as stderr: %m",
-				 OutputFileName);
-		Debugfile = fileno(stderr);
+			ereport(FATAL,
+					(errmsg("failed to reopen %s as stderr: %m",
+							OutputFileName)));
 
 		/*
 		 * If the file is a tty and we're running under the postmaster,
@@ -551,66 +760,10 @@ DebugFileOpen(void)
 		 */
 		if (istty && IsUnderPostmaster)
 			if (!freopen(OutputFileName, "a", stdout))
-				elog(FATAL, "DebugFileOpen: %s reopen as stdout: %m",
-					 OutputFileName);
-		return Debugfile;
+				ereport(FATAL,
+						(errmsg("failed to reopen %s as stdout: %m",
+								OutputFileName)));
 	}
-
-	/*
-	 * If no filename was specified, send debugging output to stderr. If
-	 * stderr has been hosed, try to open a file.
-	 */
-	fd = fileno(stderr);
-	if (fcntl(fd, F_GETFD, 0) < 0)
-	{
-		snprintf(OutputFileName, MAXPGPATH, "%s/pg.errors.%d",
-				 DataDir, (int) MyProcPid);
-		fd = open(OutputFileName, O_CREAT | O_APPEND | O_WRONLY, 0666);
-	}
-	if (fd < 0)
-		elog(FATAL, "DebugFileOpen: could not open debugging file");
-
-	Debugfile = fd;
-	return Debugfile;
-}
-
-
-/*
- * Return a timestamp string like
- *
- *	 "2000-06-04 13:12:03 "
- */
-static const char *
-print_timestamp(void)
-{
-	time_t		curtime;
-	static char buf[TIMESTAMP_SIZE + 1];
-
-	curtime = time(NULL);
-
-	strftime(buf, sizeof(buf),
-			 "%Y-%m-%d %H:%M:%S ",
-			 localtime(&curtime));
-
-	return buf;
-}
-
-
-
-/*
- * Return a string like
- *
- *	   "[123456] "
- *
- * with the current pid.
- */
-static const char *
-print_pid(void)
-{
-	static char buf[PID_SIZE + 1];
-
-	snprintf(buf, PID_SIZE + 1, "[%d]      ", (int) MyProcPid);
-	return buf;
 }
 
 
@@ -679,7 +832,6 @@ write_syslog(int level, const char *line)
 		{
 			char		buf[PG_SYSLOG_LIMIT + 1];
 			int			buflen;
-			int			l;
 			int			i;
 
 			/* if we start at a newline, move ahead one char */
@@ -695,28 +847,24 @@ write_syslog(int level, const char *line)
 			if (strchr(buf, '\n') != NULL)
 				*strchr(buf, '\n') = '\0';
 
-			l = strlen(buf);
+			buflen = strlen(buf);
 
 			/* trim to multibyte letter boundary */
-			buflen = pg_mbcliplen(buf, l, l);
+			buflen = pg_mbcliplen(buf, buflen, buflen);
 			if (buflen <= 0)
 				return;
 			buf[buflen] = '\0';
-			l = strlen(buf);
 
 			/* already word boundary? */
-			if (isspace((unsigned char) line[l]) || line[l] == '\0')
-				buflen = l;
-			else
+			if (!isspace((unsigned char) line[buflen]) &&
+				line[buflen] != '\0')
 			{
 				/* try to divide at word boundary */
-				i = l - 1;
+				i = buflen - 1;
 				while (i > 0 && !isspace((unsigned char) buf[i]))
 					i--;
 
-				if (i <= 0)		/* couldn't divide word boundary */
-					buflen = l;
-				else
+				if (i > 0)		/* else couldn't divide word boundary */
 				{
 					buflen = i;
 					buf[i] = '\0';
@@ -736,19 +884,222 @@ write_syslog(int level, const char *line)
 		syslog(level, "[%lu] %s", seq, line);
 	}
 }
+
+#endif   /* HAVE_SYSLOG */
+
+
+/*
+ * Write error report to server's log
+ */
+static void
+send_message_to_server_log(ErrorData *edata)
+{
+	StringInfoData	buf;
+
+	initStringInfo(&buf);
+
+	appendStringInfo(&buf, "%s:  ", error_severity(edata->elevel));
+
+	if (edata->message)
+		appendStringInfo(&buf, "%s\n", edata->message);
+	else
+		appendStringInfoString(&buf, "missing error text\n");
+
+	/* XXX showing of additional info should perhaps be optional */
+	/* XXX ought to localize the label strings, probably */
+
+	if (edata->detail)
+		appendStringInfo(&buf, "DETAIL:  %s\n", edata->detail);
+	if (edata->hint)
+		appendStringInfo(&buf, "HINT:  %s\n", edata->hint);
+	if (edata->context)
+		appendStringInfo(&buf, "CONTEXT:  %s\n", edata->context);
+	if (edata->funcname && edata->filename)
+		appendStringInfo(&buf, "IN:  %s (%s:%d)\n",
+						 edata->funcname, edata->filename, edata->lineno);
+
+	/*
+	 * If the user wants the query that generated this error logged, do so.
+	 * We use debug_query_string to get at the query, which is kinda useless
+	 * for queries triggered by extended query protocol; how to improve?
+	 */
+	if (edata->elevel >= log_min_error_statement && debug_query_string != NULL)
+		appendStringInfo(&buf, "STATEMENT:  %s\n", debug_query_string);
+
+
+#ifdef HAVE_SYSLOG
+	/* Write to syslog, if enabled */
+	if (Use_syslog >= 1)
+	{
+		int			syslog_level;
+
+		switch (edata->elevel)
+		{
+			case DEBUG5:
+			case DEBUG4:
+			case DEBUG3:
+			case DEBUG2:
+			case DEBUG1:
+				syslog_level = LOG_DEBUG;
+				break;
+			case LOG:
+			case COMMERROR:
+			case INFO:
+				syslog_level = LOG_INFO;
+				break;
+			case NOTICE:
+			case WARNING:
+				syslog_level = LOG_NOTICE;
+				break;
+			case ERROR:
+				syslog_level = LOG_WARNING;
+				break;
+			case FATAL:
+				syslog_level = LOG_ERR;
+				break;
+			case PANIC:
+			default:
+				syslog_level = LOG_CRIT;
+				break;
+		}
+
+		write_syslog(syslog_level, buf.data);
+	}
 #endif   /* HAVE_SYSLOG */
 
+	/* Write to stderr, if enabled */
+	if (Use_syslog <= 1 || whereToSendOutput == Debug)
+	{
+		/*
+		 * Timestamp and PID are only used for stderr output --- we assume
+		 * the syslog daemon will supply them for us in the other case.
+		 */
+		if (Log_timestamp)
+			fprintf(stderr, "%s", print_timestamp());
+		if (Log_pid)
+			fprintf(stderr, "%s", print_pid());
+		fprintf(stderr, "%s", buf.data);
+	}
+
+	pfree(buf.data);
+}
 
+
+/*
+ * Write error report to client
+ */
 static void
-send_message_to_frontend(int type, const char *msg)
+send_message_to_frontend(ErrorData *edata)
 {
-	StringInfoData buf;
+	StringInfoData msgbuf;
 
 	/* 'N' (Notice) is for nonfatal conditions, 'E' is for errors */
-	pq_beginmessage(&buf, (type < ERROR) ? 'N' : 'E');
-	/* XXX more to do here */
-	pq_sendstring(&buf, msg);
-	pq_endmessage(&buf);
+	pq_beginmessage(&msgbuf, (edata->elevel < ERROR) ? 'N' : 'E');
+
+	if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
+	{
+		/* New style with separate fields */
+		char	tbuf[12];
+		int		ssval;
+		int		i;
+
+		pq_sendbyte(&msgbuf, 'S');
+		pq_sendstring(&msgbuf, error_severity(edata->elevel));
+
+		/* unpack MAKE_SQLSTATE code */
+		ssval = edata->sqlerrcode;
+		for (i = 0; i < 5; i++)
+		{
+			tbuf[i] = PGUNSIXBIT(ssval);
+			ssval >>= 6;
+		}
+		tbuf[i] = '\0';
+
+		pq_sendbyte(&msgbuf, 'C');
+		pq_sendstring(&msgbuf, tbuf);
+
+		/* M field is required per protocol, so always send something */
+		pq_sendbyte(&msgbuf, 'M');
+		if (edata->message)
+			pq_sendstring(&msgbuf, edata->message);
+		else
+			pq_sendstring(&msgbuf, gettext("missing error text"));
+
+		if (edata->detail)
+		{
+			pq_sendbyte(&msgbuf, 'D');
+			pq_sendstring(&msgbuf, edata->detail);
+		}
+
+		if (edata->hint)
+		{
+			pq_sendbyte(&msgbuf, 'H');
+			pq_sendstring(&msgbuf, edata->hint);
+		}
+
+		if (edata->context)
+		{
+			pq_sendbyte(&msgbuf, 'W');
+			pq_sendstring(&msgbuf, edata->context);
+		}
+
+		if (edata->cursorpos > 0)
+		{
+			snprintf(tbuf, sizeof(tbuf), "%d", edata->cursorpos);
+			pq_sendbyte(&msgbuf, 'P');
+			pq_sendstring(&msgbuf, tbuf);
+		}
+
+		if (edata->filename)
+		{
+			pq_sendbyte(&msgbuf, 'F');
+			pq_sendstring(&msgbuf, edata->filename);
+		}
+
+		if (edata->lineno > 0)
+		{
+			snprintf(tbuf, sizeof(tbuf), "%d", edata->lineno);
+			pq_sendbyte(&msgbuf, 'L');
+			pq_sendstring(&msgbuf, tbuf);
+		}
+
+		if (edata->funcname)
+		{
+			pq_sendbyte(&msgbuf, 'R');
+			pq_sendstring(&msgbuf, edata->funcname);
+		}
+
+		pq_sendbyte(&msgbuf, '\0');	/* terminator */
+	}
+	else
+	{
+		/* Old style --- gin up a backwards-compatible message */
+		StringInfoData buf;
+
+		initStringInfo(&buf);
+
+		appendStringInfo(&buf, "%s:  ", error_severity(edata->elevel));
+
+		if (edata->show_funcname && edata->funcname)
+			appendStringInfo(&buf, "%s: ", edata->funcname);
+
+		if (edata->message)
+			appendStringInfo(&buf, "%s", edata->message);
+		else
+			appendStringInfoString(&buf, gettext("missing error text"));
+
+		if (edata->cursorpos > 0)
+			appendStringInfo(&buf, gettext(" at character %d"),
+							 edata->cursorpos);
+
+		appendStringInfoChar(&buf, '\n');
+
+		pq_sendstring(&msgbuf, buf.data);
+
+		pfree(buf.data);
+	}
+
+	pq_endmessage(&msgbuf);
 
 	/*
 	 * This flush is normally not necessary, since postgres.c will flush
@@ -762,12 +1113,73 @@ send_message_to_frontend(int type, const char *msg)
 }
 
 
+/*
+ * Support routines for formatting error messages.
+ */
+
+
+/*
+ * expand_fmt_string --- process special format codes in a format string
+ *
+ * We must replace %m with the appropriate strerror string, since vsnprintf
+ * won't know what to do with it.
+ *
+ * The result is a palloc'd string.
+ */
+static char *
+expand_fmt_string(const char *fmt, ErrorData *edata)
+{
+	StringInfoData	buf;
+	const char *cp;
+
+	initStringInfo(&buf);
+
+	for (cp = fmt; *cp; cp++)
+	{
+		if (cp[0] == '%' && cp[1] != '\0')
+		{
+			cp++;
+			if (*cp == 'm')
+			{
+				/*
+				 * Replace %m by system error string.  If there are any %'s
+				 * in the string, we'd better double them so that vsnprintf
+				 * won't misinterpret.
+				 */
+				const char *cp2;
+
+				cp2 = useful_strerror(edata->saved_errno);
+				for (; *cp2; cp2++)
+				{
+					if (*cp2 == '%')
+						appendStringInfoCharMacro(&buf, '%');
+					appendStringInfoCharMacro(&buf, *cp2);
+				}
+			}
+			else
+			{
+				/* copy % and next char --- this avoids trouble with %%m */
+				appendStringInfoCharMacro(&buf, '%');
+				appendStringInfoCharMacro(&buf, *cp);
+			}
+		}
+		else
+			appendStringInfoCharMacro(&buf, *cp);
+	}
+
+	return buf.data;
+}
+
+
+/*
+ * A slightly cleaned-up version of strerror()
+ */
 static const char *
 useful_strerror(int errnum)
 {
 	/* this buffer is only used if errno has a bogus value */
 	static char errorstr_buf[48];
-	char	   *str;
+	const char   *str;
 
 	if (errnum == ERANGE)
 		/* small trick to save creating many regression test result files */
@@ -794,45 +1206,87 @@ useful_strerror(int errnum)
 }
 
 
-
+/*
+ * error_severity --- get localized string representing elevel
+ */
 static const char *
-elog_message_prefix(int lev)
+error_severity(int elevel)
 {
-	const char *prefix = NULL;
+	const char *prefix;
 
-	switch (lev)
+	switch (elevel)
 	{
 		case DEBUG1:
 		case DEBUG2:
 		case DEBUG3:
 		case DEBUG4:
 		case DEBUG5:
-			prefix = gettext("DEBUG:  ");
+			prefix = gettext("DEBUG");
 			break;
 		case LOG:
 		case COMMERROR:
-			prefix = gettext("LOG:  ");
+			prefix = gettext("LOG");
 			break;
 		case INFO:
-			prefix = gettext("INFO:  ");
+			prefix = gettext("INFO");
 			break;
 		case NOTICE:
-			prefix = gettext("NOTICE:  ");
+			prefix = gettext("NOTICE");
 			break;
 		case WARNING:
-			prefix = gettext("WARNING:  ");
+			prefix = gettext("WARNING");
 			break;
 		case ERROR:
-			prefix = gettext("ERROR:  ");
+			prefix = gettext("ERROR");
 			break;
 		case FATAL:
-			prefix = gettext("FATAL:  ");
+			prefix = gettext("FATAL");
 			break;
 		case PANIC:
-			prefix = gettext("PANIC:  ");
+			prefix = gettext("PANIC");
+			break;
+		default:
+			prefix = "???";
 			break;
 	}
 
-	Assert(prefix != NULL);
 	return prefix;
 }
+
+
+/*
+ * Return a timestamp string like
+ *
+ *	 "2000-06-04 13:12:03 "
+ */
+static const char *
+print_timestamp(void)
+{
+	time_t		curtime;
+	static char buf[21];		/* format `YYYY-MM-DD HH:MM:SS ' */
+
+	curtime = time(NULL);
+
+	strftime(buf, sizeof(buf),
+			 "%Y-%m-%d %H:%M:%S ",
+			 localtime(&curtime));
+
+	return buf;
+}
+
+
+/*
+ * Return a string like
+ *
+ *	   "[123456] "
+ *
+ * with the current pid.
+ */
+static const char *
+print_pid(void)
+{
+	static char buf[10];		/* allow `[123456] ' */
+
+	snprintf(buf, sizeof(buf), "[%d] ", (int) MyProcPid);
+	return buf;
+}
diff --git a/src/include/commands/copy.h b/src/include/commands/copy.h
index dcfce2f804feac73e090d9bbea5e98dd5b3659ad..4eab57362acdc7e9b9c70f97c3465258aa9cfe9f 100644
--- a/src/include/commands/copy.h
+++ b/src/include/commands/copy.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: copy.h,v 1.20 2002/09/04 20:31:42 momjian Exp $
+ * $Id: copy.h,v 1.21 2003/04/24 21:16:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,10 +15,8 @@
 #define COPY_H
 
 #include "nodes/parsenodes.h"
-#include "nodes/primnodes.h"
 
-extern int	copy_lineno;
 
-void		DoCopy(const CopyStmt *stmt);
+extern void DoCopy(const CopyStmt *stmt);
 
 #endif   /* COPY_H */
diff --git a/src/include/lib/stringinfo.h b/src/include/lib/stringinfo.h
index c49a73c0eb18cb7a19683cd7b764ee080a136baf..8e305ee430c373321955122e46bd1845fbbd0fd4 100644
--- a/src/include/lib/stringinfo.h
+++ b/src/include/lib/stringinfo.h
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: stringinfo.h,v 1.25 2003/04/19 00:02:29 tgl Exp $
+ * $Id: stringinfo.h,v 1.26 2003/04/24 21:16:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -80,16 +80,32 @@ extern void initStringInfo(StringInfo str);
 
 /*------------------------
  * appendStringInfo
- * Format text data under the control of fmt (an sprintf-like format string)
+ * Format text data under the control of fmt (an sprintf-style format string)
  * and append it to whatever is already in str.  More space is allocated
  * to str if necessary.  This is sort of like a combination of sprintf and
  * strcat.
  */
-extern void
-appendStringInfo(StringInfo str, const char *fmt,...)
+extern void appendStringInfo(StringInfo str, const char *fmt, ...)
 /* This extension allows gcc to check the format string */
 __attribute__((format(printf, 2, 3)));
 
+/*------------------------
+ * appendStringInfoVA
+ * Attempt to format text data under the control of fmt (an sprintf-style
+ * format string) and append it to whatever is already in str.  If successful
+ * return true; if not (because there's not enough space), return false
+ * without modifying str.  Typically the caller would enlarge str and retry
+ * on false return --- see appendStringInfo for standard usage pattern.
+ */
+extern bool appendStringInfoVA(StringInfo str, const char *fmt, va_list args);
+
+/*------------------------
+ * appendStringInfoString
+ * Append a null-terminated string to str.
+ * Like appendStringInfo(str, "%s", s) but faster.
+ */
+extern void appendStringInfoString(StringInfo str, const char *s);
+
 /*------------------------
  * appendStringInfoChar
  * Append a single byte to str.
diff --git a/src/include/libpq/pqcomm.h b/src/include/libpq/pqcomm.h
index 420f1e438e328b9d024eed5ebe92c0486217dc30..4f458c51c29a5312fa5c3eb47186a550d733de8b 100644
--- a/src/include/libpq/pqcomm.h
+++ b/src/include/libpq/pqcomm.h
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pqcomm.h,v 1.78 2003/04/22 00:08:07 tgl Exp $
+ * $Id: pqcomm.h,v 1.79 2003/04/24 21:16:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -106,7 +106,7 @@ typedef union SockAddr
 /* The earliest and latest frontend/backend protocol version supported. */
 
 #define PG_PROTOCOL_EARLIEST	PG_PROTOCOL(1,0)
-#define PG_PROTOCOL_LATEST		PG_PROTOCOL(3,102) /* XXX temporary value */
+#define PG_PROTOCOL_LATEST		PG_PROTOCOL(3,103) /* XXX temporary value */
 
 typedef uint32 ProtocolVersion; /* FE/BE protocol version number */
 
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 33a20e3cb94a1db1d5bcca193266e51eebaa4f87..b54b6f9d2a02f2dd8c869c2fed36389357006cdd 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -109,6 +109,12 @@
 /* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
 #undef HAVE_FSEEKO
 
+/* Define to 1 if your compiler understands __func__. */
+#undef HAVE_FUNCNAME__FUNC
+
+/* Define to 1 if your compiler understands __FUNCTION__. */
+#undef HAVE_FUNCNAME__FUNCTION
+
 /* Define to 1 if you have the `getaddrinfo' function. */
 #undef HAVE_GETADDRINFO
 
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index b1f431ed19e555402e12fa2028a4d13685d47199..e341fa48f3c6d04a36cd8c55d8a9dbe3e86f8e75 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: elog.h,v 1.40 2002/09/04 20:31:45 momjian Exp $
+ * $Id: elog.h,v 1.41 2003/04/24 21:16:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,19 +40,233 @@
 
  /*#define DEBUG DEBUG1*/	/* Backward compatibility with pre-7.3 */
 
-/* Configurable parameters */
-#ifdef HAVE_SYSLOG
-extern int	Use_syslog;
+
+/* macros for representing SQLSTATE strings compactly */
+#define PGSIXBIT(ch)	(((ch) - '0') & 0x3F)
+#define PGUNSIXBIT(val)	(((val) & 0x3F) + '0')
+
+#define MAKE_SQLSTATE(ch1,ch2,ch3,ch4,ch5)	\
+	(PGSIXBIT(ch1) + (PGSIXBIT(ch2) << 6) + (PGSIXBIT(ch3) << 12) + \
+	 (PGSIXBIT(ch4) << 18) + (PGSIXBIT(ch5) << 24))
+
+
+/* SQLSTATE codes defined by SQL99 */
+#define ERRCODE_AMBIGUOUS_CURSOR_NAME		MAKE_SQLSTATE('3','C', '0','0','0')
+#define ERRCODE_CARDINALITY_VIOLATION		MAKE_SQLSTATE('2','1', '0','0','0')
+#define ERRCODE_CLI_SPECIFIC_CONDITION		MAKE_SQLSTATE('H','Y', '0','0','0')
+#define ERRCODE_CONNECTION_EXCEPTION		MAKE_SQLSTATE('0','8', '0','0','0')
+#define ERRCODE_CONNECTION_DOES_NOT_EXIST	MAKE_SQLSTATE('0','8', '0','0','3')
+#define ERRCODE_CONNECTION_FAILURE			MAKE_SQLSTATE('0','8', '0','0','6')
+#define ERRCODE_CONNECTION_NAME_IN_USE		MAKE_SQLSTATE('0','8', '0','0','2')
+#define ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION		MAKE_SQLSTATE('0','8', '0','0','1')
+#define ERRCODE_SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION	MAKE_SQLSTATE('0','8', '0','0','4')
+#define ERRCODE_TRANSACTION_RESOLUTION_UNKNOWN		MAKE_SQLSTATE('0','8', '0','0','7')
+#define ERRCODE_DATA_EXCEPTION				MAKE_SQLSTATE('2','2', '0','0','0')
+#define ERRCODE_ARRAY_ELEMENT_ERROR			MAKE_SQLSTATE('2','2', '0','2','E')
+#define ERRCODE_CHARACTER_NOT_IN_REPERTOIRE	MAKE_SQLSTATE('2','2', '0','2','1')
+#define ERRCODE_DATETIME_FIELD_OVERFLOW		MAKE_SQLSTATE('2','2', '0','0','8')
+#define ERRCODE_DIVISION_BY_ZERO			MAKE_SQLSTATE('2','2', '0','1','2')
+#define ERRCODE_ERROR_IN_ASSIGNMENT			MAKE_SQLSTATE('2','2', '0','0','5')
+#define ERRCODE_ESCAPE_CHARACTER_CONFLICT	MAKE_SQLSTATE('2','2', '0','0','B')
+#define ERRCODE_INDICATOR_OVERFLOW			MAKE_SQLSTATE('2','2', '0','2','2')
+#define ERRCODE_INTERVAL_FIELD_OVERFLOW		MAKE_SQLSTATE('2','2', '0','1','5')
+#define ERRCODE_INVALID_CHARACTER_VALUE_FOR_CAST		MAKE_SQLSTATE('2','2', '0','1','8')
+#define ERRCODE_INVALID_DATETIME_FORMAT		MAKE_SQLSTATE('2','2', '0','0','7')
+#define ERRCODE_INVALID_ESCAPE_CHARACTER	MAKE_SQLSTATE('2','2', '0','1','9')
+#define ERRCODE_INVALID_ESCAPE_OCTET		MAKE_SQLSTATE('2','2', '0','0','D')
+#define ERRCODE_INVALID_ESCAPE_SEQUENCE		MAKE_SQLSTATE('2','2', '0','2','5')
+#define ERRCODE_INVALID_INDICATOR_PARAMETER_VALUE		MAKE_SQLSTATE('2','2', '0','1','0')
+#define ERRCODE_INVALID_LIMIT_VALUE			MAKE_SQLSTATE('2','2', '0','2','0')
+#define ERRCODE_INVALID_PARAMETER_VALUE		MAKE_SQLSTATE('2','2', '0','2','3')
+#define ERRCODE_INVALID_REGULAR_EXPRESSION	MAKE_SQLSTATE('2','2', '0','1','B')
+#define ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE	MAKE_SQLSTATE('2','2', '0','0','9')
+#define ERRCODE_INVALID_USE_OF_ESCAPE_CHARACTER		MAKE_SQLSTATE('2','2', '0','0','C')
+#define ERRCODE_NULL_VALUE_NO_INDICATOR_PARAMETER	MAKE_SQLSTATE('2','2', '0','0','G')
+#define ERRCODE_MOST_SPECIFIC_TYPE_MISMATCH	MAKE_SQLSTATE('2','2', '0','0','2')
+#define ERRCODE_NULL_VALUE_NOT_ALLOWED		MAKE_SQLSTATE('2','2', '0','0','4')
+#define ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE	MAKE_SQLSTATE('2','2', '0','0','3')
+#define ERRCODE_STRING_DATA_LENGTH_MISMATCH	MAKE_SQLSTATE('2','2', '0','2','6')
+#define ERRCODE_STRING_DATA_RIGHT_TRUNCATION		MAKE_SQLSTATE('2','2', '0','0','1')
+#define ERRCODE_SUBSTRING_ERROR				MAKE_SQLSTATE('2','2', '0','1','1')
+#define ERRCODE_TRIM_ERROR					MAKE_SQLSTATE('2','2', '0','2','7')
+#define ERRCODE_UNTERMINATED_C_STRING		MAKE_SQLSTATE('2','2', '0','2','4')
+#define ERRCODE_ZERO_LENGTH_CHARACTER_STRING		MAKE_SQLSTATE('2','2', '0','0','F')
+#define ERRCODE_DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST		MAKE_SQLSTATE('2','B', '0','0','0')
+#define ERRCODE_EXTERNAL_ROUTINE_EXCEPTION	MAKE_SQLSTATE('3','8', '0','0','0')
+#define ERRCODE_EXTERNAL_ROUTINE_EXCEPTION_CONTAINING_SQL_NOT_PERMITTED	MAKE_SQLSTATE('3','8', '0','0','1')
+#define ERRCODE_EXTERNAL_ROUTINE_EXCEPTION_MODIFYING_SQL_DATA_NOT_PERMITTED	MAKE_SQLSTATE('3','8', '0','0','2')
+#define ERRCODE_EXTERNAL_ROUTINE_EXCEPTION_PROHIBITED_SQL_STATEMENT_ATTEMPTED	MAKE_SQLSTATE('3','8', '0','0','3')
+#define ERRCODE_EXTERNAL_ROUTINE_EXCEPTION_READING_SQL_DATA_NOT_PERMITTED	MAKE_SQLSTATE('3','8', '0','0','4')
+#define ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION	MAKE_SQLSTATE('3','9', '0','0','0')
+#define ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION_INVALID_SQLSTATE_RETURNED	MAKE_SQLSTATE('3','9', '0','0','1')
+#define ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION_NULL_VALUE_NOT_ALLOWED	MAKE_SQLSTATE('3','9', '0','0','4')
+#define ERRCODE_FEATURE_NOT_SUPPORTED		MAKE_SQLSTATE('0','A', '0','0','0')
+#define ERRCODE_MULTIPLE_ENVIRONMENT_TRANSACTIONS	MAKE_SQLSTATE('0','A', '0','0','1')
+#define ERRCODE_INTEGRITY_CONSTRAINT_VIOLATION		MAKE_SQLSTATE('2','3', '0','0','0')
+#define ERRCODE_RESTRICT_VIOLATION			MAKE_SQLSTATE('2','3', '0','0','1')
+#define ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION	MAKE_SQLSTATE('2','8', '0','0','0')
+#define ERRCODE_INVALID_CATALOG_NAME		MAKE_SQLSTATE('3','D', '0','0','0')
+#define ERRCODE_INVALID_CONDITION_NUMBER	MAKE_SQLSTATE('3','5', '0','0','0')
+#define ERRCODE_INVALID_CONNECTION_NAME		MAKE_SQLSTATE('2','E', '0','0','0')
+#define ERRCODE_INVALID_CURSOR_NAME			MAKE_SQLSTATE('3','4', '0','0','0')
+#define ERRCODE_INVALID_CURSOR_STATE		MAKE_SQLSTATE('2','4', '0','0','0')
+#define ERRCODE_INVALID_GRANTOR_STATE		MAKE_SQLSTATE('0','L', '0','0','0')
+#define ERRCODE_INVALID_ROLE_SPECIFICATION	MAKE_SQLSTATE('0','P', '0','0','0')
+#define ERRCODE_INVALID_SCHEMA_NAME			MAKE_SQLSTATE('3','F', '0','0','0')
+#define ERRCODE_INVALID_SQL_DESCRIPTOR_NAME	MAKE_SQLSTATE('3','3', '0','0','0')
+#define ERRCODE_INVALID_SQL_STATEMENT		MAKE_SQLSTATE('3','0', '0','0','0')
+#define ERRCODE_INVALID_SQL_STATEMENT_NAME	MAKE_SQLSTATE('2','6', '0','0','0')
+#define ERRCODE_INVALID_TARGET_SPECIFICATION_VALUE	MAKE_SQLSTATE('3','1', '0','0','0')
+#define ERRCODE_INVALID_TRANSACTION_STATE	MAKE_SQLSTATE('2','5', '0','0','0')
+#define ERRCODE_ACTIVE_SQL_TRANSACTION		MAKE_SQLSTATE('2','5', '0','0','1')
+#define ERRCODE_BRANCH_TRANSACTION_ALREADY_ACTIVE	MAKE_SQLSTATE('2','5', '0','0','2')
+#define ERRCODE_HELD_CURSOR_REQUIRES_SAME_ISOLATION_LEVEL	MAKE_SQLSTATE('2','5', '0','0','8')
+#define ERRCODE_INAPPROPRIATE_ACCESS_MODE_FOR_BRANCH_TRANSACTION	MAKE_SQLSTATE('2','5', '0','0','3')
+#define ERRCODE_INAPPROPRIATE_ISOLATION_LEVEL_FOR_BRANCH_TRANSACTION	MAKE_SQLSTATE('2','5', '0','0','4')
+#define ERRCODE_NO_ACTIVE_SQL_TRANSACTION_FOR_BRANCH_TRANSACTION	MAKE_SQLSTATE('2','5', '0','0','5')
+#define ERRCODE_READ_ONLY_SQL_TRANSACTION	MAKE_SQLSTATE('2','5', '0','0','6')
+#define ERRCODE_SCHEMA_AND_DATA_STATEMENT_MIXING_NOT_SUPPORTED	MAKE_SQLSTATE('2','5', '0','0','7')
+#define ERRCODE_INVALID_TRANSACTION_INITIATION		MAKE_SQLSTATE('0','B', '0','0','0')
+#define ERRCODE_INVALID_TRANSACTION_TERMINATION		MAKE_SQLSTATE('2','D', '0','0','0')
+#define ERRCODE_LOCATOR_EXCEPTION			MAKE_SQLSTATE('0','F', '0','0','0')
+#define ERRCODE_LOCATOR_EXCEPTION_INVALID_SPECIFICATION		MAKE_SQLSTATE('0','F', '0','0','1')
+#define ERRCODE_NO_DATA						MAKE_SQLSTATE('0','2', '0','0','0')
+#define ERRCODE_NO_ADDITIONAL_DYNAMIC_RESULT_SETS_RETURNED	MAKE_SQLSTATE('0','2', '0','0','1')
+#define ERRCODE_REMOTE_DATABASE_ACCESS		MAKE_SQLSTATE('H','Z', '0','0','0')
+#define ERRCODE_SAVEPOINT_EXCEPTION			MAKE_SQLSTATE('3','B', '0','0','0')
+#define ERRCODE_SAVEPOINT_EXCEPTION_INVALID_SPECIFICATION	MAKE_SQLSTATE('3','B', '0','0','1')
+#define ERRCODE_SAVEPOINT_EXCEPTION_TOO_MANY		MAKE_SQLSTATE('3','B', '0','0','2')
+#define ERRCODE_SQL_ROUTINE_EXCEPTION		MAKE_SQLSTATE('2','F', '0','0','0')
+#define ERRCODE_FUNCTION_EXECUTED_NO_RETURN_STATEMENT		MAKE_SQLSTATE('2','F', '0','0','5')
+#define ERRCODE_MODIFYING_SQL_DATA_NOT_PERMITTED	MAKE_SQLSTATE('2','F', '0','0','2')
+#define ERRCODE_PROHIBITED_SQL_STATEMENT_ATTEMPTED	MAKE_SQLSTATE('2','F', '0','0','3')
+#define ERRCODE_READING_SQL_DATA_NOT_PERMITTED		MAKE_SQLSTATE('2','F', '0','0','4')
+#define ERRCODE_SQL_STATEMENT_NOT_YET_COMPLETE		MAKE_SQLSTATE('0','3', '0','0','0')
+#define ERRCODE_SUCCESSFUL_COMPLETION		MAKE_SQLSTATE('0','0', '0','0','0')
+#define ERRCODE_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION		MAKE_SQLSTATE('4','2', '0','0','0')
+#define ERRCODE_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION_IN_DIRECT_STATEMENT	MAKE_SQLSTATE('2','A', '0','0','0')
+#define ERRCODE_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION_IN_DYNAMIC_STATEMENT	MAKE_SQLSTATE('3','7', '0','0','0')
+#define ERRCODE_TRANSACTION_ROLLBACK		MAKE_SQLSTATE('4','0', '0','0','0')
+#define ERRCODE_TRANSACTION_ROLLBACK_INTEGRITY_CONSTRAINT_VIOLATION	MAKE_SQLSTATE('4','0', '0','0','2')
+#define ERRCODE_TRANSACTION_ROLLBACK_SERIALIZATION_FAILURE	MAKE_SQLSTATE('4','0', '0','0','1')
+#define ERRCODE_TRANSACTION_ROLLBACK_STATEMENT_COMPLETION_UNKNOWN	MAKE_SQLSTATE('4','0', '0','0','3')
+#define ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION		MAKE_SQLSTATE('2','7', '0','0','0')
+#define ERRCODE_WARNING						MAKE_SQLSTATE('0','1', '0','0','0')
+#define ERRCODE_CURSOR_OPERATION_CONFLICT	MAKE_SQLSTATE('0','1', '0','0','1')
+#define ERRCODE_DISCONNECT_ERROR			MAKE_SQLSTATE('0','1', '0','0','2')
+#define ERRCODE_DYNAMIC_RESULT_SETS_RETURNED		MAKE_SQLSTATE('0','1', '0','0','C')
+#define ERRCODE_IMPLICIT_ZERO_BIT_PADDING	MAKE_SQLSTATE('0','1', '0','0','8')
+#define ERRCODE_NULL_VALUE_ELIMINATED_IN_SET_FUNCTION	MAKE_SQLSTATE('0','1', '0','0','3')
+#define ERRCODE_PRIVILEGE_NOT_GRANTED		MAKE_SQLSTATE('0','1', '0','0','7')
+#define ERRCODE_PRIVILEGE_NOT_REVOKED		MAKE_SQLSTATE('0','1', '0','0','6')
+#define ERRCODE_QUERY_EXPRESSION_TOO_LONG_FOR_INFORMATION_SCHEMA	MAKE_SQLSTATE('0','1', '0','0','A')
+#define ERRCODE_SEARCH_CONDITION_TOO_LONG_FOR_INFORMATION_SCHEMA	MAKE_SQLSTATE('0','1', '0','0','9')
+#define ERRCODE_STATEMENT_TOO_LONG_FOR_INFORMATION_SCHEMA	MAKE_SQLSTATE('0','1', '0','0','5')
+#define ERRCODE_STRING_DATA_RIGHT_TRUNCATION_WARNING	MAKE_SQLSTATE('0','1', '0','0','4')
+#define ERRCODE_WITH_CHECK_OPTION_VIOLATION	MAKE_SQLSTATE('4','4', '0','0','0')
+
+/* Implementation-defined error codes for PostgreSQL */
+#define ERRCODE_INTERNAL_ERROR				MAKE_SQLSTATE('X','X', '0','0','0')
+
+
+/* Which __func__ symbol do we have, if any? */
+#ifdef HAVE_FUNCNAME__FUNC
+#define PG_FUNCNAME_MACRO	__func__
+#else
+#ifdef HAVE_FUNCNAME__FUNCTION
+#define PG_FUNCNAME_MACRO	__FUNCTION__
+#else
+#define PG_FUNCNAME_MACRO	NULL
 #endif
-extern bool Log_timestamp;
-extern bool Log_pid;
+#endif
+
+
+/*----------
+ * New-style error reporting API: to be used in this way:
+ *		ereport(ERROR,
+ *				(errcode(ERRCODE_INVALID_CURSOR_NAME),
+ *				 errmsg("portal \"%s\" not found", stmt->portalname),
+ *				 ... other errxxx() fields as needed ...));
+ *
+ * The error level is required, and so is a primary error message (errmsg
+ * or errmsg_internal).  All else is optional.  errcode() defaults to
+ * ERRCODE_INTERNAL_ERROR.
+ *----------
+ */
+#define ereport(elevel, rest)  \
+	(errstart(elevel, __FILE__, __LINE__, PG_FUNCNAME_MACRO) ? \
+	 (errfinish rest) : (void) 0)
+
+extern bool errstart(int elevel, const char *filename, int lineno,
+					 const char *funcname);
+extern void errfinish(int dummy, ...);
+
+extern int errcode(int sqlerrcode);
+
+extern int errmsg(const char *fmt, ...)
+/* This extension allows gcc to check the format string for consistency with
+   the supplied arguments. */
+__attribute__((format(printf, 1, 2)));
+
+extern int errmsg_internal(const char *fmt, ...)
+/* This extension allows gcc to check the format string for consistency with
+   the supplied arguments. */
+__attribute__((format(printf, 1, 2)));
+
+extern int errdetail(const char *fmt, ...)
+/* This extension allows gcc to check the format string for consistency with
+   the supplied arguments. */
+__attribute__((format(printf, 1, 2)));
+
+extern int errhint(const char *fmt, ...)
+/* This extension allows gcc to check the format string for consistency with
+   the supplied arguments. */
+__attribute__((format(printf, 1, 2)));
+
+extern int errcontext(const char *fmt, ...)
+/* This extension allows gcc to check the format string for consistency with
+   the supplied arguments. */
+__attribute__((format(printf, 1, 2)));
+
+extern int errfunction(const char *funcname);
+extern int errposition(int cursorpos);
+
+
+/*----------
+ * Old-style error reporting API: to be used in this way:
+ *		elog(ERROR, "portal \"%s\" not found", stmt->portalname);
+ *----------
+ */
+#define elog    errstart(ERROR, __FILE__, __LINE__, PG_FUNCNAME_MACRO), elog_finish
 
 extern void
-elog(int lev, const char *fmt,...)
+elog_finish(int elevel, const char *fmt, ...)
 /* This extension allows gcc to check the format string for consistency with
    the supplied arguments. */
 __attribute__((format(printf, 2, 3)));
 
-extern int	DebugFileOpen(void);
+
+/* Support for attaching context information to error reports */
+
+typedef struct ErrorContextCallback
+{
+	struct ErrorContextCallback *previous;
+	void (*callback) (void *arg);
+	void *arg;
+} ErrorContextCallback;
+
+extern ErrorContextCallback *error_context_stack;
+
+
+/* GUC-configurable parameters */
+extern bool Log_timestamp;
+extern bool Log_pid;
+#ifdef HAVE_SYSLOG
+extern int	Use_syslog;
+#endif
+
+
+/* Other exported functions */
+extern void DebugFileOpen(void);
 
 #endif   /* ELOG_H */
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 086462094ff2c65a70b3cc9ead27b40ddb6a7d2e..6ee2716ec0912e0329c23c6916759b1166cba271 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.234 2003/04/22 00:08:07 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.235 2003/04/24 21:16:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1429,20 +1429,13 @@ keep_going:						/* We will come back to here until there
 				/* Handle errors. */
 				if (beresp == 'E')
 				{
-					if (pqGets(&conn->errorMessage, conn))
+					if (pqGetErrorNotice(conn, true))
 					{
 						/* We'll come back when there is more data */
 						return PGRES_POLLING_READING;
 					}
 					/* OK, we read the message; mark data consumed */
 					conn->inStart = conn->inCursor;
-
-					/*
-					 * The postmaster typically won't end its message with
-					 * a newline, so add one to conform to libpq
-					 * conventions.
-					 */
-					appendPQExpBufferChar(&conn->errorMessage, '\n');
 					goto error_return;
 				}
 
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 16e63f7f68f0944cbcd21f6429a0558f7cbd901e..3fe0ddc4920551b8ca073afd5317af64d99c378d 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.130 2003/04/22 00:08:07 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.131 2003/04/24 21:16:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -55,7 +55,6 @@ static void handleSyncLoss(PGconn *conn, char id, int msgLength);
 static int	getRowDescriptions(PGconn *conn);
 static int	getAnotherTuple(PGconn *conn, int binary);
 static int	getNotify(PGconn *conn);
-static int	getNotice(PGconn *conn);
 
 /* ---------------
  * Escaping arbitrary strings to get valid SQL strings/identifiers.
@@ -390,6 +389,16 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
 	result->cmdStatus[0] = '\0';
 	result->binary = 0;
 	result->errMsg = NULL;
+	result->errSeverity = NULL;
+	result->errCode = NULL;
+	result->errPrimary = NULL;
+	result->errDetail = NULL;
+	result->errHint = NULL;
+	result->errPosition = NULL;
+	result->errContext = NULL;
+	result->errFilename = NULL;
+	result->errLineno = NULL;
+	result->errFuncname = NULL;
 	result->null_field[0] = '\0';
 	result->curBlock = NULL;
 	result->curOffset = 0;
@@ -949,7 +958,7 @@ parseInput(PGconn *conn)
 		}
 		else if (id == 'N')
 		{
-			if (getNotice(conn))
+			if (pqGetErrorNotice(conn, false))
 				return;
 		}
 		else if (conn->asyncStatus != PGASYNC_BUSY)
@@ -968,7 +977,7 @@ parseInput(PGconn *conn)
 			 */
 			if (id == 'E')
 			{
-				if (getNotice(conn))
+				if (pqGetErrorNotice(conn, false /* treat as notice */))
 					return;
 			}
 			else
@@ -999,10 +1008,8 @@ parseInput(PGconn *conn)
 					conn->asyncStatus = PGASYNC_READY;
 					break;
 				case 'E':		/* error return */
-					if (pqGets(&conn->errorMessage, conn))
+					if (pqGetErrorNotice(conn, true))
 						return;
-					/* build an error result holding the error message */
-					saveErrorResult(conn);
 					conn->asyncStatus = PGASYNC_READY;
 					break;
 				case 'Z':		/* backend is ready for new query */
@@ -1540,31 +1547,132 @@ errout:
 
 
 /*
- * Attempt to read a Notice response message.
+ * Attempt to read an Error or Notice response message.
  * This is possible in several places, so we break it out as a subroutine.
- * Entry: 'N' message type and length have already been consumed.
- * Exit: returns 0 if successfully consumed Notice message.
+ * Entry: 'E' or 'N' message type and length have already been consumed.
+ * Exit: returns 0 if successfully consumed message.
  *		 returns EOF if not enough data.
  */
-static int
-getNotice(PGconn *conn)
+int
+pqGetErrorNotice(PGconn *conn, bool isError)
 {
+	PGresult   *res;
+	PQExpBufferData workBuf;
+	char		id;
+
+	/*
+	 * Make a PGresult to hold the accumulated fields.  We temporarily
+	 * lie about the result status, so that PQmakeEmptyPGresult doesn't
+	 * uselessly copy conn->errorMessage.
+	 */
+	res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY);
+	res->resultStatus = PGRES_FATAL_ERROR;
 	/*
-	 * Since the Notice might be pretty long, we create a temporary
+	 * Since the fields might be pretty long, we create a temporary
 	 * PQExpBuffer rather than using conn->workBuffer.	workBuffer is
-	 * intended for stuff that is expected to be short.
+	 * intended for stuff that is expected to be short.  We shouldn't
+	 * use conn->errorMessage either, since this might be only a notice.
 	 */
-	PQExpBufferData noticeBuf;
+	initPQExpBuffer(&workBuf);
 
-	initPQExpBuffer(&noticeBuf);
-	if (pqGets(&noticeBuf, conn))
+	/*
+	 * Read the fields and save into res.
+	 */
+	for (;;)
 	{
-		termPQExpBuffer(&noticeBuf);
-		return EOF;
+		if (pqGetc(&id, conn))
+			goto fail;
+		if (id == '\0')
+			break;				/* terminator found */
+		if (pqGets(&workBuf, conn))
+			goto fail;
+		switch (id)
+		{
+			case 'S':
+				res->errSeverity = pqResultStrdup(res, workBuf.data);
+				break;
+			case 'C':
+				res->errCode = pqResultStrdup(res, workBuf.data);
+				break;
+			case 'M':
+				res->errPrimary = pqResultStrdup(res, workBuf.data);
+				break;
+			case 'D':
+				res->errDetail = pqResultStrdup(res, workBuf.data);
+				break;
+			case 'H':
+				res->errHint = pqResultStrdup(res, workBuf.data);
+				break;
+			case 'P':
+				res->errPosition = pqResultStrdup(res, workBuf.data);
+				break;
+			case 'W':
+				res->errContext = pqResultStrdup(res, workBuf.data);
+				break;
+			case 'F':
+				res->errFilename = pqResultStrdup(res, workBuf.data);
+				break;
+			case 'L':
+				res->errLineno = pqResultStrdup(res, workBuf.data);
+				break;
+			case 'R':
+				res->errFuncname = pqResultStrdup(res, workBuf.data);
+				break;
+			default:
+				/* silently ignore any other field type */
+				break;
+		}
 	}
-	DONOTICE(conn, noticeBuf.data);
-	termPQExpBuffer(&noticeBuf);
+
+	/*
+	 * Now build the "overall" error message for PQresultErrorMessage.
+	 *
+	 * XXX this should be configurable somehow.
+	 */
+	resetPQExpBuffer(&workBuf);
+	if (res->errSeverity)
+		appendPQExpBuffer(&workBuf, "%s:  ", res->errSeverity);
+	if (res->errPrimary)
+		appendPQExpBufferStr(&workBuf, res->errPrimary);
+	/* translator: %s represents a digit string */
+	if (res->errPosition)
+		appendPQExpBuffer(&workBuf, libpq_gettext(" at character %s"),
+						  res->errPosition);
+	appendPQExpBufferChar(&workBuf, '\n');
+	if (res->errDetail)
+		appendPQExpBuffer(&workBuf, libpq_gettext("DETAIL:  %s\n"),
+						  res->errDetail);
+	if (res->errHint)
+		appendPQExpBuffer(&workBuf, libpq_gettext("HINT:  %s\n"),
+						  res->errHint);
+	if (res->errContext)
+		appendPQExpBuffer(&workBuf, libpq_gettext("CONTEXT:  %s\n"),
+						  res->errContext);
+
+	/*
+	 * Either save error as current async result, or just emit the notice.
+	 */
+	if (isError)
+	{
+		res->errMsg = pqResultStrdup(res, workBuf.data);
+		pqClearAsyncResult(conn);
+		conn->result = res;
+		resetPQExpBuffer(&conn->errorMessage);
+		appendPQExpBufferStr(&conn->errorMessage, workBuf.data);
+	}
+	else
+	{
+		DONOTICE(conn, workBuf.data);
+		PQclear(res);
+	}
+
+	termPQExpBuffer(&workBuf);
 	return 0;
+
+fail:
+	PQclear(res);
+	termPQExpBuffer(&workBuf);
+	return EOF;
 }
 
 /*
@@ -2123,10 +2231,8 @@ PQfn(PGconn *conn,
 				}
 				break;
 			case 'E':			/* error return */
-				if (pqGets(&conn->errorMessage, conn))
+				if (pqGetErrorNotice(conn, true))
 					continue;
-				/* build an error result holding the error message */
-				saveErrorResult(conn);
 				status = PGRES_FATAL_ERROR;
 				break;
 			case 'A':			/* notify message */
@@ -2136,7 +2242,7 @@ PQfn(PGconn *conn,
 				break;
 			case 'N':			/* notice */
 				/* handle notice and go back to processing return values */
-				if (getNotice(conn))
+				if (pqGetErrorNotice(conn, false))
 					continue;
 				break;
 			case 'Z':			/* backend is ready for new query */
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 35e3208eb0efb1e05302d2409828684acddeb9ca..4688fbf8f5aaef7e30f47235f185c4a281b01c6c 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -12,7 +12,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: libpq-int.h,v 1.63 2003/04/22 00:08:07 tgl Exp $
+ * $Id: libpq-int.h,v 1.64 2003/04/24 21:16:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -56,7 +56,7 @@ typedef int ssize_t;			/* ssize_t doesn't exist in VC (atleast
  * pqcomm.h describe what the backend knows, not what libpq knows.
  */
 
-#define PG_PROTOCOL_LIBPQ	PG_PROTOCOL(3,102) /* XXX temporary value */
+#define PG_PROTOCOL_LIBPQ	PG_PROTOCOL(3,103) /* XXX temporary value */
 
 /*
  * POSTGRES backend dependent Constants.
@@ -101,7 +101,7 @@ typedef struct pgresAttDesc
 /* We use char* for Attribute values.
    The value pointer always points to a null-terminated area; we add a
    null (zero) byte after whatever the backend sends us.  This is only
-   particularly useful for ASCII tuples ... with a binary value, the
+   particularly useful for text tuples ... with a binary value, the
    value might have embedded nulls, so the application can't use C string
    operators on it.  But we add a null anyway for consistency.
    Note that the value itself does not contain a length word.
@@ -133,7 +133,7 @@ struct pg_result
 	char		cmdStatus[CMDSTATUS_LEN];		/* cmd status from the
 												 * last query */
 	int			binary;			/* binary tuple values if binary == 1,
-								 * otherwise ASCII */
+								 * otherwise text */
 
 	/*
 	 * The conn link in PGresult is no longer used by any libpq code. It
@@ -152,9 +152,25 @@ struct pg_result
 	void	   *noticeArg;
 	int			client_encoding;	/* encoding id */
 
-
+	/*
+	 * Error information (all NULL if not an error result).  errMsg is the
+	 * "overall" error message returned by PQresultErrorMessage.  If we
+	 * got a field-ized error from the server then the additional fields
+	 * may be set.
+	 */
 	char	   *errMsg;			/* error message, or NULL if no error */
 
+	char	   *errSeverity;	/* severity code */
+	char	   *errCode;		/* SQLSTATE code */
+	char	   *errPrimary;		/* primary message text */
+	char	   *errDetail;		/* detail text */
+	char	   *errHint;		/* hint text */
+	char	   *errPosition;	/* cursor position */
+	char	   *errContext;		/* location information */
+	char	   *errFilename;	/* source-code file name */
+	char	   *errLineno;		/* source-code line number */
+	char	   *errFuncname;	/* source-code function name */
+
 	/* All NULL attributes in the query result point to this null string */
 	char		null_field[1];
 
@@ -321,6 +337,7 @@ extern void pqSetResultError(PGresult *res, const char *msg);
 extern void *pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary);
 extern char *pqResultStrdup(PGresult *res, const char *str);
 extern void pqClearAsyncResult(PGconn *conn);
+extern int	pqGetErrorNotice(PGconn *conn, bool isError);
 
 /* === in fe-misc.c === */
 
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index dedbca9d2fcabf9d41e39181482c63a6555a7dae..feb80f94ae78d146334dfbe90fb010330254f6bb 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -3,7 +3,7 @@
  *			  procedural language
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.55 2003/03/25 00:34:23 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.56 2003/04/24 21:16:44 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -80,6 +80,7 @@ int			plpgsql_DumpExecTree = 0;
 PLpgSQL_function *plpgsql_curr_compile;
 
 
+static void plpgsql_compile_error_callback(void *arg);
 static PLpgSQL_row *build_rowtype(Oid classOid);
 
 
@@ -121,7 +122,7 @@ plpgsql_compile(Oid fn_oid, int functype)
 	PLpgSQL_rec *rec;
 	int			i;
 	int			arg_varnos[FUNC_MAX_ARGS];
-	sigjmp_buf	save_restart;
+	ErrorContextCallback plerrcontext;
 
 	/*
 	 * Lookup the pg_proc tuple by Oid
@@ -133,37 +134,25 @@ plpgsql_compile(Oid fn_oid, int functype)
 		elog(ERROR, "plpgsql: cache lookup for proc %u failed", fn_oid);
 
 	/*
-	 * Setup the scanner input and error info
+	 * Setup the scanner input and error info.  We assume that this function
+	 * cannot be invoked recursively, so there's no need to save and restore
+	 * the static variables used here.
 	 */
 	procStruct = (Form_pg_proc) GETSTRUCT(procTup);
 	proc_source = DatumGetCString(DirectFunctionCall1(textout,
 								  PointerGetDatum(&procStruct->prosrc)));
 	plpgsql_setinput(proc_source, functype);
+
 	plpgsql_error_funcname = pstrdup(NameStr(procStruct->proname));
 	plpgsql_error_lineno = 0;
 
 	/*
-	 * Catch elog() so we can provide notice about where the error is
+	 * Setup error traceback support for ereport()
 	 */
-	memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
-	if (sigsetjmp(Warn_restart, 1) != 0)
-	{
-		memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
-
-		/*
-		 * If we are the first of cascaded error catchings, print where
-		 * this happened
-		 */
-		if (plpgsql_error_funcname != NULL)
-		{
-			elog(WARNING, "plpgsql: ERROR during compile of %s near line %d",
-				 plpgsql_error_funcname, plpgsql_error_lineno);
-
-			plpgsql_error_funcname = NULL;
-		}
-
-		siglongjmp(Warn_restart, 1);
-	}
+	plerrcontext.callback = plpgsql_compile_error_callback;
+	plerrcontext.arg = NULL;
+	plerrcontext.previous = error_context_stack;
+	error_context_stack = &plerrcontext;
 
 	/*
 	 * Initialize the compiler
@@ -530,11 +519,11 @@ plpgsql_compile(Oid fn_oid, int functype)
 	ReleaseSysCache(procTup);
 
 	/*
-	 * Restore the previous elog() jump target
+	 * Pop the error context stack
 	 */
+	error_context_stack = plerrcontext.previous;
 	plpgsql_error_funcname = NULL;
 	plpgsql_error_lineno = 0;
-	memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
 
 	/*
 	 * Finally return the compiled function
@@ -545,6 +534,18 @@ plpgsql_compile(Oid fn_oid, int functype)
 }
 
 
+/*
+ * error context callback to let us supply a call-stack traceback
+ */
+static void
+plpgsql_compile_error_callback(void *arg)
+{
+	if (plpgsql_error_funcname)
+		errcontext("compile of PL/pgSQL function %s near line %d",
+				   plpgsql_error_funcname, plpgsql_error_lineno);
+}
+
+
 /* ----------
  * plpgsql_parse_word		The scanner calls this to postparse
  *				any single word not found by a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 6620e41970335666eda0582b4030d36686ce55d1..efb423541a9d64ae77bf51de3b38153a46f0088b 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3,7 +3,7 @@
  *			  procedural language
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.85 2003/04/08 23:20:04 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.86 2003/04/24 21:16:44 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -55,14 +55,12 @@
 #include "utils/syscache.h"
 
 
-static PLpgSQL_function *error_info_func = NULL;
-static PLpgSQL_stmt *error_info_stmt = NULL;
-static char *error_info_text = NULL;
-
+static const char * const raise_skip_msg = "RAISE";
 
 /************************************************************
  * Local function forward declarations
  ************************************************************/
+static void plpgsql_exec_error_callback(void *arg);
 static PLpgSQL_var *copy_var(PLpgSQL_var * var);
 static PLpgSQL_rec *copy_rec(PLpgSQL_rec * rec);
 
@@ -173,61 +171,26 @@ Datum
 plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo)
 {
 	PLpgSQL_execstate estate;
+	ErrorContextCallback plerrcontext;
 	int			i;
-	sigjmp_buf	save_restart;
-	PLpgSQL_function *save_efunc;
-	PLpgSQL_stmt *save_estmt;
-	char	   *save_etext;
 
 	/*
-	 * Setup debug error info and catch elog()
+	 * Setup the execution state
 	 */
-	save_efunc = error_info_func;
-	save_estmt = error_info_stmt;
-	save_etext = error_info_text;
-
-	error_info_func = func;
-	error_info_stmt = NULL;
-	error_info_text = "while initialization of execution state";
-
-	memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
-	if (sigsetjmp(Warn_restart, 1) != 0)
-	{
-		memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
-
-		/*
-		 * If we are the first of cascaded error catchings, print where
-		 * this happened
-		 */
-		if (error_info_func != NULL)
-		{
-			elog(WARNING, "Error occurred while executing PL/pgSQL function %s",
-				 error_info_func->fn_name);
-			if (error_info_stmt != NULL)
-				elog(WARNING, "line %d at %s", error_info_stmt->lineno,
-					 plpgsql_stmt_typename(error_info_stmt));
-			else if (error_info_text != NULL)
-				elog(WARNING, "%s", error_info_text);
-			else
-				elog(WARNING, "no more error information available");
-
-			error_info_func = NULL;
-			error_info_stmt = NULL;
-			error_info_text = NULL;
-		}
-
-		siglongjmp(Warn_restart, 1);
-	}
-
+	plpgsql_estate_setup(&estate, func, (ReturnSetInfo *) fcinfo->resultinfo);
 
 	/*
-	 * Setup the execution state
+	 * Setup error traceback support for ereport()
 	 */
-	plpgsql_estate_setup(&estate, func, (ReturnSetInfo *) fcinfo->resultinfo);
+	plerrcontext.callback = plpgsql_exec_error_callback;
+	plerrcontext.arg = &estate;
+	plerrcontext.previous = error_context_stack;
+	error_context_stack = &plerrcontext;
 
 	/*
 	 * Make local execution copies of all the datums
 	 */
+	estate.err_text = "while initialization of execution state";
 	for (i = 0; i < func->ndatums; i++)
 	{
 		switch (func->datums[i]->dtype)
@@ -257,7 +220,7 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo)
 	/*
 	 * Put the actual call argument values into the variables
 	 */
-	error_info_text = "while putting call arguments to local variables";
+	estate.err_text = "while putting call arguments to local variables";
 	for (i = 0; i < func->fn_nargs; i++)
 	{
 		int			n = func->fn_argvarnos[i];
@@ -298,7 +261,7 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo)
 	 * Initialize the other variables to NULL values for now. The default
 	 * values are set when the blocks are entered.
 	 */
-	error_info_text = "while initializing local variables to NULL";
+	estate.err_text = "while initializing local variables to NULL";
 	for (i = estate.found_varno; i < estate.ndatums; i++)
 	{
 		switch (estate.datums[i]->dtype)
@@ -333,20 +296,20 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo)
 	/*
 	 * Now call the toplevel block of statements
 	 */
-	error_info_text = NULL;
-	error_info_stmt = (PLpgSQL_stmt *) (func->action);
+	estate.err_text = NULL;
+	estate.err_stmt = (PLpgSQL_stmt *) (func->action);
 	if (exec_stmt_block(&estate, func->action) != PLPGSQL_RC_RETURN)
 	{
-		error_info_stmt = NULL;
-		error_info_text = "at END of toplevel PL block";
+		estate.err_stmt = NULL;
+		estate.err_text = "at END of toplevel PL block";
 		elog(ERROR, "control reaches end of function without RETURN");
 	}
 
 	/*
 	 * We got a return value - process it
 	 */
-	error_info_stmt = NULL;
-	error_info_text = "while casting return value to function's return type";
+	estate.err_stmt = NULL;
+	estate.err_text = "while casting return value to function's return type";
 
 	fcinfo->isnull = estate.retisnull;
 
@@ -417,12 +380,9 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo)
 	exec_eval_cleanup(&estate);
 
 	/*
-	 * Restore the previous error info and elog() jump target
+	 * Pop the error context stack
 	 */
-	error_info_func = save_efunc;
-	error_info_stmt = save_estmt;
-	error_info_text = save_etext;
-	memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
+	error_context_stack = plerrcontext.previous;
 
 	/*
 	 * Return the functions result
@@ -441,65 +401,30 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
 					 TriggerData *trigdata)
 {
 	PLpgSQL_execstate estate;
+	ErrorContextCallback plerrcontext;
 	int			i;
-	sigjmp_buf	save_restart;
-	PLpgSQL_function *save_efunc;
-	PLpgSQL_stmt *save_estmt;
-	char	   *save_etext;
 	PLpgSQL_var *var;
 	PLpgSQL_rec *rec_new,
 				*rec_old;
 	HeapTuple	rettup;
 
 	/*
-	 * Setup debug error info and catch elog()
+	 * Setup the execution state
 	 */
-	save_efunc = error_info_func;
-	save_estmt = error_info_stmt;
-	save_etext = error_info_text;
-
-	error_info_func = func;
-	error_info_stmt = NULL;
-	error_info_text = "while initialization of execution state";
-
-	memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
-	if (sigsetjmp(Warn_restart, 1) != 0)
-	{
-		memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
-
-		/*
-		 * If we are the first of cascaded error catchings, print where
-		 * this happened
-		 */
-		if (error_info_func != NULL)
-		{
-			elog(WARNING, "Error occurred while executing PL/pgSQL function %s",
-				 error_info_func->fn_name);
-			if (error_info_stmt != NULL)
-				elog(WARNING, "line %d at %s", error_info_stmt->lineno,
-					 plpgsql_stmt_typename(error_info_stmt));
-			else if (error_info_text != NULL)
-				elog(WARNING, "%s", error_info_text);
-			else
-				elog(WARNING, "no more error information available");
-
-			error_info_func = NULL;
-			error_info_stmt = NULL;
-			error_info_text = NULL;
-		}
-
-		siglongjmp(Warn_restart, 1);
-	}
-
+	plpgsql_estate_setup(&estate, func, NULL);
 
 	/*
-	 * Setup the execution state
+	 * Setup error traceback support for ereport()
 	 */
-	plpgsql_estate_setup(&estate, func, NULL);
+	plerrcontext.callback = plpgsql_exec_error_callback;
+	plerrcontext.arg = &estate;
+	plerrcontext.previous = error_context_stack;
+	error_context_stack = &plerrcontext;
 
 	/*
 	 * Make local execution copies of all the datums
 	 */
+	estate.err_text = "while initialization of execution state";
 	for (i = 0; i < func->ndatums; i++)
 	{
 		switch (func->datums[i]->dtype)
@@ -634,7 +559,7 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
 	 * Put the actual call argument values into the special execution
 	 * state variables
 	 */
-	error_info_text = "while putting call arguments to local variables";
+	estate.err_text = "while putting call arguments to local variables";
 	estate.trig_nargs = trigdata->tg_trigger->tgnargs;
 	if (estate.trig_nargs == 0)
 		estate.trig_argv = NULL;
@@ -650,7 +575,7 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
 	 * Initialize the other variables to NULL values for now. The default
 	 * values are set when the blocks are entered.
 	 */
-	error_info_text = "while initializing local variables to NULL";
+	estate.err_text = "while initializing local variables to NULL";
 	for (i = estate.found_varno; i < estate.ndatums; i++)
 	{
 		switch (estate.datums[i]->dtype)
@@ -686,12 +611,12 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
 	/*
 	 * Now call the toplevel block of statements
 	 */
-	error_info_text = NULL;
-	error_info_stmt = (PLpgSQL_stmt *) (func->action);
+	estate.err_text = NULL;
+	estate.err_stmt = (PLpgSQL_stmt *) (func->action);
 	if (exec_stmt_block(&estate, func->action) != PLPGSQL_RC_RETURN)
 	{
-		error_info_stmt = NULL;
-		error_info_text = "at END of toplevel PL block";
+		estate.err_stmt = NULL;
+		estate.err_text = "at END of toplevel PL block";
 		elog(ERROR, "control reaches end of trigger procedure without RETURN");
 	}
 
@@ -723,12 +648,9 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
 	exec_eval_cleanup(&estate);
 
 	/*
-	 * Restore the previous error info and elog() jump target
+	 * Pop the error context stack
 	 */
-	error_info_func = save_efunc;
-	error_info_stmt = save_estmt;
-	error_info_text = save_etext;
-	memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
+	error_context_stack = plerrcontext.previous;
 
 	/*
 	 * Return the triggers result
@@ -737,6 +659,37 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
 }
 
 
+/*
+ * error context callback to let us supply a call-stack traceback
+ */
+static void
+plpgsql_exec_error_callback(void *arg)
+{
+	PLpgSQL_execstate *estate = (PLpgSQL_execstate *) arg;
+
+	/* safety check, shouldn't happen */
+	if (estate->err_func == NULL)
+		return;
+
+	/* if we are doing RAISE, don't report its location */
+	if (estate->err_text == raise_skip_msg)
+		return;
+
+	if (estate->err_stmt != NULL)
+		errcontext("PL/pgSQL function %s line %d at %s",
+				   estate->err_func->fn_name,
+				   estate->err_stmt->lineno,
+				   plpgsql_stmt_typename(estate->err_stmt));
+	else if (estate->err_text != NULL)
+		errcontext("PL/pgSQL function %s %s",
+				   estate->err_func->fn_name,
+				   estate->err_text);
+	else
+		errcontext("PL/pgSQL function %s",
+				   estate->err_func->fn_name);
+}
+
+
 /* ----------
  * Support functions for copying local execution variables
  * ----------
@@ -909,8 +862,8 @@ exec_stmt(PLpgSQL_execstate * estate, PLpgSQL_stmt * stmt)
 	PLpgSQL_stmt *save_estmt;
 	int			rc = -1;
 
-	save_estmt = error_info_stmt;
-	error_info_stmt = stmt;
+	save_estmt = estate->err_stmt;
+	estate->err_stmt = stmt;
 
 	CHECK_FOR_INTERRUPTS();
 
@@ -997,12 +950,12 @@ exec_stmt(PLpgSQL_execstate * estate, PLpgSQL_stmt * stmt)
 			break;
 
 		default:
-			error_info_stmt = save_estmt;
+			estate->err_stmt = save_estmt;
 			elog(ERROR, "unknown cmdtype %d in exec_stmt",
 				 stmt->cmd_type);
 	}
 
-	error_info_stmt = save_estmt;
+	estate->err_stmt = save_estmt;
 
 	return rc;
 }
@@ -1854,15 +1807,15 @@ exec_stmt_raise(PLpgSQL_execstate * estate, PLpgSQL_stmt_raise * stmt)
 	}
 
 	/*
-	 * Now suppress debug info and throw the elog()
+	 * Throw the error (may or may not come back)
 	 */
-	if (stmt->elog_level == ERROR)
-	{
-		error_info_func = NULL;
-		error_info_stmt = NULL;
-		error_info_text = NULL;
-	}
-	elog(stmt->elog_level, "%s", plpgsql_dstring_get(&ds));
+	estate->err_text = raise_skip_msg; /* suppress traceback of raise */
+
+	ereport(stmt->elog_level,
+			(errmsg_internal("%s", plpgsql_dstring_get(&ds))));
+
+	estate->err_text = NULL;	/* un-suppress... */
+
 	plpgsql_dstring_free(&ds);
 
 	return PLPGSQL_RC_OK;
@@ -1905,6 +1858,10 @@ plpgsql_estate_setup(PLpgSQL_execstate * estate,
 	estate->eval_processed = 0;
 	estate->eval_lastoid = InvalidOid;
 	estate->eval_econtext = NULL;
+
+	estate->err_func = func;
+	estate->err_stmt = NULL;
+	estate->err_text = NULL;
 }
 
 /* ----------
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 9706db602c206a743e91d57c1c08b6c2c9b9a885..3756d94c911de447049ac7410c683846ab030dc7 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -3,7 +3,7 @@
  *			  procedural language
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.33 2003/03/25 03:16:41 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.34 2003/04/24 21:16:44 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -552,6 +552,11 @@ typedef struct
 	uint32		eval_processed;
 	Oid			eval_lastoid;
 	ExprContext *eval_econtext;
+
+	/* status information for error context reporting */
+	PLpgSQL_function   *err_func;		/* current func */
+	PLpgSQL_stmt	   *err_stmt;		/* current stmt */
+	const char		   *err_text;		/* additional state info */
 }	PLpgSQL_execstate;
 
 
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 4d1458f891624917093aed3a72c11338370bbeda..26c8c2ed423bb02acf8eebdcc818cd6ea20d67d1 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -994,7 +994,8 @@ ERROR:  Relation "test" has no column "a"
 copy test("........pg.dropped.1........") to stdout;
 ERROR:  Relation "test" has no column "........pg.dropped.1........"
 copy test from stdin;
-ERROR:  copy: line 1, Extra data after last expected column
+ERROR:  Extra data after last expected column
+CONTEXT:  COPY FROM, line 1
 SET autocommit TO 'on';
 select * from test;
  b | c 
diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out
index 983e6bb4a41af71dd2eb726a6110a12953d6728f..2d69434b5b8271bb1c58c9817fe51369e727689a 100644
--- a/src/test/regress/expected/copy2.out
+++ b/src/test/regress/expected/copy2.out
@@ -34,14 +34,18 @@ COPY x (a, b, c, d, e, d, c) from stdin;
 ERROR:  Attribute "d" specified more than once
 -- missing data: should fail
 COPY x from stdin;
-ERROR:  copy: line 1, pg_atoi: zero-length string
+ERROR:  pg_atoi: zero-length string
+CONTEXT:  COPY FROM, line 1
 COPY x from stdin;
-ERROR:  copy: line 1, Missing data for column "e"
+ERROR:  Missing data for column "e"
+CONTEXT:  COPY FROM, line 1
 COPY x from stdin;
-ERROR:  copy: line 1, Missing data for column "e"
+ERROR:  Missing data for column "e"
+CONTEXT:  COPY FROM, line 1
 -- extra data: should fail
 COPY x from stdin;
-ERROR:  copy: line 1, Extra data after last expected column
+ERROR:  Extra data after last expected column
+CONTEXT:  COPY FROM, line 1
 SET autocommit TO 'on';
 -- various COPY options: delimiters, oids, NULL string
 COPY x (b, c, d, e) from stdin with oids delimiter ',' null 'x';
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index 13eb14cfa2f0d17df66f8629fa1b81986c2161ad..299bae55f8af15b149ca9e4027686cc7cc513798 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -39,7 +39,8 @@ ERROR:  value too long for type character varying(5)
 INSERT INTO basictest values ('88', 'haha', 'short', '123.1212');    -- Truncate numeric
 -- Test copy
 COPY basictest (testvarchar) FROM stdin; -- fail
-ERROR:  copy: line 1, value too long for type character varying(5)
+ERROR:  value too long for type character varying(5)
+CONTEXT:  COPY FROM, line 1
 SET autocommit TO 'on';
 COPY basictest (testvarchar) FROM stdin;
 select * from basictest;
@@ -126,11 +127,13 @@ ERROR:  ExecInsert: Fail to add null value in not null attribute col3
 INSERT INTO nulltest values ('a', 'b', 'c', NULL, 'd'); -- Good
 -- Test copy
 COPY nulltest FROM stdin; --fail
-ERROR:  copy: line 1, Domain dcheck does not allow NULL values
+ERROR:  Domain dcheck does not allow NULL values
+CONTEXT:  COPY FROM, line 1
 SET autocommit TO 'on';
 -- Last row is bad
 COPY nulltest FROM stdin;
-ERROR:  copy: line 3, CopyFrom: rejected due to CHECK constraint "nulltest_col5" on "nulltest"
+ERROR:  CopyFrom: rejected due to CHECK constraint "nulltest_col5" on "nulltest"
+CONTEXT:  COPY FROM, line 3
 select * from nulltest;
  col1 | col2 | col3 | col4 | col5 
 ------+------+------+------+------
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index b300b695d9133880dc499bff8d84bd45ae5cd178..6ef512a218d9641d0fdd72bd805172822703f855 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -1518,12 +1518,16 @@ insert into PField values ('PF1_1', 'should fail due to unique index');
 ERROR:  Cannot insert a duplicate key into unique index pfield_name
 update PSlot set backlink = 'WS.not.there' where slotname = 'PS.base.a1';
 ERROR:  WS.not.there         does not exist
+CONTEXT:  PL/pgSQL function tg_backlink_a line 16 at assignment
 update PSlot set backlink = 'XX.illegal' where slotname = 'PS.base.a1';
 ERROR:  illegal backlink beginning with XX
+CONTEXT:  PL/pgSQL function tg_backlink_a line 16 at assignment
 update PSlot set slotlink = 'PS.not.there' where slotname = 'PS.base.a1';
 ERROR:  PS.not.there         does not exist
+CONTEXT:  PL/pgSQL function tg_slotlink_a line 16 at assignment
 update PSlot set slotlink = 'XX.illegal' where slotname = 'PS.base.a1';
 ERROR:  illegal slotlink beginning with XX
+CONTEXT:  PL/pgSQL function tg_slotlink_a line 16 at assignment
 insert into HSlot values ('HS', 'base.hub1', 1, '');
 ERROR:  Cannot insert a duplicate key into unique index hslot_name
 insert into HSlot values ('HS', 'base.hub1', 20, '');
diff --git a/src/test/regress/output/constraints.source b/src/test/regress/output/constraints.source
index 7df5e414e87acb631edd59f397b650f02b0ed05a..7a0e335509ece7b540ad93d0a33870310d9488b1 100644
--- a/src/test/regress/output/constraints.source
+++ b/src/test/regress/output/constraints.source
@@ -273,7 +273,8 @@ SELECT '' AS two, * FROM COPY_TBL;
 (2 rows)
 
 COPY COPY_TBL FROM '@abs_srcdir@/data/constrf.data';
-ERROR:  copy: line 2, CopyFrom: rejected due to CHECK constraint "copy_con" on "copy_tbl"
+ERROR:  CopyFrom: rejected due to CHECK constraint "copy_con" on "copy_tbl"
+CONTEXT:  COPY FROM, line 2
 SELECT * FROM COPY_TBL;
  x |       y       | z 
 ---+---------------+---