diff doc/src/sgml/config.sgml index 4e0492b..ddaf900 *** a/doc/src/sgml/config.sgml --- b/doc/src/sgml/config.sgml *************** CREATE TABLE postgres_log *** 4183,4188 **** --- 4183,4198 ---- query_pos integer, location text, application_name text, + column_name text, + table_name text, + schema_name text, + constraint_name text, + constraint_table text, + constraint_schema text, + routine_name text, + trigger_name text, + trigger_table text, + trigger_schema text, PRIMARY KEY (session_id, session_line_num) ); diff doc/src/sgml/protocol.sgml index e725563..0c1042f *** a/doc/src/sgml/protocol.sgml --- b/doc/src/sgml/protocol.sgml *************** message. *** 4720,4732 **** The client is responsible for formatting displayed information to meet its needs; in particular it should break long lines as needed. Newline characters appearing in the error message fields should be treated as paragraph breaks, ! not line breaks. --- 4720,4865 ---- + + + c + + + + Column name: the name of the column associated with the the error, if + any. + + + + + + + t + + + + Table name: the name of the table associated with the error, if any. + + + + + + + s + + + + Schema name: the name of schema associated with the error. + + + + + + + n + + + + Constraint name: the name of the constraint associated with the error, + if any. + + + + + + + o + + + + Constraint table: the table name of the constraint associated with the + error, if any. + + + + + + + m + + + + Constraint schema: the schema name of the constraint associated with the + error, if any. + + + + + + + r + + + + Routine name: the name of the routine associated with the error, if any. + + + + + + + u + + + + Routine schema: the schema of the routine associated with the error, if + any. + + + + + + + g + + + + Trigger name: the name of the trigger associated with the error, if any. + + + + + + + i + + + + Trigger table: the name of the table of the trigger associated with the + error, if any. + + + + + + + h + + + + Trigger schema: the schema name of the trigger associated with the + error, if any. + + + + The client is responsible for formatting displayed information to meet its needs; in particular it should break long lines as needed. Newline characters appearing in the error message fields should be treated as paragraph breaks, ! not line breaks. Note that certain object names represented as ErrorResponse ! fields, such as Trigger name, are not guaranteed to be unique across a database, ! and such objects cannot generally be uniquely identified by name alone. It may ! be necessary in these cases to unambiguously establish the identity of the ! object of interest based on a unique combination of the object's name, schema ! name, and, in the case of integrity constraints, table name. diff src/backend/access/nbtree/nbtinsert.c index 3ed9b5c..9251508 *** 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,400 ---- RelationGetRelationName(rel)), errdetail("Key %s already exists.", BuildIndexValueDescription(rel, ! values, isnull)), ! errrel(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); --- 456,463 ---- (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."), ! errrel(rel))); 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, --- 535,542 ---- 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."), ! errrel(rel))); /*---------- * If we will need to split the page to put the item on this page, diff src/backend/commands/tablecmds.c index d69809a..679a89e *** a/src/backend/commands/tablecmds.c --- b/src/backend/commands/tablecmds.c *************** ATRewriteTable(AlteredTableInfo *tab, Oi *** 3805,3814 **** 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) --- 3805,3821 ---- 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) ? ! errrelcol(newrel, NameStr(att->attname)): ! errrelcol(oldrel, NameStr(att->attname)))); ! } } foreach(l, tab->constraints) *************** ATRewriteTable(AlteredTableInfo *tab, Oi *** 3822,3828 **** 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 */ --- 3829,3841 ---- ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", ! con->name), ! (newrel) ? ! errrel(newrel): ! errrel(oldrel), ! (newrel) ? ! errconstraint(newrel, con->name): ! errconstraint(oldrel, con->name))); break; case CONSTR_FOREIGN: /* Nothing to do here */ *************** validateCheckConstraint(Relation rel, He *** 6632,6638 **** ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", ! NameStr(constrForm->conname)))); ResetExprContext(econtext); } --- 6645,6653 ---- ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", ! NameStr(constrForm->conname)), ! errrel(rel), ! errconstraint(rel, NameStr(constrForm->conname)))); ResetExprContext(econtext); } diff src/backend/commands/typecmds.c index 30850b2..2dfc7c6 *** a/src/backend/commands/typecmds.c --- b/src/backend/commands/typecmds.c *************** AlterDomainNotNull(List *names, bool not *** 2227,2239 **** 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); --- 2227,2240 ---- 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)), ! errrelcol(testrel, NameStr(att->attname)))); } } heap_endscan(scan); *************** validateDomainConstraint(Oid domainoid, *** 2602,2608 **** (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); --- 2603,2611 ---- (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)), ! errrelcol(testrel, ! NameStr(tupdesc->attrs[attnum - 1]->attname)))); } ResetExprContext(econtext); diff src/backend/executor/execMain.c index 440438b..dc2d516 *** a/src/backend/executor/execMain.c --- b/src/backend/executor/execMain.c *************** ExecConstraints(ResultRelInfo *resultRel *** 1515,1528 **** 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)))); } } --- 1515,1530 ---- for (attrChk = 1; attrChk <= natts; attrChk++) { ! Form_pg_attribute Chk = rel->rd_att->attrs[attrChk - 1]; ! ! if (Chk->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)), ! errrelcol(rel, NameStr(Chk->attname)))); } } *************** ExecConstraints(ResultRelInfo *resultRel *** 1534,1542 **** ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("new row for relation \"%s\" violates check constraint \"%s\"", ! RelationGetRelationName(rel), failed), errdetail("Failing row contains %s.", ! ExecBuildSlotValueDescription(slot, 64)))); } } --- 1536,1546 ---- ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("new row for relation \"%s\" violates check constraint \"%s\"", ! RelationGetRelationName(rel), failed), errdetail("Failing row contains %s.", ! ExecBuildSlotValueDescription(slot, 64)), ! errrel(rel), ! errconstraint(rel, failed))); } } diff src/backend/executor/execUtils.c index 2bd8b42..59388c2 *** a/src/backend/executor/execUtils.c --- b/src/backend/executor/execUtils.c *************** retry: *** 1304,1317 **** 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); --- 1304,1319 ---- errmsg("could not create exclusion constraint \"%s\"", RelationGetRelationName(index)), errdetail("Key %s conflicts with key %s.", ! error_new, error_existing), ! errrel(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), ! errrel(index))); } index_endscan(index_scan); diff src/backend/utils/adt/ri_triggers.c index 983f631..fa99ad8 *** a/src/backend/utils/adt/ri_triggers.c --- b/src/backend/utils/adt/ri_triggers.c *************** RI_FKey_check(TriggerData *trigdata) *** 338,344 **** 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); --- 338,346 ---- 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."), ! errrel(trigdata->tg_relation), ! errconstraint(trigdata->tg_relation, NameStr(riinfo->conname)))); heap_close(pk_rel, RowShareLock); return PointerGetDatum(NULL); *************** RI_Initial_Check(Trigger *trigger, Relat *** 2466,2472 **** 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 --- 2468,2477 ---- 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."), ! errrel(fk_rel), ! errconstraint(fk_rel, NameStr(fake_riinfo.conname)))); ! /* * We tell ri_ReportViolation we were doing the RI_PLAN_CHECK_LOOKUPPK *************** ri_ReportViolation(const RI_ConstraintIn *** 3218,3224 **** 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), --- 3223,3231 ---- NameStr(riinfo->conname)), errdetail("Key (%s)=(%s) is not present in table \"%s\".", key_names.data, key_values.data, ! RelationGetRelationName(pk_rel)), ! errrel(fk_rel), ! errconstraint(fk_rel, NameStr(riinfo->conname)))); else ereport(ERROR, (errcode(ERRCODE_FOREIGN_KEY_VIOLATION), *************** ri_ReportViolation(const RI_ConstraintIn *** 3226,3234 **** RelationGetRelationName(pk_rel), NameStr(riinfo->conname), RelationGetRelationName(fk_rel)), ! errdetail("Key (%s)=(%s) is still referenced from table \"%s\".", ! key_names.data, key_values.data, ! RelationGetRelationName(fk_rel)))); } --- 3233,3243 ---- RelationGetRelationName(pk_rel), NameStr(riinfo->conname), RelationGetRelationName(fk_rel)), ! errdetail("Key (%s)=(%s) is still referenced from table \"%s\".", ! key_names.data, key_values.data, ! RelationGetRelationName(fk_rel)), ! errrel(pk_rel), ! errconstraint(fk_rel, NameStr(riinfo->conname)))); } diff src/backend/utils/error/elog.c index a40b343..f76e88d *** a/src/backend/utils/error/elog.c --- b/src/backend/utils/error/elog.c *************** *** 75,82 **** --- 75,84 ---- #include "storage/proc.h" #include "tcop/tcopprot.h" #include "utils/guc.h" + #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/ps_status.h" + #include "utils/rel.h" #undef _ *************** static int syslog_facility = LOG_LOCAL0; *** 129,134 **** --- 131,139 ---- static void write_syslog(int level, const char *line); #endif + static int erritem(int field, const char *str); + static void set_errdata_field(char **ptr, const char *str, bool overwrite); + static void write_console(const char *line, int len); #ifdef WIN32 *************** errfinish(int dummy,...) *** 477,482 **** --- 482,509 ---- 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->constraint_table) + pfree(edata->constraint_table); + if (edata->constraint_schema) + pfree(edata->constraint_schema); + if (edata->routine_name) + pfree(edata->routine_name); + if (edata->routine_schema) + pfree(edata->routine_schema); + if (edata->trigger_name) + pfree(edata->trigger_name); + if (edata->trigger_table) + pfree(edata->trigger_table); + if (edata->trigger_schema) + pfree(edata->trigger_schema); errordata_stack_depth--; *************** internalerrquery(const char *query) *** 1079,1084 **** --- 1106,1248 ---- } /* + * errrelcol --- sets column_name, table_name and schema_name of a column + * within errordata + */ + int + errrelcol(Relation rel, const char *colname) + { + erritem(PG_DIAG_COLUMN_NAME, colname); + erritem(PG_DIAG_TABLE_NAME, RelationGetRelationName(rel)); + erritem(PG_DIAG_SCHEMA_NAME, + get_namespace_name(RelationGetNamespace(rel))); + + return 0; /* return value does not matter */ + } + + /* + * errrel --- sets column_name, table_name and schema_name within errordata + */ + int + errrel(Relation rel) + { + erritem(PG_DIAG_TABLE_NAME, RelationGetRelationName(rel)); + erritem(PG_DIAG_SCHEMA_NAME, + get_namespace_name(RelationGetNamespace(rel))); + + return 0; /* return value does not matter */ + } + + /* + * errcontraint --- sets constraint_name, constraint_table and constraint_schema + * within errordata. + */ + int + errconstraint(Relation rel, const char *cname) + { + erritem(PG_DIAG_CONSTRAINT_NAME, cname); + erritem(PG_DIAG_CONSTRAINT_TABLE, RelationGetRelationName(rel)); + erritem(PG_DIAG_CONSTRAINT_SCHEMA, + get_namespace_name(RelationGetNamespace(rel))); + + return 0; /* return value does not matter */ + } + + /* + * erritem -- generic setting of ErrorData string fields + */ + static int + erritem(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, true); + break; + case PG_DIAG_MESSAGE_DETAIL: + set_errdata_field(&edata->detail, str, true); + break; + case PG_DIAG_MESSAGE_HINT: + set_errdata_field(&edata->hint, str, true); + break; + case PG_DIAG_CONTEXT: + set_errdata_field(&edata->context, str, true); + break; + case PG_DIAG_COLUMN_NAME: + set_errdata_field(&edata->column_name, str, true); + break; + case PG_DIAG_TABLE_NAME: + set_errdata_field(&edata->table_name, str, true); + break; + case PG_DIAG_SCHEMA_NAME: + set_errdata_field(&edata->schema_name, str, true); + break; + case PG_DIAG_CONSTRAINT_NAME: + set_errdata_field(&edata->constraint_name, str, true); + break; + case PG_DIAG_CONSTRAINT_TABLE: + set_errdata_field(&edata->constraint_table, str, true); + break; + case PG_DIAG_CONSTRAINT_SCHEMA: + set_errdata_field(&edata->constraint_schema, str, true); + break; + + /* + * The remaining fields, once set, will not be set again. We need to + * guard against resetting here because there is partial redundancy in + * the fields set between some of our potential callers, even though two + * or more of them might reasonably coexist within the same ereport + * call. + */ + case PG_DIAG_ROUTINE_NAME: + set_errdata_field(&edata->routine_name, str, false); + break; + case PG_DIAG_ROUTINE_SCHEMA: + set_errdata_field(&edata->routine_schema, str, false); + break; + case PG_DIAG_TRIGGER_NAME: + set_errdata_field(&edata->trigger_name, str, false); + break; + case PG_DIAG_TRIGGER_TABLE: + set_errdata_field(&edata->trigger_table, str, false); + break; + case PG_DIAG_TRIGGER_SCHEMA: + set_errdata_field(&edata->trigger_schema, str, false); + break; + default: + elog(ERROR, "unknown ErrorData field identifier %d", field); + } + + return 0; /* return value does not matter */ + } + + /* + * set_errdata_field --- set an ErrorData string field, while potentially + * avoiding overwriting any existing value + */ + static void + set_errdata_field(char **ptr, const char *str, bool overwrite) + { + if (*ptr != NULL) + { + /* Avoid overwriting existing value entirely */ + if (!overwrite) + return; + + pfree(*ptr); + *ptr = NULL; + } + + if (str != 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) *** 1352,1357 **** --- 1516,1543 ---- 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->constraint_table) + newedata->constraint_table = pstrdup(newedata->constraint_table); + if (newedata->constraint_schema) + newedata->constraint_schema = pstrdup(newedata->constraint_schema); + 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); + if (newedata->trigger_table) + newedata->trigger_table = pstrdup(newedata->trigger_table); + if (newedata->trigger_schema) + newedata->trigger_schema = pstrdup(newedata->trigger_schema); return newedata; } *************** FreeErrorData(ErrorData *edata) *** 1377,1382 **** --- 1563,1590 ---- 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->constraint_table) + pfree(edata->constraint_table); + if (edata->constraint_schema) + pfree(edata->constraint_schema); + if (edata->routine_name) + pfree(edata->routine_name); + if (edata->routine_schema) + pfree(edata->routine_schema); + if (edata->trigger_name) + pfree(edata->trigger_name); + if (edata->trigger_table) + pfree(edata->trigger_table); + if (edata->trigger_schema) + pfree(edata->trigger_schema); pfree(edata); } *************** ReThrowError(ErrorData *edata) *** 1449,1454 **** --- 1657,1684 ---- 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->constraint_table) + newedata->constraint_table = pstrdup(newedata->constraint_table); + if (newedata->constraint_schema) + newedata->constraint_schema = pstrdup(newedata->constraint_schema); + 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); + if (newedata->trigger_table) + newedata->trigger_table = pstrdup(newedata->trigger_table); + if (newedata->trigger_schema) + newedata->trigger_schema = pstrdup(newedata->trigger_schema); recursion_depth--; PG_RE_THROW(); *************** write_csvlog(ErrorData *edata) *** 2259,2264 **** --- 2489,2527 ---- /* application name */ if (application_name) appendCSVLiteral(&buf, application_name); + 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->constraint_table); + appendStringInfoChar(&buf, ','); + + appendCSVLiteral(&buf, edata->constraint_schema); + appendStringInfoChar(&buf, ','); + + appendCSVLiteral(&buf, edata->routine_name); + appendStringInfoChar(&buf, ','); + + appendCSVLiteral(&buf, edata->routine_schema); + appendStringInfoChar(&buf, ','); + + appendCSVLiteral(&buf, edata->trigger_name); + appendStringInfoChar(&buf, ','); + + appendCSVLiteral(&buf, edata->trigger_table); + appendStringInfoChar(&buf, ','); + + appendCSVLiteral(&buf, edata->trigger_schema); appendStringInfoChar(&buf, '\n'); *************** send_message_to_server_log(ErrorData *ed *** 2377,2382 **** --- 2640,2722 ---- appendStringInfo(&buf, _("LOCATION: %s:%d\n"), edata->filename, edata->lineno); } + if (edata->column_name) + { + log_line_prefix(&buf, edata); + appendStringInfoString(&buf, _("COLUMN NAME: ")); + append_with_tabs(&buf, edata->column_name); + appendStringInfoChar(&buf, '\n'); + } + if (edata->table_name) + { + log_line_prefix(&buf, edata); + appendStringInfoString(&buf, _("TABLE NAME: ")); + append_with_tabs(&buf, edata->table_name); + appendStringInfoChar(&buf, '\n'); + } + if (edata->schema_name) + { + log_line_prefix(&buf, edata); + appendStringInfoString(&buf, _("SCHEMA NAME: ")); + append_with_tabs(&buf, edata->schema_name); + appendStringInfoChar(&buf, '\n'); + } + if (edata->constraint_name) + { + log_line_prefix(&buf, edata); + appendStringInfoString(&buf, _("CONSTRAINT NAME: ")); + append_with_tabs(&buf, edata->constraint_name); + appendStringInfoChar(&buf, '\n'); + } + if (edata->constraint_table) + { + log_line_prefix(&buf, edata); + appendStringInfoString(&buf, _("CONSTRAINT TABLE: ")); + append_with_tabs(&buf, edata->constraint_table); + appendStringInfoChar(&buf, '\n'); + } + if (edata->constraint_schema) + { + log_line_prefix(&buf, edata); + appendStringInfoString(&buf, _("CONSTRAINT SCHEMA: ")); + append_with_tabs(&buf, edata->constraint_schema); + appendStringInfoChar(&buf, '\n'); + } + if (edata->routine_name) + { + log_line_prefix(&buf, edata); + appendStringInfoString(&buf, _("ROUTINE NAME: ")); + append_with_tabs(&buf, edata->routine_name); + appendStringInfoChar(&buf, '\n'); + } + if (edata->routine_schema) + { + log_line_prefix(&buf, edata); + appendStringInfoString(&buf, _("ROUTINE SCHEMA: ")); + append_with_tabs(&buf, edata->routine_schema); + appendStringInfoChar(&buf, '\n'); + } + if (edata->trigger_name) + { + log_line_prefix(&buf, edata); + appendStringInfoString(&buf, _("TRIGGER NAME: ")); + append_with_tabs(&buf, edata->trigger_name); + appendStringInfoChar(&buf, '\n'); + } + if (edata->trigger_table) + { + log_line_prefix(&buf, edata); + appendStringInfoString(&buf, _("TRIGGER TABLE: ")); + append_with_tabs(&buf, edata->trigger_table); + appendStringInfoChar(&buf, '\n'); + } + if (edata->trigger_schema) + { + log_line_prefix(&buf, edata); + appendStringInfoString(&buf, _("TRIGGER SCHEMA: ")); + append_with_tabs(&buf, edata->trigger_schema); + appendStringInfoChar(&buf, '\n'); + } } } *************** send_message_to_frontend(ErrorData *edat *** 2673,2678 **** --- 3013,3084 ---- 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->schema_name) + { + pq_sendbyte(&msgbuf, PG_DIAG_SCHEMA_NAME); + err_sendstring(&msgbuf, edata->schema_name); + } + + if (edata->constraint_name) + { + pq_sendbyte(&msgbuf, PG_DIAG_CONSTRAINT_NAME); + err_sendstring(&msgbuf, edata->constraint_name); + } + + if (edata->constraint_table) + { + pq_sendbyte(&msgbuf, PG_DIAG_CONSTRAINT_TABLE); + err_sendstring(&msgbuf, edata->constraint_table); + } + + if (edata->constraint_schema) + { + pq_sendbyte(&msgbuf, PG_DIAG_CONSTRAINT_SCHEMA); + err_sendstring(&msgbuf, edata->constraint_schema); + } + + 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->trigger_name) + { + pq_sendbyte(&msgbuf, PG_DIAG_TRIGGER_NAME); + err_sendstring(&msgbuf, edata->trigger_name); + } + + if (edata->trigger_table) + { + pq_sendbyte(&msgbuf, PG_DIAG_TRIGGER_TABLE); + err_sendstring(&msgbuf, edata->trigger_table); + } + + if (edata->trigger_schema) + { + pq_sendbyte(&msgbuf, PG_DIAG_TRIGGER_SCHEMA); + err_sendstring(&msgbuf, edata->trigger_schema); + } + pq_sendbyte(&msgbuf, '\0'); /* terminator */ } else diff src/backend/utils/sort/tuplesort.c index d5a2003..5fce241 *** a/src/backend/utils/sort/tuplesort.c --- b/src/backend/utils/sort/tuplesort.c *************** comparetup_index_btree(const SortTuple * *** 3090,3096 **** RelationGetRelationName(state->indexRel)), errdetail("Key %s is duplicated.", BuildIndexValueDescription(state->indexRel, ! values, isnull)))); } /* --- 3090,3097 ---- RelationGetRelationName(state->indexRel)), errdetail("Key %s is duplicated.", BuildIndexValueDescription(state->indexRel, ! values, isnull)), ! errrel(state->indexRel))); } /* diff src/include/postgres_ext.h index b6ebb7a..b37adf0 *** a/src/include/postgres_ext.h --- b/src/include/postgres_ext.h *************** typedef unsigned int Oid; *** 55,59 **** --- 55,70 ---- #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_SCHEMA_NAME 's' + #define PG_DIAG_CONSTRAINT_NAME 'n' + #define PG_DIAG_CONSTRAINT_TABLE 'o' + #define PG_DIAG_CONSTRAINT_SCHEMA 'm' + #define PG_DIAG_ROUTINE_NAME 'r' + #define PG_DIAG_ROUTINE_SCHEMA 'u' + #define PG_DIAG_TRIGGER_NAME 'g' + #define PG_DIAG_TRIGGER_TABLE 'i' + #define PG_DIAG_TRIGGER_SCHEMA 'h' #endif diff src/include/utils/elog.h index 1bbfd2b..48e9afe *** a/src/include/utils/elog.h --- b/src/include/utils/elog.h *************** extern int errhidestmt(bool hide_stmt); *** 183,188 **** --- 183,195 ---- extern int errfunction(const char *funcname); extern int errposition(int cursorpos); + /* Pre-declare Relation, in order to avoid a build dependency on rel.h. */ + typedef struct RelationData *Relation; + + extern int errrelcol(Relation rel, const char *colname); + extern int errrel(Relation rel); + extern int errconstraint(Relation rel, const char *cname); + extern int internalerrposition(int cursorpos); extern int internalerrquery(const char *query); *************** typedef struct ErrorData *** 321,330 **** char *detail_log; /* detail error message for server log only */ char *hint; /* hint message */ char *context; /* context message */ ! int cursorpos; /* cursor index into query string */ ! int internalpos; /* cursor index into internalquery */ ! char *internalquery; /* text of internally-generated query */ ! int saved_errno; /* errno at entry */ } ErrorData; extern void EmitErrorReport(void); --- 327,347 ---- 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 *schema_name; /* name of schema */ ! char *constraint_name; /* name of constraint */ ! char *constraint_table; /* name of table related to constraint */ ! char *constraint_schema; /* name of schema with constraint */ ! char *routine_name; /* name of function that caused error */ ! char *routine_schema; /* schema name of function that caused error */ ! char *trigger_name; /* name of trigger that caused error */ ! char *trigger_table; /* table of trigger that caused error */ ! char *trigger_schema; /* schema of trigger that caused error */ ! int cursorpos; /* cursor index into query string */ ! int internalpos; /* cursor index into internalquery */ ! char *internalquery; /* text of internally-generated query */ ! int saved_errno; /* errno at entry */ } ErrorData; extern void EmitErrorReport(void); diff src/interfaces/libpq/fe-protocol3.c index 173af2e..f2616b3 *** a/src/interfaces/libpq/fe-protocol3.c --- b/src/interfaces/libpq/fe-protocol3.c *************** pqGetErrorNotice3(PGconn *conn, bool isE *** 980,985 **** --- 980,1029 ---- 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_SCHEMA_NAME); + if (val) + appendPQExpBuffer(&workBuf, + libpq_gettext("SCHEMA 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_CONSTRAINT_TABLE); + if (val) + appendPQExpBuffer(&workBuf, + libpq_gettext("CONSTRAINT TABLE: %s\n"), val); + val = PQresultErrorField(res, PG_DIAG_CONSTRAINT_SCHEMA); + if (val) + appendPQExpBuffer(&workBuf, + libpq_gettext("CONSTRAINT SCHEMA: %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 SCHEMA: %s\n"), val); + val = PQresultErrorField(res, PG_DIAG_TRIGGER_NAME); + if (val) + appendPQExpBuffer(&workBuf, + libpq_gettext("TRIGGER NAME: %s\n"), val); + val = PQresultErrorField(res, PG_DIAG_TRIGGER_TABLE); + if (val) + appendPQExpBuffer(&workBuf, + libpq_gettext("TRIGGER TABLE: %s\n"), val); + val = PQresultErrorField(res, PG_DIAG_TRIGGER_SCHEMA); + if (val) + appendPQExpBuffer(&workBuf, + libpq_gettext("TRIGGER SCHEMA: %s\n"), val); } /*