eelog5.patch
application/octet-stream
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: context
| File | + | − |
|---|---|---|
| doc/src/sgml/config.sgml | 13 | 0 |
| doc/src/sgml/errcodes.sgml | 12 | 0 |
| doc/src/sgml/generate-errcodes-table.pl | 25 | 0 |
| doc/src/sgml/protocol.sgml | 90 | 0 |
| src/backend/access/nbtree/nbtinsert.c | 7 | 0 |
| src/backend/commands/tablecmds.c | 16 | 0 |
| src/backend/commands/typecmds.c | 13 | 0 |
| src/backend/executor/execMain.c | 8 | 0 |
| src/backend/executor/execQual.c | 38 | 0 |
| src/backend/executor/execUtils.c | 6 | 0 |
| src/backend/utils/adt/domains.c | 10 | 0 |
| src/backend/utils/adt/ri_triggers.c | 13 | 0 |
| src/backend/utils/errcodes.txt | 15 | 0 |
| src/backend/utils/error/elog.c | 277 | 0 |
| src/backend/utils/error/Makefile | 1 | 0 |
| src/backend/utils/generate-errcodes.pl | 6 | 0 |
| src/backend/utils/sort/tuplesort.c | 8 | 0 |
| src/include/postgres_ext.h | 8 | 0 |
| src/include/utils/elog.h | 10 | 0 |
| src/include/utils/rel.h | 5 | 0 |
| src/interfaces/libpq/fe-protocol3.c | 33 | 0 |
| src/pl/plpgsql/src/generate-plerrcodes.pl | 2 | 0 |
| src/pl/plpgsql/src/pl_comp.c | 2 | 0 |
| src/pl/plpgsql/src/pl_exec.c | 20 | 0 |
| src/pl/plpgsql/src/plpgsql.h | 3 | 0 |
*** a/doc/src/sgml/config.sgml
--- b/doc/src/sgml/config.sgml
***************
*** 4246,4255 **** FROM pg_stat_activity;
error context,
user query that led to the error (if any and enabled by
<varname>log_min_error_statement</>),
! character count of the error position therein,
! location of the error in the PostgreSQL source code
! (if <varname>log_error_verbosity</> is set to <literal>verbose</>),
! and application name.
Here is a sample table definition for storing CSV-format log output:
<programlisting>
--- 4246,4256 ----
error context,
user query that led to the error (if any and enabled by
<varname>log_min_error_statement</>),
! character count of the error position therein, and, if
! <varname>log_error_verbosity</> is set to <literal>verbose</>,
! column name, table name, constraint name and location of the
! error in the PostgreSQL source code.
! Application name is also listed.
Here is a sample table definition for storing CSV-format log output:
<programlisting>
***************
*** 4277,4282 **** CREATE TABLE postgres_log
--- 4278,4291 ----
query text,
query_pos integer,
location text,
+ column_name text,
+ table_name text,
+ constraint_name text,
+ schema_name text,
+ routine_name text,
+ routine_schema text,
+ routine_oid oid,
+ trigger_name text,
application_name text,
PRIMARY KEY (session_id, session_line_num)
);
*** a/doc/src/sgml/errcodes.sgml
--- b/doc/src/sgml/errcodes.sgml
***************
*** 42,47 ****
--- 42,59 ----
</para>
<para>
+ A small number of error codes listed in
+ <xref linkend="errcodes-table"> are guaranteed to provide
+ additional fields to facilitate applications in recognizing
+ particular domain-specific errors, provided that the the errors are
+ emitted from within the PostgreSQL server itself (it is not
+ strictly guaranteed that an application will not emit an
+ ill-considered error without these field, but with an errorcode
+ documented as providing them). These additional fields are not
+ described by the SQL standard.
+ </para>
+
+ <para>
The symbol shown in the column <quote>Condition Name</quote> is also
the condition name to use in <application>PL/pgSQL</>. Condition
names can be written in either upper or lower case. (Note that
*** a/doc/src/sgml/generate-errcodes-table.pl
--- b/doc/src/sgml/generate-errcodes-table.pl
***************
*** 41,46 **** while (<$errcodes>)
--- 41,71 ----
next;
}
+ # Emit requirement headers
+ if (/^Requirement:/)
+ {
+ # Replace the Requirement: string
+ s/^Requirement: /Provides: /;
+
+ # Replace "unused"
+ if (!s/unused/\(no fields\)/)
+ {
+ # Use literal for field names if appropriate
+ s/: (.*)/: <symbol>$1<\/symbol>/;
+ }
+
+ # Escape dashes for SGML
+ s/-/—/;
+
+ print "\n\n";
+ print "<row>\n";
+ print "<entry spanname=\"span12\">";
+ print "$_</entry>\n";
+ print "</row>\n";
+
+ next;
+ }
+
die unless /^([^\s]{5})\s+([EWS])\s+([^\s]+)(?:\s+)?([^\s]+)?/;
(my $sqlstate, my $type, my $errcode_macro, my $condition_name) =
*** a/doc/src/sgml/protocol.sgml
--- b/doc/src/sgml/protocol.sgml
***************
*** 4786,4791 **** message.
--- 4786,4881 ----
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>
+ <literal>c</>
+ </term>
+ <listitem>
+ <para>
+ Column name: the name of the column associated with the the
+ error, if any. This must be a column within the table of the
+ Table name field.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <literal>t</>
+ </term>
+ <listitem>
+ <para>
+ Table name: the name of the table associated with the error,
+ if any.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <literal>n</>
+ </term>
+ <listitem>
+ <para>
+ Constraint name: the name of the constraint associated with
+ the error, if any. Note that NOT NULL constraints are not
+ cataloged, and as such will never have a constraint name,
+ though in the case of NOT NULL constraints on columns the
+ column name will be available. This value is not guaranteed
+ to uniquely identify the constraint. Even when combined with
+ other fields, constraint name will not unambiguously identify
+ the constraint in all cases, since it is possible for the
+ constraint to be in a different schema to the table associated
+ with the error, or for the constraint to not be associated
+ with a table at all.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <literal>r</>
+ </term>
+ <listitem>
+ <para>
+ Routine name: the name of routine that reports error.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <literal>u</>
+ </term>
+ <listitem>
+ <para>
+ Routine schema: the schema of routine that reports error.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <literal>O</>
+ </term>
+ <listitem>
+ <para>
+ Routine oid: the object id of routine that reports error.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <literal>T</>
+ </term>
+ <listitem>
+ <para>
+ Trigger name: the name of trigger that caused error.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
<para>
*** a/src/backend/access/nbtree/nbtinsert.c
--- b/src/backend/access/nbtree/nbtinsert.c
***************
*** 393,399 **** _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
RelationGetRelationName(rel)),
errdetail("Key %s already exists.",
BuildIndexValueDescription(rel,
! values, isnull))));
}
}
else if (all_dead)
--- 393,401 ----
RelationGetRelationName(rel)),
errdetail("Key %s already exists.",
BuildIndexValueDescription(rel,
! values, isnull)),
! errtable(heapRel),
! errconstraint(RelationGetRelationName(rel))));
}
}
else if (all_dead)
***************
*** 455,461 **** _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("failed to re-find tuple within index \"%s\"",
RelationGetRelationName(rel)),
! errhint("This may be because of a non-immutable index expression.")));
if (nbuf != InvalidBuffer)
_bt_relbuf(rel, nbuf);
--- 457,464 ----
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("failed to re-find tuple within index \"%s\"",
RelationGetRelationName(rel)),
! errhint("This may be because of a non-immutable index expression."),
! errtable(heapRel)));
if (nbuf != InvalidBuffer)
_bt_relbuf(rel, nbuf);
***************
*** 533,539 **** _bt_findinsertloc(Relation rel,
RelationGetRelationName(rel)),
errhint("Values larger than 1/3 of a buffer page cannot be indexed.\n"
"Consider a function index of an MD5 hash of the value, "
! "or use full text indexing.")));
/*----------
* If we will need to split the page to put the item on this page,
--- 536,543 ----
RelationGetRelationName(rel)),
errhint("Values larger than 1/3 of a buffer page cannot be indexed.\n"
"Consider a function index of an MD5 hash of the value, "
! "or use full text indexing."),
! errtable(heapRel)));
/*----------
* If we will need to split the page to put the item on this page,
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 3819,3828 **** ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
int attn = lfirst_int(l);
if (heap_attisnull(tuple, attn + 1))
ereport(ERROR,
(errcode(ERRCODE_NOT_NULL_VIOLATION),
errmsg("column \"%s\" contains null values",
! NameStr(newTupDesc->attrs[attn]->attname))));
}
foreach(l, tab->constraints)
--- 3819,3835 ----
int attn = lfirst_int(l);
if (heap_attisnull(tuple, attn + 1))
+ {
+ Form_pg_attribute att = newTupDesc->attrs[attn];
+
ereport(ERROR,
(errcode(ERRCODE_NOT_NULL_VIOLATION),
errmsg("column \"%s\" contains null values",
! NameStr(att->attname)),
! (newrel) ?
! errtablecol(newrel, NameStr(att->attname)) :
! errtablecol(oldrel, NameStr(att->attname))));
! }
}
foreach(l, tab->constraints)
***************
*** 3836,3842 **** ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("check constraint \"%s\" is violated by some row",
! con->name)));
break;
case CONSTR_FOREIGN:
/* Nothing to do here */
--- 3843,3853 ----
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("check constraint \"%s\" is violated by some row",
! con->name),
! (newrel) ?
! errtable(newrel) :
! errtable(oldrel),
! errconstraint(con->name)));
break;
case CONSTR_FOREIGN:
/* Nothing to do here */
***************
*** 6653,6659 **** validateCheckConstraint(Relation rel, HeapTuple constrtup)
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("check constraint \"%s\" is violated by some row",
! NameStr(constrForm->conname))));
ResetExprContext(econtext);
}
--- 6664,6672 ----
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("check constraint \"%s\" is violated by some row",
! NameStr(constrForm->conname)),
! errtable(rel),
! errconstraint(NameStr(constrForm->conname))));
ResetExprContext(econtext);
}
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
***************
*** 98,104 **** static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
static Oid findRangeSubOpclass(List *opcname, Oid subtype);
static Oid findRangeCanonicalFunction(List *procname, Oid typeOid);
static Oid findRangeSubtypeDiffFunction(List *procname, Oid subtype);
! static void validateDomainConstraint(Oid domainoid, char *ccbin);
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
static void checkEnumOwner(HeapTuple tup);
static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
--- 98,104 ----
static Oid findRangeSubOpclass(List *opcname, Oid subtype);
static Oid findRangeCanonicalFunction(List *procname, Oid typeOid);
static Oid findRangeSubtypeDiffFunction(List *procname, Oid subtype);
! static void validateDomainConstraint(Oid domainoid, char *ccbin, char *conname);
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
static void checkEnumOwner(HeapTuple tup);
static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
***************
*** 2258,2270 **** AlterDomainNotNull(List *names, bool notNull)
for (i = 0; i < rtc->natts; i++)
{
int attnum = rtc->atts[i];
if (heap_attisnull(tuple, attnum))
ereport(ERROR,
(errcode(ERRCODE_NOT_NULL_VIOLATION),
errmsg("column \"%s\" of table \"%s\" contains null values",
! NameStr(tupdesc->attrs[attnum - 1]->attname),
! RelationGetRelationName(testrel))));
}
}
heap_endscan(scan);
--- 2258,2271 ----
for (i = 0; i < rtc->natts; i++)
{
int attnum = rtc->atts[i];
+ Form_pg_attribute att = tupdesc->attrs[attnum - 1];
if (heap_attisnull(tuple, attnum))
ereport(ERROR,
(errcode(ERRCODE_NOT_NULL_VIOLATION),
errmsg("column \"%s\" of table \"%s\" contains null values",
! NameStr(att->attname), RelationGetRelationName(testrel)),
! errtablecol(testrel, NameStr(att->attname))));
}
}
heap_endscan(scan);
***************
*** 2470,2476 **** AlterDomainAddConstraint(List *names, Node *newConstraint)
* attributes based on the domain the constraint is being added to.
*/
if (!constr->skip_validation)
! validateDomainConstraint(domainoid, ccbin);
/* Clean up */
heap_close(typrel, RowExclusiveLock);
--- 2471,2477 ----
* attributes based on the domain the constraint is being added to.
*/
if (!constr->skip_validation)
! validateDomainConstraint(domainoid, ccbin, constr->conname);
/* Clean up */
heap_close(typrel, RowExclusiveLock);
***************
*** 2555,2561 **** AlterDomainValidateConstraint(List *names, char *constrName)
HeapTupleGetOid(tuple));
conbin = TextDatumGetCString(val);
! validateDomainConstraint(domainoid, conbin);
/*
* Now update the catalog, while we have the door open.
--- 2556,2562 ----
HeapTupleGetOid(tuple));
conbin = TextDatumGetCString(val);
! validateDomainConstraint(domainoid, conbin, constrName);
/*
* Now update the catalog, while we have the door open.
***************
*** 2576,2582 **** AlterDomainValidateConstraint(List *names, char *constrName)
}
static void
! validateDomainConstraint(Oid domainoid, char *ccbin)
{
Expr *expr = (Expr *) stringToNode(ccbin);
List *rels;
--- 2577,2583 ----
}
static void
! validateDomainConstraint(Oid domainoid, char *ccbin, char *conname)
{
Expr *expr = (Expr *) stringToNode(ccbin);
List *rels;
***************
*** 2618,2623 **** validateDomainConstraint(Oid domainoid, char *ccbin)
--- 2619,2625 ----
Datum d;
bool isNull;
Datum conResult;
+ const char *col = NameStr(tupdesc->attrs[attnum - 1]->attname);
d = heap_getattr(tuple, attnum, tupdesc, &isNull);
***************
*** 2632,2639 **** validateDomainConstraint(Oid domainoid, char *ccbin)
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("column \"%s\" of table \"%s\" contains values that violate the new constraint",
! NameStr(tupdesc->attrs[attnum - 1]->attname),
! RelationGetRelationName(testrel))));
}
ResetExprContext(econtext);
--- 2634,2644 ----
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("column \"%s\" of table \"%s\" contains values that violate the new constraint",
! col,
! RelationGetRelationName(testrel)),
! errtablecol(testrel, col),
! conname?
! errconstraint(conname):0));
}
ResetExprContext(econtext);
*** a/src/backend/executor/execMain.c
--- b/src/backend/executor/execMain.c
***************
*** 1522,1535 **** ExecConstraints(ResultRelInfo *resultRelInfo,
for (attrChk = 1; attrChk <= natts; attrChk++)
{
if (rel->rd_att->attrs[attrChk - 1]->attnotnull &&
slot_attisnull(slot, attrChk))
ereport(ERROR,
(errcode(ERRCODE_NOT_NULL_VIOLATION),
errmsg("null value in column \"%s\" violates not-null constraint",
! NameStr(rel->rd_att->attrs[attrChk - 1]->attname)),
errdetail("Failing row contains %s.",
! ExecBuildSlotValueDescription(slot, 64))));
}
}
--- 1522,1538 ----
for (attrChk = 1; attrChk <= natts; attrChk++)
{
+ Form_pg_attribute Chk = rel->rd_att->attrs[attrChk - 1];
+
if (rel->rd_att->attrs[attrChk - 1]->attnotnull &&
slot_attisnull(slot, attrChk))
ereport(ERROR,
(errcode(ERRCODE_NOT_NULL_VIOLATION),
errmsg("null value in column \"%s\" violates not-null constraint",
! NameStr(Chk->attname)),
errdetail("Failing row contains %s.",
! ExecBuildSlotValueDescription(slot, 64)),
! errtablecol(rel, NameStr(Chk->attname))));
}
}
***************
*** 1543,1549 **** ExecConstraints(ResultRelInfo *resultRelInfo,
errmsg("new row for relation \"%s\" violates check constraint \"%s\"",
RelationGetRelationName(rel), failed),
errdetail("Failing row contains %s.",
! ExecBuildSlotValueDescription(slot, 64))));
}
}
--- 1546,1554 ----
errmsg("new row for relation \"%s\" violates check constraint \"%s\"",
RelationGetRelationName(rel), failed),
errdetail("Failing row contains %s.",
! ExecBuildSlotValueDescription(slot, 64)),
! errtable(rel),
! errconstraint(failed)));
}
}
*** a/src/backend/executor/execQual.c
--- b/src/backend/executor/execQual.c
***************
*** 54,59 ****
--- 54,60 ----
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
+ #include "utils/rel.h"
#include "utils/typcache.h"
#include "utils/xml.h"
***************
*** 3864,3869 **** ExecEvalCoerceToDomain(CoerceToDomainState *cstate, ExprContext *econtext,
--- 3865,3876 ----
CoerceToDomain *ctest = (CoerceToDomain *) cstate->xprstate.expr;
Datum result;
ListCell *l;
+ Relation heapRel = NULL;
+ struct EState *estate = econtext->ecxt_estate;
+
+ /* Build relation description, if possible */
+ if (estate && estate->es_result_relation_info)
+ heapRel = estate->es_result_relation_info->ri_RelationDesc;
result = ExecEvalExpr(cstate->arg, econtext, isNull, isDone);
***************
*** 3878,3887 **** ExecEvalCoerceToDomain(CoerceToDomainState *cstate, ExprContext *econtext,
{
case DOM_CONSTRAINT_NOTNULL:
if (*isNull)
! ereport(ERROR,
! (errcode(ERRCODE_NOT_NULL_VIOLATION),
! errmsg("domain %s does not allow null values",
! format_type_be(ctest->resulttype))));
break;
case DOM_CONSTRAINT_CHECK:
{
--- 3885,3904 ----
{
case DOM_CONSTRAINT_NOTNULL:
if (*isNull)
! {
! if (heapRel)
! ereport(ERROR,
! (errcode(ERRCODE_NOT_NULL_VIOLATION),
! errmsg("domain %s does not allow null values",
! format_type_be(ctest->resulttype)),
! errtable(heapRel)));
!
! else
! ereport(ERROR,
! (errcode(ERRCODE_NOT_NULL_VIOLATION),
! errmsg("domain %s does not allow null values",
! format_type_be(ctest->resulttype))));
! }
break;
case DOM_CONSTRAINT_CHECK:
{
***************
*** 3907,3917 **** ExecEvalCoerceToDomain(CoerceToDomainState *cstate, ExprContext *econtext,
if (!conIsNull &&
!DatumGetBool(conResult))
! ereport(ERROR,
! (errcode(ERRCODE_CHECK_VIOLATION),
! errmsg("value for domain %s violates check constraint \"%s\"",
! format_type_be(ctest->resulttype),
! con->name)));
econtext->domainValue_datum = save_datum;
econtext->domainValue_isNull = save_isNull;
--- 3924,3946 ----
if (!conIsNull &&
!DatumGetBool(conResult))
! {
! if (heapRel)
! ereport(ERROR,
! (errcode(ERRCODE_CHECK_VIOLATION),
! errmsg("value for domain %s violates check constraint \"%s\"",
! format_type_be(ctest->resulttype),
! con->name),
! errtable(heapRel),
! errconstraint(con->name)));
! else
! ereport(ERROR,
! (errcode(ERRCODE_CHECK_VIOLATION),
! errmsg("value for domain %s violates check constraint \"%s\"",
! format_type_be(ctest->resulttype),
! con->name),
! errconstraint(con->name)));
! }
econtext->domainValue_datum = save_datum;
econtext->domainValue_isNull = save_isNull;
*** a/src/backend/executor/execUtils.c
--- b/src/backend/executor/execUtils.c
***************
*** 1307,1320 **** retry:
errmsg("could not create exclusion constraint \"%s\"",
RelationGetRelationName(index)),
errdetail("Key %s conflicts with key %s.",
! error_new, error_existing)));
else
ereport(ERROR,
(errcode(ERRCODE_EXCLUSION_VIOLATION),
errmsg("conflicting key value violates exclusion constraint \"%s\"",
RelationGetRelationName(index)),
errdetail("Key %s conflicts with existing key %s.",
! error_new, error_existing)));
}
index_endscan(index_scan);
--- 1307,1324 ----
errmsg("could not create exclusion constraint \"%s\"",
RelationGetRelationName(index)),
errdetail("Key %s conflicts with key %s.",
! error_new, error_existing),
! errtable(heap),
! errconstraint(RelationGetRelationName(index))));
else
ereport(ERROR,
(errcode(ERRCODE_EXCLUSION_VIOLATION),
errmsg("conflicting key value violates exclusion constraint \"%s\"",
RelationGetRelationName(index)),
errdetail("Key %s conflicts with existing key %s.",
! error_new, error_existing),
! errtable(heap),
! errconstraint(RelationGetRelationName(index))));
}
index_endscan(index_scan);
*** a/src/backend/utils/adt/domains.c
--- b/src/backend/utils/adt/domains.c
***************
*** 36,41 ****
--- 36,42 ----
#include "lib/stringinfo.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
+ #include "utils/rel.h"
/*
***************
*** 123,132 **** domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
--- 124,140 ----
{
case DOM_CONSTRAINT_NOTNULL:
if (isnull)
+ {
+ /*
+ * We don't provide errtable here, because it is
+ * unavailable, though we require it anywhere where it is
+ * available in principle.
+ */
ereport(ERROR,
(errcode(ERRCODE_NOT_NULL_VIOLATION),
errmsg("domain %s does not allow null values",
format_type_be(my_extra->domain_type))));
+ }
break;
case DOM_CONSTRAINT_CHECK:
{
***************
*** 163,169 **** domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("value for domain %s violates check constraint \"%s\"",
format_type_be(my_extra->domain_type),
! con->name)));
break;
}
default:
--- 171,178 ----
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("value for domain %s violates check constraint \"%s\"",
format_type_be(my_extra->domain_type),
! con->name),
! errconstraint(con->name)));
break;
}
default:
*** a/src/backend/utils/adt/ri_triggers.c
--- b/src/backend/utils/adt/ri_triggers.c
***************
*** 339,345 **** RI_FKey_check(TriggerData *trigdata)
errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
RelationGetRelationName(trigdata->tg_relation),
NameStr(riinfo->conname)),
! errdetail("MATCH FULL does not allow mixing of null and nonnull key values.")));
heap_close(pk_rel, RowShareLock);
return PointerGetDatum(NULL);
--- 339,347 ----
errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
RelationGetRelationName(trigdata->tg_relation),
NameStr(riinfo->conname)),
! errdetail("MATCH FULL does not allow mixing of null and nonnull key values."),
! errtable(trigdata->tg_relation),
! errconstraint(NameStr(riinfo->conname))));
heap_close(pk_rel, RowShareLock);
return PointerGetDatum(NULL);
***************
*** 2467,2473 **** RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
RelationGetRelationName(fk_rel),
NameStr(fake_riinfo.conname)),
! errdetail("MATCH FULL does not allow mixing of null and nonnull key values.")));
/*
* We tell ri_ReportViolation we were doing the RI_PLAN_CHECK_LOOKUPPK
--- 2469,2478 ----
errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
RelationGetRelationName(fk_rel),
NameStr(fake_riinfo.conname)),
! errdetail("MATCH FULL does not allow mixing of null and nonnull key values."),
! errtable(fk_rel),
! errconstraint(NameStr(fake_riinfo.conname))));
!
/*
* We tell ri_ReportViolation we were doing the RI_PLAN_CHECK_LOOKUPPK
***************
*** 3219,3225 **** ri_ReportViolation(const RI_ConstraintInfo *riinfo,
NameStr(riinfo->conname)),
errdetail("Key (%s)=(%s) is not present in table \"%s\".",
key_names.data, key_values.data,
! RelationGetRelationName(pk_rel))));
else
ereport(ERROR,
(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
--- 3224,3232 ----
NameStr(riinfo->conname)),
errdetail("Key (%s)=(%s) is not present in table \"%s\".",
key_names.data, key_values.data,
! RelationGetRelationName(pk_rel)),
! errtable(fk_rel),
! errconstraint(NameStr(riinfo->conname))));
else
ereport(ERROR,
(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
***************
*** 3229,3235 **** ri_ReportViolation(const RI_ConstraintInfo *riinfo,
RelationGetRelationName(fk_rel)),
errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
key_names.data, key_values.data,
! RelationGetRelationName(fk_rel))));
}
--- 3236,3244 ----
RelationGetRelationName(fk_rel)),
errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
key_names.data, key_values.data,
! RelationGetRelationName(fk_rel)),
! errtable(pk_rel),
! errconstraint(NameStr(riinfo->conname))));
}
*** a/src/backend/utils/errcodes.txt
--- b/src/backend/utils/errcodes.txt
***************
*** 200,213 **** Section: Class 22 - Data Exception
2200S E ERRCODE_INVALID_XML_COMMENT invalid_xml_comment
2200T E ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION invalid_xml_processing_instruction
Section: Class 23 - Integrity Constraint Violation
!
23000 E ERRCODE_INTEGRITY_CONSTRAINT_VIOLATION integrity_constraint_violation
23001 E ERRCODE_RESTRICT_VIOLATION restrict_violation
23502 E ERRCODE_NOT_NULL_VIOLATION not_null_violation
23503 E ERRCODE_FOREIGN_KEY_VIOLATION foreign_key_violation
23505 E ERRCODE_UNIQUE_VIOLATION unique_violation
23514 E ERRCODE_CHECK_VIOLATION check_violation
23P01 E ERRCODE_EXCLUSION_VIOLATION exclusion_violation
Section: Class 24 - Invalid Cursor State
--- 200,227 ----
2200S E ERRCODE_INVALID_XML_COMMENT invalid_xml_comment
2200T E ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION invalid_xml_processing_instruction
+ # Postgres coding standards mandate that certain fields be available in all
+ # instances for some of the Class 23 errcodes, documented under "Requirement: "
+ # here. Some other errcode's ereport sites may, at their own discretion, make
+ # errcolumn, errtable, errconstraint and errschema fields available too.
+ # Furthermore, it is possible to make some fields available beyond those
+ # formally required at callsites involving these Class 23 errcodes with
+ # "Requirements: ".
Section: Class 23 - Integrity Constraint Violation
! Requirement: unused
23000 E ERRCODE_INTEGRITY_CONSTRAINT_VIOLATION integrity_constraint_violation
+ Requirement: unused
23001 E ERRCODE_RESTRICT_VIOLATION restrict_violation
+ # Note that requirements for ERRCODE_NOT_NULL do not apply to domains:
+ Requirement: schema_name, table_name
23502 E ERRCODE_NOT_NULL_VIOLATION not_null_violation
+ Requirement: schema_name, table_name, constraint_name
23503 E ERRCODE_FOREIGN_KEY_VIOLATION foreign_key_violation
+ Requirement: schema_name, table_name, constraint_name
23505 E ERRCODE_UNIQUE_VIOLATION unique_violation
+ Requirement: constraint_name
23514 E ERRCODE_CHECK_VIOLATION check_violation
+ Requirement: schema_name, table_name, constraint_name
23P01 E ERRCODE_EXCLUSION_VIOLATION exclusion_violation
Section: Class 24 - Invalid Cursor State
*** a/src/backend/utils/error/Makefile
--- b/src/backend/utils/error/Makefile
***************
*** 12,17 **** subdir = src/backend/utils/error
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
! OBJS = assert.o elog.o
include $(top_srcdir)/src/backend/common.mk
--- 12,17 ----
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
! OBJS = assert.o elog.o relerror.o
include $(top_srcdir)/src/backend/common.mk
*** a/src/backend/utils/error/elog.c
--- b/src/backend/utils/error/elog.c
***************
*** 130,135 **** static void write_syslog(int level, const char *line);
--- 130,136 ----
#endif
static void write_console(const char *line, int len);
+ static void set_errdata_field(char **ptr, const char *str);
#ifdef WIN32
extern char *event_source;
***************
*** 477,482 **** errfinish(int dummy,...)
--- 478,497 ----
pfree(edata->context);
if (edata->internalquery)
pfree(edata->internalquery);
+ if (edata->column_name)
+ pfree(edata->column_name);
+ if (edata->table_name)
+ pfree(edata->table_name);
+ if (edata->schema_name)
+ pfree(edata->schema_name);
+ if (edata->constraint_name)
+ pfree(edata->constraint_name);
+ if (edata->routine_name)
+ pfree(edata->routine_name);
+ if (edata->routine_schema)
+ pfree(edata->routine_schema);
+ if (edata->trigger_name)
+ pfree(edata->trigger_name);
errordata_stack_depth--;
***************
*** 1101,1106 **** internalerrquery(const char *query)
--- 1116,1214 ----
}
/*
+ * errroutineid -- set routine_oid of top custom functions
+ */
+ int
+ errroutineid(Oid routineid)
+ {
+ ErrorData *edata = &errordata[errordata_stack_depth];
+
+ /* we don't bother incrementing recursion_depth */
+ CHECK_STACK_DEPTH();
+
+ edata->routine_oid = routineid;
+
+ return 0; /* return value does not matter */
+ }
+
+ /*
+ * err_generic_string -- used to set individual ErrorData string fields.
+ *
+ * Callers should prefer higher-level abstractions such as those within
+ * relerror.c.
+ */
+ int
+ err_generic_string(int field, const char *str)
+ {
+ ErrorData *edata = &errordata[errordata_stack_depth];
+
+ /* we don't bother incrementing recursion_depth */
+ CHECK_STACK_DEPTH();
+
+ switch (field)
+ {
+ case PG_DIAG_MESSAGE_PRIMARY:
+ set_errdata_field(&edata->message, str);
+ break;
+
+ case PG_DIAG_MESSAGE_DETAIL:
+ set_errdata_field(&edata->detail, str);
+ break;
+
+ case PG_DIAG_MESSAGE_HINT:
+ set_errdata_field(&edata->hint, str);
+ break;
+
+ case PG_DIAG_CONTEXT:
+ set_errdata_field(&edata->context, str);
+ break;
+
+ case PG_DIAG_COLUMN_NAME:
+ set_errdata_field(&edata->column_name, str);
+ break;
+
+ case PG_DIAG_TABLE_NAME:
+ set_errdata_field(&edata->table_name, str);
+ break;
+
+ case PG_DIAG_SCHEMA_NAME:
+ set_errdata_field(&edata->schema_name, str);
+ break;
+
+ case PG_DIAG_CONSTRAINT_NAME:
+ set_errdata_field(&edata->constraint_name, str);
+ break;
+
+ case PG_DIAG_ROUTINE_NAME:
+ set_errdata_field(&edata->routine_name, str);
+ break;
+
+ case PG_DIAG_ROUTINE_SCHEMA:
+ set_errdata_field(&edata->routine_schema, str);
+ break;
+
+ case PG_DIAG_TRIGGER_NAME:
+ set_errdata_field(&edata->trigger_name, str);
+ break;
+
+ default:
+ elog(ERROR, "unsupported or unknown ErrorData field id: %d", field);
+ }
+
+ return 0; /* return value does not matter */
+ }
+
+ /*
+ * set_errdata_field --- set an ErrorData string field
+ */
+ static void
+ set_errdata_field(char **ptr, const char *str)
+ {
+ Assert(*ptr == NULL);
+ *ptr = MemoryContextStrdup(ErrorContext, str);
+ }
+
+ /*
* geterrcode --- return the currently set SQLSTATE error code
*
* This is only intended for use in error callback subroutines, since there
***************
*** 1374,1379 **** CopyErrorData(void)
--- 1482,1501 ----
newedata->context = pstrdup(newedata->context);
if (newedata->internalquery)
newedata->internalquery = pstrdup(newedata->internalquery);
+ if (newedata->column_name)
+ newedata->column_name = pstrdup(newedata->column_name);
+ if (newedata->table_name)
+ newedata->table_name = pstrdup(newedata->table_name);
+ if (newedata->schema_name)
+ newedata->schema_name = pstrdup(newedata->schema_name);
+ if (newedata->constraint_name)
+ newedata->constraint_name = pstrdup(newedata->constraint_name);
+ if (newedata->routine_name)
+ newedata->routine_name = pstrdup(newedata->routine_name);
+ if (newedata->routine_schema)
+ newedata->routine_schema = pstrdup(newedata->routine_schema);
+ if (newedata->trigger_name)
+ newedata->trigger_name = pstrdup(newedata->trigger_name);
return newedata;
}
***************
*** 1399,1404 **** FreeErrorData(ErrorData *edata)
--- 1521,1541 ----
pfree(edata->context);
if (edata->internalquery)
pfree(edata->internalquery);
+ if (edata->column_name)
+ pfree(edata->column_name);
+ if (edata->table_name)
+ pfree(edata->table_name);
+ if (edata->schema_name)
+ pfree(edata->schema_name);
+ if (edata->constraint_name)
+ pfree(edata->constraint_name);
+ if (edata->routine_name)
+ pfree(edata->routine_name);
+ if (edata->routine_schema)
+ pfree(edata->routine_schema);
+ if (edata->trigger_name)
+ pfree(edata->trigger_name);
+
pfree(edata);
}
***************
*** 1471,1476 **** ReThrowError(ErrorData *edata)
--- 1608,1627 ----
newedata->context = pstrdup(newedata->context);
if (newedata->internalquery)
newedata->internalquery = pstrdup(newedata->internalquery);
+ if (newedata->column_name)
+ newedata->column_name = pstrdup(newedata->column_name);
+ if (newedata->table_name)
+ newedata->table_name = pstrdup(newedata->table_name);
+ if (newedata->schema_name)
+ newedata->schema_name = pstrdup(newedata->schema_name);
+ if (newedata->constraint_name)
+ newedata->constraint_name = pstrdup(newedata->constraint_name);
+ if (newedata->routine_name)
+ newedata->routine_name = pstrdup(newedata->routine_name);
+ if (newedata->routine_schema)
+ newedata->routine_schema = pstrdup(newedata->routine_schema);
+ if (newedata->trigger_name)
+ newedata->trigger_name = pstrdup(newedata->trigger_name);
recursion_depth--;
PG_RE_THROW();
***************
*** 2275,2282 **** write_csvlog(ErrorData *edata)
edata->filename, edata->lineno);
appendCSVLiteral(&buf, msgbuf.data);
pfree(msgbuf.data);
}
- appendStringInfoChar(&buf, ',');
/* application name */
if (application_name)
--- 2426,2457 ----
edata->filename, edata->lineno);
appendCSVLiteral(&buf, msgbuf.data);
pfree(msgbuf.data);
+ appendStringInfoChar(&buf, ',');
+
+ appendCSVLiteral(&buf, edata->column_name);
+ appendStringInfoChar(&buf, ',');
+
+ appendCSVLiteral(&buf, edata->table_name);
+ appendStringInfoChar(&buf, ',');
+
+ appendCSVLiteral(&buf, edata->schema_name);
+ appendStringInfoChar(&buf, ',');
+
+ appendCSVLiteral(&buf, edata->constraint_name);
+ appendStringInfoChar(&buf, ',');
+
+ appendCSVLiteral(&buf, edata->routine_name);
+ appendStringInfoChar(&buf, ',');
+
+ appendCSVLiteral(&buf, edata->routine_schema);
+ appendStringInfoChar(&buf, ',');
+
+ appendStringInfo(&buf, "%d", edata->routine_oid);
+ appendStringInfoChar(&buf, ',');
+
+ appendCSVLiteral(&buf, edata->trigger_name);
+ appendStringInfoChar(&buf, ',');
}
/* application name */
if (application_name)
***************
*** 2385,2391 **** send_message_to_server_log(ErrorData *edata)
}
if (Log_error_verbosity >= PGERROR_VERBOSE)
{
! /* assume no newlines in funcname or filename... */
if (edata->funcname && edata->filename)
{
log_line_prefix(&buf, edata);
--- 2560,2569 ----
}
if (Log_error_verbosity >= PGERROR_VERBOSE)
{
! /*
! * assume no newlines in funcname, filename, column_name,
! * table_name, constraint_name or schema_name...
! */
if (edata->funcname && edata->filename)
{
log_line_prefix(&buf, edata);
***************
*** 2399,2404 **** send_message_to_server_log(ErrorData *edata)
--- 2577,2630 ----
appendStringInfo(&buf, _("LOCATION: %s:%d\n"),
edata->filename, edata->lineno);
}
+ if (edata->column_name && edata->table_name)
+ {
+ log_line_prefix(&buf, edata);
+ appendStringInfo(&buf, _("COLUMN NAME: %s:%s\n"),
+ edata->table_name, edata->column_name);
+ }
+ else if (edata->table_name)
+ {
+ log_line_prefix(&buf, edata);
+ appendStringInfo(&buf, _("TABLE NAME: %s\n"),
+ edata->table_name);
+ }
+ if (edata->constraint_name)
+ {
+ log_line_prefix(&buf, edata);
+ appendStringInfo(&buf, _("CONSTRAINT NAME: %s\n"),
+ edata->constraint_name);
+ }
+ if (edata->schema_name)
+ {
+ log_line_prefix(&buf, edata);
+ appendStringInfo(&buf, _("SCHEMA NAME: %s\n"),
+ edata->schema_name);
+ }
+ if (edata->routine_name)
+ {
+ log_line_prefix(&buf, edata);
+ appendStringInfo(&buf, _("ROUTINE NAME: %s\n"),
+ edata->routine_name);
+ }
+ if (edata->routine_schema)
+ {
+ log_line_prefix(&buf, edata);
+ appendStringInfo(&buf, _("ROUTINE SCHEMA: %s\n"),
+ edata->routine_schema);
+ }
+ if (edata->routine_oid != InvalidOid)
+ {
+ log_line_prefix(&buf, edata);
+ appendStringInfo(&buf, _("ROUTINE OID: %d\n"),
+ edata->routine_oid);
+ }
+ if (edata->trigger_name)
+ {
+ log_line_prefix(&buf, edata);
+ appendStringInfo(&buf, _("TRIGGER NAME: %s\n"),
+ edata->trigger_name);
+ }
}
}
***************
*** 2695,2700 **** send_message_to_frontend(ErrorData *edata)
--- 2921,2975 ----
err_sendstring(&msgbuf, edata->funcname);
}
+ if (edata->column_name)
+ {
+ pq_sendbyte(&msgbuf, PG_DIAG_COLUMN_NAME);
+ err_sendstring(&msgbuf, edata->column_name);
+ }
+
+ if (edata->table_name)
+ {
+ pq_sendbyte(&msgbuf, PG_DIAG_TABLE_NAME);
+ err_sendstring(&msgbuf, edata->table_name);
+ }
+
+ if (edata->constraint_name)
+ {
+ pq_sendbyte(&msgbuf, PG_DIAG_CONSTRAINT_NAME);
+ err_sendstring(&msgbuf, edata->constraint_name);
+ }
+
+ if (edata->schema_name)
+ {
+ pq_sendbyte(&msgbuf, PG_DIAG_SCHEMA_NAME);
+ err_sendstring(&msgbuf, edata->schema_name);
+ }
+
+ if (edata->routine_name)
+ {
+ pq_sendbyte(&msgbuf, PG_DIAG_ROUTINE_NAME);
+ err_sendstring(&msgbuf, edata->routine_name);
+ }
+
+ if (edata->routine_schema)
+ {
+ pq_sendbyte(&msgbuf, PG_DIAG_ROUTINE_SCHEMA);
+ err_sendstring(&msgbuf, edata->routine_schema);
+ }
+
+ if (edata->routine_oid != InvalidOid)
+ {
+ snprintf(tbuf, sizeof(tbuf), "%d", edata->routine_oid);
+ pq_sendbyte(&msgbuf, PG_DIAG_ROUTINE_OID);
+ err_sendstring(&msgbuf, tbuf);
+ }
+
+ if (edata->trigger_name)
+ {
+ pq_sendbyte(&msgbuf, PG_DIAG_TRIGGER_NAME);
+ err_sendstring(&msgbuf, edata->trigger_name);
+ }
+
pq_sendbyte(&msgbuf, '\0'); /* terminator */
}
else
*** a/src/backend/utils/generate-errcodes.pl
--- b/src/backend/utils/generate-errcodes.pl
***************
*** 28,33 **** while (<$errcodes>)
--- 28,39 ----
print "\n/* $header */\n";
next;
}
+ elsif (/(^Requirement:.*)/)
+ {
+ my $header = $1;
+ print "/* $header */\n";
+ next;
+ }
die "unable to parse errcodes.txt"
unless /^([^\s]{5})\s+[EWS]\s+([^\s]+)/;
*** a/src/backend/utils/sort/tuplesort.c
--- b/src/backend/utils/sort/tuplesort.c
***************
*** 3014,3019 **** comparetup_index_btree(const SortTuple *a, const SortTuple *b,
--- 3014,3020 ----
* for equal keys at the end.
*/
ScanKey scanKey = state->indexScanKey;
+ Relation indexRel = state->indexRel;
IndexTuple tuple1;
IndexTuple tuple2;
int keysz;
***************
*** 3038,3044 **** comparetup_index_btree(const SortTuple *a, const SortTuple *b,
tuple1 = (IndexTuple) a->tuple;
tuple2 = (IndexTuple) b->tuple;
keysz = state->nKeys;
! tupDes = RelationGetDescr(state->indexRel);
scanKey++;
for (nkey = 2; nkey <= keysz; nkey++, scanKey++)
{
--- 3039,3045 ----
tuple1 = (IndexTuple) a->tuple;
tuple2 = (IndexTuple) b->tuple;
keysz = state->nKeys;
! tupDes = RelationGetDescr(indexRel);
scanKey++;
for (nkey = 2; nkey <= keysz; nkey++, scanKey++)
{
***************
*** 3075,3080 **** comparetup_index_btree(const SortTuple *a, const SortTuple *b,
--- 3076,3083 ----
{
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
+ const char *indrelname = RelationGetRelationName(indexRel);
+ Relation heapRel = RelationIdGetRelation(indexRel->rd_index->indrelid);
/*
* Some rather brain-dead implementations of qsort (such as the one in
***************
*** 3088,3097 **** comparetup_index_btree(const SortTuple *a, const SortTuple *b,
ereport(ERROR,
(errcode(ERRCODE_UNIQUE_VIOLATION),
errmsg("could not create unique index \"%s\"",
! RelationGetRelationName(state->indexRel)),
errdetail("Key %s is duplicated.",
! BuildIndexValueDescription(state->indexRel,
! values, isnull))));
}
/*
--- 3091,3101 ----
ereport(ERROR,
(errcode(ERRCODE_UNIQUE_VIOLATION),
errmsg("could not create unique index \"%s\"",
! indrelname),
errdetail("Key %s is duplicated.",
! BuildIndexValueDescription(indexRel, values, isnull)),
! errtable(heapRel),
! errconstraint(indrelname)));
}
/*
*** a/src/include/postgres_ext.h
--- b/src/include/postgres_ext.h
***************
*** 60,64 **** typedef PG_INT64_TYPE pg_int64;
--- 60,72 ----
#define PG_DIAG_SOURCE_FILE 'F'
#define PG_DIAG_SOURCE_LINE 'L'
#define PG_DIAG_SOURCE_FUNCTION 'R'
+ #define PG_DIAG_COLUMN_NAME 'c'
+ #define PG_DIAG_TABLE_NAME 't'
+ #define PG_DIAG_CONSTRAINT_NAME 'n'
+ #define PG_DIAG_SCHEMA_NAME 's'
+ #define PG_DIAG_ROUTINE_NAME 'r'
+ #define PG_DIAG_ROUTINE_SCHEMA 'u'
+ #define PG_DIAG_ROUTINE_OID 'O'
+ #define PG_DIAG_TRIGGER_NAME 'T'
#endif /* POSTGRES_EXT_H */
*** a/src/include/utils/elog.h
--- b/src/include/utils/elog.h
***************
*** 206,211 **** extern int geterrcode(void);
--- 206,213 ----
extern int geterrposition(void);
extern int getinternalerrposition(void);
+ extern int errroutineid(Oid routineid);
+ extern int err_generic_string(int field, const char *str);
/*----------
* Old-style error reporting API: to be used in this way:
***************
*** 338,343 **** typedef struct ErrorData
--- 340,353 ----
char *detail_log; /* detail error message for server log only */
char *hint; /* hint message */
char *context; /* context message */
+ char *column_name; /* name of column */
+ char *table_name; /* name of table */
+ char *constraint_name; /* name of constraint */
+ char *schema_name; /* name of schema */
+ char *routine_name; /* name of routine */
+ char *routine_schema; /* schema of routine */
+ Oid routine_oid; /* oid of routine */
+ char *trigger_name; /* name of trigger */
int cursorpos; /* cursor index into query string */
int internalpos; /* cursor index into internalquery */
char *internalquery; /* text of internally-generated query */
*** a/src/include/utils/rel.h
--- b/src/include/utils/rel.h
***************
*** 398,401 **** typedef struct StdRdOptions
--- 398,406 ----
extern void RelationIncrementReferenceCount(Relation rel);
extern void RelationDecrementReferenceCount(Relation rel);
+ /* routines in utils/error/relerror.c */
+ extern int errtablecol(Relation table, const char *colname);
+ extern int errtable(Relation table);
+ extern int errconstraint(const char *cname);
+
#endif /* REL_H */
*** a/src/interfaces/libpq/fe-protocol3.c
--- b/src/interfaces/libpq/fe-protocol3.c
***************
*** 936,941 **** pqGetErrorNotice3(PGconn *conn, bool isError)
--- 936,974 ----
valf, vall);
appendPQExpBufferChar(&workBuf, '\n');
}
+
+ val = PQresultErrorField(res, PG_DIAG_COLUMN_NAME);
+ if (val)
+ appendPQExpBuffer(&workBuf,
+ libpq_gettext("COLUMN NAME: %s\n"), val);
+ val = PQresultErrorField(res, PG_DIAG_TABLE_NAME);
+ if (val)
+ appendPQExpBuffer(&workBuf,
+ libpq_gettext("TABLE NAME: %s\n"), val);
+ val = PQresultErrorField(res, PG_DIAG_CONSTRAINT_NAME);
+ if (val)
+ appendPQExpBuffer(&workBuf,
+ libpq_gettext("CONSTRAINT NAME: %s\n"), val);
+ val = PQresultErrorField(res, PG_DIAG_SCHEMA_NAME);
+ if (val)
+ appendPQExpBuffer(&workBuf,
+ libpq_gettext("SCHEMA NAME: %s\n"), val);
+ val = PQresultErrorField(res, PG_DIAG_ROUTINE_NAME);
+ if (val)
+ appendPQExpBuffer(&workBuf,
+ libpq_gettext("ROUTINE NAME: %s\n"), val);
+ val = PQresultErrorField(res, PG_DIAG_ROUTINE_SCHEMA);
+ if (val)
+ appendPQExpBuffer(&workBuf,
+ libpq_gettext("ROUTINE SHEMA: %s\n"), val);
+ val = PQresultErrorField(res, PG_DIAG_ROUTINE_OID);
+ if (val)
+ appendPQExpBuffer(&workBuf,
+ libpq_gettext("ROUTINE OID: %s\n"), val);
+ val = PQresultErrorField(res, PG_DIAG_TRIGGER_NAME);
+ if (val)
+ appendPQExpBuffer(&workBuf,
+ libpq_gettext("TRIGGER NAME: %s\n"), val);
}
/*
*** a/src/pl/plpgsql/src/generate-plerrcodes.pl
--- b/src/pl/plpgsql/src/generate-plerrcodes.pl
***************
*** 20,27 **** while (<$errcodes>)
next if /^#/;
next if /^\s*$/;
! # Skip section headers
! next if /^Section:/;
die unless /^([^\s]{5})\s+([EWS])\s+([^\s]+)(?:\s+)?([^\s]+)?/;
--- 20,27 ----
next if /^#/;
next if /^\s*$/;
! # Skip section headers, and requirement notes
! next if /^Section:/ or /^Requirement:/;
die unless /^([^\s]{5})\s+([EWS])\s+([^\s]+)(?:\s+)?([^\s]+)?/;
*** a/src/pl/plpgsql/src/pl_comp.c
--- b/src/pl/plpgsql/src/pl_comp.c
***************
*** 344,349 **** do_compile(FunctionCallInfo fcinfo,
--- 344,351 ----
compile_tmp_cxt = MemoryContextSwitchTo(func_cxt);
function->fn_signature = format_procedure(fcinfo->flinfo->fn_oid);
+ function->fn_name = get_func_name(fcinfo->flinfo->fn_oid);
+ function->fn_schema = get_namespace_name(get_func_namespace(fcinfo->flinfo->fn_oid));
function->fn_oid = fcinfo->flinfo->fn_oid;
function->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
function->fn_tid = procTup->t_self;
*** a/src/pl/plpgsql/src/pl_exec.c
--- b/src/pl/plpgsql/src/pl_exec.c
***************
*** 583,588 **** plpgsql_exec_trigger(PLpgSQL_function *func,
--- 583,590 ----
var->isnull = false;
var->freeval = true;
+ estate.trigger_name = trigdata->tg_trigger->tgname;
+
var = (PLpgSQL_var *) (estate.datums[func->tg_when_varno]);
if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
var->value = CStringGetTextDatum("BEFORE");
***************
*** 917,922 **** plpgsql_exec_error_callback(void *arg)
--- 919,941 ----
else
errcontext("PL/pgSQL function %s",
estate->func->fn_signature);
+
+ /*
+ * if execution of this function is on top of error_context_stack,
+ * then set enhanced diagnostics fields related to function.
+ */
+ if (error_context_stack->callback == plpgsql_exec_error_callback &&
+ (PLpgSQL_execstate *) error_context_stack->arg == estate)
+ {
+ PLpgSQL_function *func = estate->func;
+
+ errroutineid(func->fn_oid);
+ err_generic_string(PG_DIAG_ROUTINE_NAME, func->fn_name);
+ err_generic_string(PG_DIAG_ROUTINE_SCHEMA, func->fn_schema);
+
+ if (func->fn_is_trigger != PLPGSQL_NOT_TRIGGER)
+ err_generic_string(PG_DIAG_TRIGGER_NAME, estate->trigger_name);
+ }
}
***************
*** 3053,3058 **** plpgsql_estate_setup(PLpgSQL_execstate *estate,
--- 3072,3078 ----
estate->err_stmt = NULL;
estate->err_text = NULL;
+ estate->trigger_name = NULL;
estate->plugin_info = NULL;
*** a/src/pl/plpgsql/src/plpgsql.h
--- b/src/pl/plpgsql/src/plpgsql.h
***************
*** 686,691 **** typedef enum PLpgSQL_trigtype
--- 686,693 ----
typedef struct PLpgSQL_function
{ /* Complete compiled function */
char *fn_signature;
+ char *fn_schema;
+ char *fn_name; /* fields for enhanced diagnostics */
Oid fn_oid;
TransactionId fn_xmin;
ItemPointerData fn_tid;
***************
*** 754,759 **** typedef struct PLpgSQL_execstate
--- 756,762 ----
char *exitlabel; /* the "target" label of the current EXIT or
* CONTINUE stmt, if any */
ErrorData *cur_error; /* current exception handler's error */
+ char *trigger_name; /* name of trigger for enhanced diagnostics */
Tuplestorestate *tuple_store; /* SRFs accumulate results here */
MemoryContext tuple_store_cxt;