diff doc/src/sgml/errcodes.sgml
index 16cb6c7..7c153b5
*** a/doc/src/sgml/errcodes.sgml
--- b/doc/src/sgml/errcodes.sgml
***************
*** 42,47 ****
--- 42,58 ----
+ A small number of error codes listed in
+ 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).
+
+
+
The symbol shown in the column Condition Name
is also
the condition name to use in PL/pgSQL>. Condition
names can be written in either upper or lower case. (Note that
diff doc/src/sgml/generate-errcodes-table.pl
index 2ed12ce..7535395
*** a/doc/src/sgml/generate-errcodes-table.pl
--- b/doc/src/sgml/generate-errcodes-table.pl
*************** while (<$errcodes>)
*** 41,46 ****
--- 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/: (.*)/: $1<\/symbol>/;
+ }
+
+ # Escape dashes for SGML
+ s/-/—/;
+
+ print "\n\n";
+ print "\n";
+ print "";
+ print "$_\n";
+ print "
\n";
+
+ next;
+ }
+
die unless /^([^\s]{5})\s+([EWS])\s+([^\s]+)(?:\s+)?([^\s]+)?/;
(my $sqlstate, my $type, my $errcode_macro, my $condition_name) =
diff doc/src/sgml/protocol.sgml
index e14627c..9286dfc
*** a/doc/src/sgml/protocol.sgml
--- b/doc/src/sgml/protocol.sgml
*************** message.
*** 4786,4791 ****
--- 4786,4849 ----
+
+
+ c>
+
+
+
+ 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.
+
+
+
+
+
+
+ t>
+
+
+
+ Table name: the name of the table associated with the error,
+ if any.
+
+
+
+
+
+
+ n>
+
+
+
+ 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.
+
+
+
+
+
+
+ s>
+
+
+
+ Schema name: the name of schema that contains the table
+ associated with the error, if any.
+
+
+
+
diff src/backend/access/nbtree/nbtinsert.c
index 4432bb1..25d4f09
*** a/src/backend/access/nbtree/nbtinsert.c
--- b/src/backend/access/nbtree/nbtinsert.c
*************** _bt_check_unique(Relation rel, IndexTupl
*** 393,399 ****
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)
*************** _bt_check_unique(Relation rel, IndexTupl
*** 455,461 ****
(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);
*************** _bt_findinsertloc(Relation rel,
*** 533,539 ****
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,
diff src/backend/commands/tablecmds.c
index cad8311..e3a33b6
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
*************** ATRewriteTable(AlteredTableInfo *tab, Oi
*** 3819,3828 ****
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)
*************** ATRewriteTable(AlteredTableInfo *tab, Oi
*** 3836,3842 ****
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 */
*************** validateCheckConstraint(Relation rel, He
*** 6653,6659 ****
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);
}
diff src/backend/commands/typecmds.c
index 7a72416..e88d306
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
*************** static Oid findTypeAnalyzeFunction(List
*** 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);
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,
*************** AlterDomainNotNull(List *names, bool not
*** 2262,2274 ****
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);
--- 2262,2275 ----
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);
*************** AlterDomainAddConstraint(List *names, No
*** 2478,2484 ****
* attributes based on the domain the constraint is being added to.
*/
if (!constr->skip_validation)
! validateDomainConstraint(domainoid, ccbin);
/* Clean up */
heap_close(typrel, RowExclusiveLock);
--- 2479,2485 ----
* 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);
*************** AlterDomainValidateConstraint(List *name
*** 2565,2571 ****
HeapTupleGetOid(tuple));
conbin = TextDatumGetCString(val);
! validateDomainConstraint(domainoid, conbin);
/*
* Now update the catalog, while we have the door open.
--- 2566,2572 ----
HeapTupleGetOid(tuple));
conbin = TextDatumGetCString(val);
! validateDomainConstraint(domainoid, conbin, constrName);
/*
* Now update the catalog, while we have the door open.
*************** AlterDomainValidateConstraint(List *name
*** 2588,2594 ****
}
static void
! validateDomainConstraint(Oid domainoid, char *ccbin)
{
Expr *expr = (Expr *) stringToNode(ccbin);
List *rels;
--- 2589,2595 ----
}
static void
! validateDomainConstraint(Oid domainoid, char *ccbin, char *conname)
{
Expr *expr = (Expr *) stringToNode(ccbin);
List *rels;
*************** validateDomainConstraint(Oid domainoid,
*** 2630,2635 ****
--- 2631,2637 ----
Datum d;
bool isNull;
Datum conResult;
+ const char *col = NameStr(tupdesc->attrs[attnum - 1]->attname);
d = heap_getattr(tuple, attnum, tupdesc, &isNull);
*************** validateDomainConstraint(Oid domainoid,
*** 2644,2651 ****
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);
--- 2646,2656 ----
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);
diff src/backend/executor/execMain.c
index 9d5d829..f94e18f
*** a/src/backend/executor/execMain.c
--- b/src/backend/executor/execMain.c
*************** ExecConstraints(ResultRelInfo *resultRel
*** 1522,1535 ****
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))));
}
}
*************** ExecConstraints(ResultRelInfo *resultRel
*** 1543,1549 ****
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)));
}
}
diff src/backend/executor/execQual.c
index d9981a5..3bacd55
*** 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"
*************** ExecEvalCoerceToDomain(CoerceToDomainSta
*** 3863,3870 ****
--- 3864,3877 ----
{
CoerceToDomain *ctest = (CoerceToDomain *) cstate->xprstate.expr;
Datum result;
+ Relation heapRel = NULL;
+ struct EState *estate = econtext->ecxt_estate;
ListCell *l;
+ /* 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);
if (isDone && *isDone == ExprEndResult)
*************** ExecEvalCoerceToDomain(CoerceToDomainSta
*** 3878,3887 ****
{
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:
{
*************** ExecEvalCoerceToDomain(CoerceToDomainSta
*** 3907,3917 ****
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;
diff src/backend/executor/execUtils.c
index 9206195..114a8af
*** a/src/backend/executor/execUtils.c
--- b/src/backend/executor/execUtils.c
*************** retry:
*** 1307,1320 ****
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);
diff src/backend/utils/adt/domains.c
index 9d2fb1e..8a3d645
*** 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"
/*
*************** domain_check_input(Datum value, bool isn
*** 123,128 ****
--- 124,134 ----
{
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",
*************** domain_check_input(Datum value, bool isn
*** 163,169 ****
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("value for domain %s violates check constraint \"%s\"",
format_type_be(my_extra->domain_type),
! con->name)));
break;
}
default:
--- 169,176 ----
(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:
diff src/backend/utils/adt/ri_triggers.c
index 601d5ec..09396e6
*** a/src/backend/utils/adt/ri_triggers.c
--- b/src/backend/utils/adt/ri_triggers.c
*************** RI_FKey_check(TriggerData *trigdata)
*** 339,345 ****
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);
*************** RI_Initial_Check(Trigger *trigger, Relat
*** 2467,2473 ****
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
*************** ri_ReportViolation(const RI_ConstraintIn
*** 3219,3225 ****
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),
*************** ri_ReportViolation(const RI_ConstraintIn
*** 3229,3235 ****
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))));
}
diff src/backend/utils/errcodes.txt
index 0b129b1..e53c94e
*** a/src/backend/utils/errcodes.txt
--- b/src/backend/utils/errcodes.txt
*************** Section: Class 22 - Data Exception
*** 200,213 ****
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
diff src/backend/utils/error/Makefile
index 4c313b7..c2ba4d0
*** a/src/backend/utils/error/Makefile
--- b/src/backend/utils/error/Makefile
*************** subdir = src/backend/utils/error
*** 12,17 ****
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
diff src/backend/utils/error/elog.c
index e710f22..a28e4bd
*** a/src/backend/utils/error/elog.c
--- b/src/backend/utils/error/elog.c
*************** static void write_syslog(int level, cons
*** 130,135 ****
--- 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;
*************** errfinish(int dummy,...)
*** 477,482 ****
--- 478,491 ----
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);
errordata_stack_depth--;
*************** internalerrquery(const char *query)
*** 1101,1106 ****
--- 1110,1180 ----
}
/*
+ * err_generic_string -- used to set individual ErrorData string fields.
+ *
+ * Callers should prefer higher-level abstractions, such as the utility
+ * functions 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;
+
+ 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
*************** CopyErrorData(void)
*** 1374,1379 ****
--- 1448,1461 ----
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);
return newedata;
}
*************** FreeErrorData(ErrorData *edata)
*** 1399,1404 ****
--- 1481,1494 ----
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);
pfree(edata);
}
*************** ReThrowError(ErrorData *edata)
*** 1471,1476 ****
--- 1561,1574 ----
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);
recursion_depth--;
PG_RE_THROW();
*************** send_message_to_frontend(ErrorData *edat
*** 2695,2700 ****
--- 2793,2822 ----
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);
+ }
+
pq_sendbyte(&msgbuf, '\0'); /* terminator */
}
else
diff src/backend/utils/error/relerror.c
new file mode 100644
index ...d327845
*** a/src/backend/utils/error/relerror.c
--- b/src/backend/utils/error/relerror.c
***************
*** 0 ****
--- 1,64 ----
+ /*-------------------------------------------------------------------------
+ *
+ * relerror.c relation error logging utility functions
+ *
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/error/relerror.c
+ *
+ *-------------------------------------------------------------------------
+ */
+ #include "postgres.h"
+
+ #include "utils/elog.h"
+ #include "utils/lsyscache.h"
+ #include "utils/rel.h"
+
+ /*
+ * errtablecol --- sets schema_name, table_name and column_name of a column within
+ * errordata. Since table and table schema are set, rel must be an ordinary
+ * table.
+ */
+ int
+ errtablecol(Relation table, const char *colname)
+ {
+ Assert(table->rd_rel->relkind == RELKIND_RELATION);
+
+ err_generic_string(PG_DIAG_SCHEMA_NAME,
+ get_namespace_name(RelationGetNamespace(table)));
+ err_generic_string(PG_DIAG_TABLE_NAME, RelationGetRelationName(table));
+ err_generic_string(PG_DIAG_COLUMN_NAME, colname);
+
+ return 0;
+ }
+
+ /*
+ * errtable --- sets schema_name and table_name within errordata. Since table and
+ * table schema are set, rel must be an ordinary table.
+ */
+ int
+ errtable(Relation table)
+ {
+ Assert(table->rd_rel->relkind == RELKIND_RELATION);
+
+ err_generic_string(PG_DIAG_SCHEMA_NAME,
+ get_namespace_name(RelationGetNamespace(table)));
+ err_generic_string(PG_DIAG_TABLE_NAME, RelationGetRelationName(table));
+
+ return 0;
+ }
+
+ /*
+ * errcontraint --- sets constraint_name within errordata.
+ */
+ int
+ errconstraint(const char *cname)
+ {
+ err_generic_string(PG_DIAG_CONSTRAINT_NAME, cname);
+
+ return 0;
+ }
diff src/backend/utils/generate-errcodes.pl
index b04076f..87e101d
*** a/src/backend/utils/generate-errcodes.pl
--- b/src/backend/utils/generate-errcodes.pl
*************** while (<$errcodes>)
*** 28,33 ****
--- 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]+)/;
diff src/backend/utils/sort/tuplesort.c
index d63c24d..8b750dd
*** a/src/backend/utils/sort/tuplesort.c
--- b/src/backend/utils/sort/tuplesort.c
*************** comparetup_index_btree(const SortTuple *
*** 3014,3019 ****
--- 3014,3020 ----
* for equal keys at the end.
*/
ScanKey scanKey = state->indexScanKey;
+ Relation indexRel = state->indexRel;
IndexTuple tuple1;
IndexTuple tuple2;
int keysz;
*************** comparetup_index_btree(const SortTuple *
*** 3038,3044 ****
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++)
{
*************** comparetup_index_btree(const SortTuple *
*** 3075,3080 ****
--- 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
*************** comparetup_index_btree(const SortTuple *
*** 3088,3097 ****
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)));
}
/*
diff src/include/postgres_ext.h
index 5ba379f..9776541
*** a/src/include/postgres_ext.h
--- b/src/include/postgres_ext.h
*************** typedef PG_INT64_TYPE pg_int64;
*** 60,64 ****
--- 60,68 ----
#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'
#endif /* POSTGRES_EXT_H */
diff src/include/utils/elog.h
index cbbda04..27b84d5
*** a/src/include/utils/elog.h
--- b/src/include/utils/elog.h
*************** extern int geterrcode(void);
*** 206,211 ****
--- 206,212 ----
extern int geterrposition(void);
extern int getinternalerrposition(void);
+ extern int err_generic_string(int field, const char *str);
/*----------
* Old-style error reporting API: to be used in this way:
*************** typedef struct ErrorData
*** 338,343 ****
--- 339,348 ----
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 */
int cursorpos; /* cursor index into query string */
int internalpos; /* cursor index into internalquery */
char *internalquery; /* text of internally-generated query */
diff src/include/utils/rel.h
index bde5f17..0357b04
*** a/src/include/utils/rel.h
--- b/src/include/utils/rel.h
*************** typedef struct StdRdOptions
*** 398,401 ****
--- 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 */
diff src/interfaces/libpq/fe-protocol3.c
index 40e75d4..d71e331
*** a/src/interfaces/libpq/fe-protocol3.c
--- b/src/interfaces/libpq/fe-protocol3.c
*************** pqGetErrorNotice3(PGconn *conn, bool isE
*** 936,941 ****
--- 936,958 ----
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);
}
/*
diff src/pl/plpgsql/src/generate-plerrcodes.pl
index 90e1010..ccd94bd
*** a/src/pl/plpgsql/src/generate-plerrcodes.pl
--- b/src/pl/plpgsql/src/generate-plerrcodes.pl
*************** while (<$errcodes>)
*** 20,27 ****
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]+)?/;