# 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
[$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
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
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
AC_C_STRINGIZE
PGAC_C_SIGNED
AC_C_VOLATILE
+PGAC_C_FUNCNAME_SUPPORT
AC_STRUCT_TIMEZONE
PGAC_UNION_SEMUN
PGAC_STRUCT_SOCKADDR_UN
-<!-- $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>
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
* 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 $
*
*-------------------------------------------------------------------------
*/
{
if (l != names)
appendStringInfoChar(&string, '.');
- appendStringInfo(&string, "%s", strVal(lfirst(l)));
+ appendStringInfoString(&string, strVal(lfirst(l)));
}
return string.data;
{
if (l != names)
appendStringInfoChar(&string, '.');
- appendStringInfo(&string, "%s", quote_identifier(strVal(lfirst(l))));
+ appendStringInfoString(&string, quote_identifier(strVal(lfirst(l))));
}
return string.data;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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
}
+/*
+ * 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.
*/
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;
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;
/*
* Done, clean up
*/
- copy_lineno = 0;
+ error_context_stack = errcontext.previous;
MemoryContextSwitchTo(oldcontext);
* 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 $
*
*-------------------------------------------------------------------------
*/
if (plan == NULL)
{
- appendStringInfo(str, "\n");
+ appendStringInfoChar(str, '\n');
return;
}
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)
{
appendStringInfo(str, " (never executed)");
}
}
- appendStringInfo(str, "\n");
+ appendStringInfoChar(str, '\n');
/* quals, sort keys, etc */
switch (nodeTag(plan))
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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);
* 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 $
*
*-------------------------------------------------------------------------
*/
/*
* 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));
}
/*
* 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.
*
*
* 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*
/* 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) \
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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)
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
{
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));
}
/*
* LookupTypeName
*/
if (typename->pct_type)
- appendStringInfo(&string, "%%TYPE");
+ appendStringInfoString(&string, "%TYPE");
if (typename->arrayBounds != NIL)
- appendStringInfo(&string, "[]");
+ appendStringInfoString(&string, "[]");
return string.data;
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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;
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)));
}
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);
*
*
* 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
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.
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");
}
/*
* 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.
*
constraintId);
/* Append the constraint source */
- appendStringInfo(&buf, DatumGetCString(DirectFunctionCall1(textout, val)));
+ appendStringInfoString(&buf, DatumGetCString(DirectFunctionCall1(textout, val)));
break;
}
*/
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 */
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
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);
/*-------------------------------------------------------------------------
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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
/*
* ... 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;
}
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
* 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;
* 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
}
/*
- * 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;
/*
siglongjmp(Warn_restart, 1);
}
- if (lev == PANIC)
+ if (elevel >= PANIC)
{
/*
* Serious crash time. Postmaster will observe nonzero process
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])
{
/*
*/
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);
* 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,
*/
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;
}
{
char buf[PG_SYSLOG_LIMIT + 1];
int buflen;
- int l;
int i;
/* if we start at a newline, move ahead one char */
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';
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
}
+/*
+ * 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 */
}
-
+/*
+ * 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;
+}
* 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 $
*
*-------------------------------------------------------------------------
*/
#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 */
* 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 $
*
*-------------------------------------------------------------------------
*/
/*------------------------
* 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.
* 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 $
*
*-------------------------------------------------------------------------
*/
/* 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 */
/* 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
* 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 $
*
*-------------------------------------------------------------------------
*/
/*#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 */
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/* 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;
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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.
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;
}
else if (id == 'N')
{
- if (getNotice(conn))
+ if (pqGetErrorNotice(conn, false))
return;
}
else if (conn->asyncStatus != PGASYNC_BUSY)
*/
if (id == 'E')
{
- if (getNotice(conn))
+ if (pqGetErrorNotice(conn, false /* treat as notice */))
return;
}
else
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 */
/*
- * 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(¬iceBuf);
- if (pqGets(¬iceBuf, conn))
+ /*
+ * Read the fields and save into res.
+ */
+ for (;;)
{
- termPQExpBuffer(¬iceBuf);
- 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(¬iceBuf);
+
+ /*
+ * 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;
}
/*
}
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 */
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 */
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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.
/* 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.
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
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];
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 === */
* 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.
*
PLpgSQL_function *plpgsql_curr_compile;
+static void plpgsql_compile_error_callback(void *arg);
static PLpgSQL_row *build_rowtype(Oid classOid);
PLpgSQL_rec *rec;
int i;
int arg_varnos[FUNC_MAX_ARGS];
- sigjmp_buf save_restart;
+ ErrorContextCallback plerrcontext;
/*
* Lookup the pg_proc tuple by Oid
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
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
}
+/*
+ * 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
* 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.
*
#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);
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)
/*
* 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];
* 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)
/*
* 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;
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
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)
* 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;
* 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)
/*
* 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");
}
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
}
+/*
+ * 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
* ----------
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();
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;
}
}
/*
- * 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;
estate->eval_processed = 0;
estate->eval_lastoid = InvalidOid;
estate->eval_econtext = NULL;
+
+ estate->err_func = func;
+ estate->err_stmt = NULL;
+ estate->err_text = NULL;
}
/* ----------
* 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.
*
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;
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
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';
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;
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
------+------+------+------+------
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, '');
(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
---+---------------+---