diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 4e0492b..ddaf900 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -4183,6 +4183,16 @@ CREATE TABLE postgres_log 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 --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml index e725563..6f34448 100644 --- a/doc/src/sgml/protocol.sgml +++ b/doc/src/sgml/protocol.sgml @@ -4720,6 +4720,127 @@ message. + + +c + + + + Column name: the name of column related to error + + + + + + +t + + + + Table name: the name of table related to error + + + + + + +s + + + + Schema name: the name of schema related to error + + + + + + +n + + + + Constraint name: the name of constraint related to error + + + + + + +o + + + + Constraint table: the table name of constraint related to error + + + + + + +m + + + + Constraint schema: the schema of constraint related to error + + + + + + +r + + + + Routine name: the name of routine related to error + + + + + + +u + + + + Routine schema: the schema of routine related to error + + + + + + +g + + + + Trigger name: the name of trigger related to error + + + + + + +i + + + + Trigger table: the table of trigger related to error + + + + + + +h + + + + Trigger schema: the schema of trigger related to error + + + + diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c index 3ed9b5c..419823c 100644 --- a/src/backend/access/nbtree/nbtinsert.c +++ b/src/backend/access/nbtree/nbtinsert.c @@ -23,6 +23,7 @@ #include "storage/predicate.h" #include "utils/inval.h" #include "utils/tqual.h" +#include "utils/relerror.h" typedef struct @@ -393,7 +394,8 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel, RelationGetRelationName(rel)), errdetail("Key %s already exists.", BuildIndexValueDescription(rel, - values, isnull)))); + values, isnull)), + errrelation(rel))); } } else if (all_dead) @@ -455,7 +457,8 @@ _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."))); + errhint("This may be because of a non-immutable index expression."), + errrelation(rel))); if (nbuf != InvalidBuffer) _bt_relbuf(rel, nbuf); @@ -533,7 +536,8 @@ _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."))); + "or use full text indexing."), + errrelation(rel))); /*---------- * If we will need to split the page to put the item on this page, diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index d69809a..91b8665 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -81,6 +81,7 @@ #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/relcache.h" +#include "utils/relerror.h" #include "utils/snapmgr.h" #include "utils/syscache.h" #include "utils/tqual.h" @@ -3808,6 +3809,10 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) ereport(ERROR, (errcode(ERRCODE_NOT_NULL_VIOLATION), errmsg("column \"%s\" contains null values", + NameStr(newTupDesc->attrs[attn]->attname)), + (newrel) ? errrelation_column(newrel, + NameStr(newTupDesc->attrs[attn]->attname)) : + errrelation_column(oldrel, NameStr(newTupDesc->attrs[attn]->attname)))); } @@ -3822,7 +3827,9 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", - con->name))); + con->name), + (newrel) ? errrelation(newrel) : errrelation(oldrel), + (newrel) ? errconstraint(newrel, con->name) : errconstraint(oldrel, con->name))); break; case CONSTR_FOREIGN: /* Nothing to do here */ @@ -6632,7 +6639,9 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", - NameStr(constrForm->conname)))); + NameStr(constrForm->conname)), + errrelation(rel), + errconstraint(rel, NameStr(constrForm->conname)))); ResetExprContext(econtext); } diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 30850b2..040adab 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -69,6 +69,7 @@ #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/rel.h" +#include "utils/relerror.h" #include "utils/syscache.h" #include "utils/tqual.h" @@ -2233,7 +2234,9 @@ AlterDomainNotNull(List *names, bool notNull) (errcode(ERRCODE_NOT_NULL_VIOLATION), errmsg("column \"%s\" of table \"%s\" contains null values", NameStr(tupdesc->attrs[attnum - 1]->attname), - RelationGetRelationName(testrel)))); + RelationGetRelationName(testrel)), + errrelation_column(testrel, + NameStr(tupdesc->attrs[attnum - 1]->attname)))); } } heap_endscan(scan); @@ -2602,7 +2605,9 @@ validateDomainConstraint(Oid domainoid, char *ccbin) (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)))); + RelationGetRelationName(testrel)), + errrelation_column(testrel, + NameStr(tupdesc->attrs[attnum - 1]->attname)))); } ResetExprContext(econtext); diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 440438b..8332ecf 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -53,6 +53,7 @@ #include "utils/acl.h" #include "utils/lsyscache.h" #include "utils/memutils.h" +#include "utils/relerror.h" #include "utils/snapmgr.h" #include "utils/tqual.h" @@ -1522,7 +1523,9 @@ ExecConstraints(ResultRelInfo *resultRelInfo, 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)))); + ExecBuildSlotValueDescription(slot, 64)), + errrelation_column(rel, + NameStr(rel->rd_att->attrs[attrChk - 1]->attname)))); } } @@ -1536,7 +1539,9 @@ ExecConstraints(ResultRelInfo *resultRelInfo, errmsg("new row for relation \"%s\" violates check constraint \"%s\"", RelationGetRelationName(rel), failed), errdetail("Failing row contains %s.", - ExecBuildSlotValueDescription(slot, 64)))); + ExecBuildSlotValueDescription(slot, 64)), + errrelation(rel), + errconstraint(rel, failed))); } } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 2bd8b42..7f0d331 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -50,6 +50,7 @@ #include "parser/parsetree.h" #include "storage/lmgr.h" #include "utils/memutils.h" +#include "utils/relerror.h" #include "utils/tqual.h" @@ -1304,14 +1305,16 @@ retry: errmsg("could not create exclusion constraint \"%s\"", RelationGetRelationName(index)), errdetail("Key %s conflicts with key %s.", - error_new, error_existing))); + error_new, error_existing), + errrelation(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))); + error_new, error_existing), + errrelation(index))); } index_endscan(index_scan); diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 983f631..48b02d5 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -49,6 +49,7 @@ #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/rel.h" +#include "utils/relerror.h" #include "utils/snapmgr.h" #include "utils/syscache.h" #include "utils/tqual.h" @@ -338,7 +339,9 @@ 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."))); + errdetail("MATCH FULL does not allow mixing of null and nonnull key values."), + errrelation(trigdata->tg_relation), + errconstraint(trigdata->tg_relation, NameStr(riinfo->conname)))); heap_close(pk_rel, RowShareLock); return PointerGetDatum(NULL); @@ -2466,7 +2469,10 @@ 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."))); + errdetail("match full does not allow mixing of null and nonnull key values."), + errrelation(fk_rel), + errconstraint(fk_rel, NameStr(fake_riinfo.conname)))); + /* * We tell ri_ReportViolation we were doing the RI_PLAN_CHECK_LOOKUPPK @@ -3218,7 +3224,9 @@ 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)))); + RelationGetRelationName(pk_rel)), + errrelation(fk_rel), + errconstraint(fk_rel, NameStr(riinfo->conname)))); else ereport(ERROR, (errcode(ERRCODE_FOREIGN_KEY_VIOLATION), @@ -3228,7 +3236,9 @@ 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)))); + RelationGetRelationName(fk_rel)), + errrelation(pk_rel), + errconstraint(fk_rel, NameStr(riinfo->conname)))); } diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index a40b343..93f39eb 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -75,9 +75,11 @@ #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" +#include "utils/relerror.h" #undef _ #define _(x) err_gettext(x) @@ -477,6 +479,28 @@ errfinish(int dummy,...) 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--; @@ -1079,6 +1103,157 @@ internalerrquery(const char *query) } /* + * Sets column_name, table_name and schema_name in ErrorData related to relation + */ +int +errrelation_column(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; +} + +/* + * Sets column_name, table_name and schema_name in ErrorData related to relation + */ +int +errrelation(Relation rel) +{ + erritem(PG_DIAG_TABLE_NAME, RelationGetRelationName(rel)); + erritem(PG_DIAG_SCHEMA_NAME, + get_namespace_name(RelationGetNamespace(rel))); + + return 0; +} + +/* + * Sets constraint_name, constraint_table and constraint_schema in 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; +} + +/* + * set ErrorData field - ensure not overwriting for selected fields + */ +static void +set_field(char **ptr, const char *str, bool overwrite) +{ + if (*ptr != NULL) + { + /* + * for some cases like ROUTINE_NAME, ROUTINE_SCHEMA we would + * to get the most older value. + */ + if (!overwrite) + return; + + pfree(*ptr); + *ptr = NULL; + } + + if (str != NULL) + *ptr = MemoryContextStrdup(ErrorContext, str); +} + +/* + * erritem -- generic setting of ErrorData string fields + */ +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_field(&edata->message, str, true); + break; + + case PG_DIAG_MESSAGE_DETAIL: + set_field(&edata->detail, str, true); + break; + + case PG_DIAG_MESSAGE_HINT: + set_field(&edata->hint, str, true); + break; + + case PG_DIAG_CONTEXT: + set_field(&edata->context, str, true); + break; + + case PG_DIAG_COLUMN_NAME: + set_field(&edata->column_name, str, true); + break; + + case PG_DIAG_TABLE_NAME: + set_field(&edata->table_name, str, true); + break; + + case PG_DIAG_SCHEMA_NAME: + set_field(&edata->schema_name, str, true); + break; + + case PG_DIAG_CONSTRAINT_NAME: + set_field(&edata->constraint_name, str, true); + break; + + case PG_DIAG_CONSTRAINT_TABLE: + set_field(&edata->constraint_table, str, true); + break; + + case PG_DIAG_CONSTRAINT_SCHEMA: + set_field(&edata->constraint_schema, str, true); + break; + + /* + * setup of routine_name, routine_schema, trigger_name, + * trigger_table, trigger_schema is processed together + * with collecting context. Only first values are valid, + * so we should to protect these values. + */ + case PG_DIAG_ROUTINE_NAME: + set_field(&edata->routine_name, str, false); + break; + + case PG_DIAG_ROUTINE_SCHEMA: + set_field(&edata->routine_schema, str, false); + break; + + case PG_DIAG_TRIGGER_NAME: + set_field(&edata->trigger_name, str, false); + break; + + case PG_DIAG_TRIGGER_TABLE: + set_field(&edata->trigger_table, str, false); + break; + + case PG_DIAG_TRIGGER_SCHEMA: + set_field(&edata->trigger_schema, str, false); + break; + + default: + elog(ERROR, "unknown ErrorData field id %d", + field); + } + + return 0; /* return value does not matter */ +} + +/* * geterrcode --- return the currently set SQLSTATE error code * * This is only intended for use in error callback subroutines, since there @@ -1352,6 +1527,28 @@ CopyErrorData(void) 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; } @@ -1377,6 +1574,28 @@ FreeErrorData(ErrorData *edata) 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); } @@ -1449,6 +1668,28 @@ ReThrowError(ErrorData *edata) 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(); @@ -2259,6 +2500,39 @@ write_csvlog(ErrorData *edata) /* 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'); @@ -2377,6 +2651,83 @@ send_message_to_server_log(ErrorData *edata) 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'); + } } } @@ -2673,6 +3024,72 @@ send_message_to_frontend(ErrorData *edata) 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 --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c index d5a2003..3a3c663 100644 --- a/src/backend/utils/sort/tuplesort.c +++ b/src/backend/utils/sort/tuplesort.c @@ -112,6 +112,7 @@ #include "utils/memutils.h" #include "utils/pg_rusage.h" #include "utils/rel.h" +#include "utils/relerror.h" #include "utils/sortsupport.h" #include "utils/tuplesort.h" @@ -3090,7 +3091,8 @@ comparetup_index_btree(const SortTuple *a, const SortTuple *b, RelationGetRelationName(state->indexRel)), errdetail("Key %s is duplicated.", BuildIndexValueDescription(state->indexRel, - values, isnull)))); + values, isnull)), + errrelation(state->indexRel))); } /* diff --git a/src/include/postgres_ext.h b/src/include/postgres_ext.h index b6ebb7a..829114a 100644 --- a/src/include/postgres_ext.h +++ b/src/include/postgres_ext.h @@ -55,5 +55,16 @@ typedef unsigned int Oid; #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 --git a/src/include/utils/elog.h b/src/include/utils/elog.h index 1bbfd2b..c897073 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -190,6 +190,8 @@ extern int geterrcode(void); extern int geterrposition(void); extern int getinternalerrposition(void); +extern int erritem(int field, const char *str); + /*---------- * Old-style error reporting API: to be used in this way: @@ -321,6 +323,17 @@ typedef struct ErrorData 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 */ diff --git a/src/include/utils/relerror.h b/src/include/utils/relerror.h new file mode 100644 index 0000000..357222e --- /dev/null +++ b/src/include/utils/relerror.h @@ -0,0 +1,21 @@ +/*------------------------------------------------------------------------- + * + * relerror.h + * setting ErrorData fields related to Relation struct + * + * Copyright (c) 2007-2012, PostgreSQL Global Development Group + * + * src/include/utils/relerror.h + * + *------------------------------------------------------------------------- + */ +#ifndef RELERROR_H +#define RELERROR_H + +#include "utils/relcache.h" + +extern int errrelation_column(Relation rel, const char *colname); +extern int errrelation(Relation rel); +extern int errconstraint(Relation rel, const char *cname); + +#endif /* RELERROR_H */ diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c index 173af2e..e83a8b7 100644 --- a/src/interfaces/libpq/fe-protocol3.c +++ b/src/interfaces/libpq/fe-protocol3.c @@ -980,6 +980,40 @@ pqGetErrorNotice3(PGconn *conn, bool isError) 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); } /*