diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 3a0b16d..13c42fa 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -4153,6 +4153,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..7a5005f 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -393,7 +393,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 +456,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 +535,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 70e408c..1b1877b 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -3808,6 +3808,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 +3826,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 */
@@ -6619,7 +6625,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..0d78c34 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2233,7 +2233,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 +2604,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..607d661 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1522,7 +1522,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 +1538,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..805515e 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -1304,14 +1304,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..11f431f 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -338,7 +338,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 +2468,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 +3223,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 +3235,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/Makefile b/src/backend/utils/error/Makefile
index 4c313b7..c2ba4d0 100644
--- a/src/backend/utils/error/Makefile
+++ b/src/backend/utils/error/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/utils/error
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = assert.o elog.o
+OBJS = assert.o elog.o relerror.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index a40b343..9a47b99 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -477,6 +477,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 +1101,116 @@ internalerrquery(const char *query)
}
/*
+ * 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);
+}
+
+/*
+ * err_generic_string -- generic setting of ErrorData string fields
+ */
+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_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 +1484,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 +1531,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 +1625,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();
@@ -2238,7 +2436,7 @@ write_csvlog(ErrorData *edata)
appendStringInfoChar(&buf, ',');
/* file error location */
- if (Log_error_verbosity >= PGERROR_VERBOSE)
+
{
StringInfoData msgbuf;
@@ -2259,6 +2457,50 @@ write_csvlog(ErrorData *edata)
/* application name */
if (application_name)
appendCSVLiteral(&buf, application_name);
+ appendStringInfoChar(&buf, ',');
+
+ if (Log_error_verbosity >= PGERROR_VERBOSE)
+ appendCSVLiteral(&buf, edata->column_name);
+ appendStringInfoChar(&buf, ',');
+
+ if (Log_error_verbosity >= PGERROR_VERBOSE)
+ appendCSVLiteral(&buf, edata->table_name);
+ appendStringInfoChar(&buf, ',');
+
+ if (Log_error_verbosity >= PGERROR_VERBOSE)
+ appendCSVLiteral(&buf, edata->schema_name);
+ appendStringInfoChar(&buf, ',');
+
+ if (Log_error_verbosity >= PGERROR_VERBOSE)
+ appendCSVLiteral(&buf, edata->constraint_name);
+ appendStringInfoChar(&buf, ',');
+
+ if (Log_error_verbosity >= PGERROR_VERBOSE)
+ appendCSVLiteral(&buf, edata->constraint_table);
+ appendStringInfoChar(&buf, ',');
+
+ if (Log_error_verbosity >= PGERROR_VERBOSE)
+ appendCSVLiteral(&buf, edata->constraint_schema);
+ appendStringInfoChar(&buf, ',');
+
+ if (Log_error_verbosity >= PGERROR_VERBOSE)
+ appendCSVLiteral(&buf, edata->routine_name);
+ appendStringInfoChar(&buf, ',');
+
+ if (Log_error_verbosity >= PGERROR_VERBOSE)
+ appendCSVLiteral(&buf, edata->routine_schema);
+ appendStringInfoChar(&buf, ',');
+
+ if (Log_error_verbosity >= PGERROR_VERBOSE)
+ appendCSVLiteral(&buf, edata->trigger_name);
+ appendStringInfoChar(&buf, ',');
+
+ if (Log_error_verbosity >= PGERROR_VERBOSE)
+ appendCSVLiteral(&buf, edata->trigger_table);
+ appendStringInfoChar(&buf, ',');
+
+ if (Log_error_verbosity >= PGERROR_VERBOSE)
+ appendCSVLiteral(&buf, edata->trigger_schema);
appendStringInfoChar(&buf, '\n');
@@ -2377,6 +2619,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 +2992,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/error/relerror.c b/src/backend/utils/error/relerror.c
new file mode 100644
index 0000000..5e50333
--- /dev/null
+++ b/src/backend/utils/error/relerror.c
@@ -0,0 +1,61 @@
+/*-------------------------------------------------------------------------
+ *
+ * relerror.c
+ * relation error loging 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"
+
+/*
+ * Sets column_name, table_name and schema_name in ErrorData related to relation
+ */
+int
+errrelation_column(Relation rel, const char *colname)
+{
+ err_generic_string(PG_DIAG_COLUMN_NAME, colname);
+ err_generic_string(PG_DIAG_TABLE_NAME, RelationGetRelationName(rel));
+ err_generic_string(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)
+{
+ err_generic_string(PG_DIAG_TABLE_NAME, RelationGetRelationName(rel));
+ err_generic_string(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)
+{
+ err_generic_string(PG_DIAG_CONSTRAINT_NAME, cname);
+ err_generic_string(PG_DIAG_CONSTRAINT_TABLE, RelationGetRelationName(rel));
+ err_generic_string(PG_DIAG_CONSTRAINT_SCHEMA,
+ get_namespace_name(RelationGetNamespace(rel)));
+
+ return 0;
+}
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index d5a2003..e6ecfeb 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -3090,7 +3090,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..b96adf3 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 err_generic_string(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/rel.h b/src/include/utils/rel.h
index 4669d8a..32dcae4 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -394,4 +394,9 @@ typedef struct StdRdOptions
extern void RelationIncrementReferenceCount(Relation rel);
extern void RelationDecrementReferenceCount(Relation rel);
+/* routines in utils/error/relerror.c */
+extern int errrelation_column(Relation rel, const char *colname);
+extern int errrelation(Relation rel);
+extern int errconstraint(Relation rel, const char *cname);
+
#endif /* REL_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);
}
/*