Hello

I am sending a updated patch

>
> Coding Review
> -----------------
>
>
> In tupdesc.c
>
> line 202 the existing code is performing a deep copy of ConstrCheck.  Do you
> need to copy nkeys and conkey here as well?
>
> Then at line 250 ccname is freed but not conkey
>

fixed

>
> postgres_ext.h line 55
> + #define PG_DIAG_SCHEMA_NAME    's'
> + #define PG_DIAG_TABLE_NAME    't'
> + #define PG_DIAG_COLUMN_NAMES    'c'
> + #define PG_DIAG_CONSTRAINT_NAME 'n'
>
> The assignment of letters to parameters seems arbitrary to me, I don't have
> a different non-arbitrary mapping in mind but if anyone else does they
> should speak up.  I think it will be difficult to change this after 9.2 goes
> out.
>
>
> elog.c:
> ***************
> *** 2197,2202 ****
> --- 2299,2319 ----
>      if (application_name)
>          appendCSVLiteral(&buf, application_name);
>
> +     /* constraint_name */
> +     appendCSVLiteral(&buf, edata->constraint_name);
> +     appendStringInfoChar(&buf, ',');
> +
> +     /* schema name */
> +     appendCSVLiteral(&buf, edata->schema_name);
> +     appendStringInfoChar(&buf, ',');
>
> You need to update config.sgml at the same time you update this format.
> You need to append a "," after application name but before constraintName.
> As it stands the CSV log has something like:
> .....nbtinsert.c:433","psql""a_pkey","public","a","a"

fixed

>
>
> nbtinsert.c
>
> pg_get_indrelation is named differently than everything else in this file
> (ie _bt...).  My guess is that this function belongs somewhere else but I
> don't know the code well enough to say where you should move it too.
>

I renamed this function to IndexRelationGetParentRelation and muved to
relcache.c

I don't call a quote_identifier on only data error properties like
table_name or schema_name (but I am open to arguments for it or
against it). The quote_identifier is used for column names, because
there should be a more names and comma should be used inside name -
and this is consistent with pg_get_indexdef_columns.

Regards

Pavel Stehule
*** ./doc/src/sgml/config.sgml.orig	2011-06-20 18:08:39.000000000 +0200
--- ./doc/src/sgml/config.sgml	2011-06-20 21:12:31.688165497 +0200
***************
*** 3919,3927 ****
          user query that led to the error (if any and enabled by
          <varname>log_min_error_statement</>),
          character count of the error position therein,
!         location of the error in the PostgreSQL source code
          (if <varname>log_error_verbosity</> is set to <literal>verbose</>),
!         and application name.
          Here is a sample table definition for storing CSV-format log output:
  
  <programlisting>
--- 3919,3933 ----
          user query that led to the error (if any and enabled by
          <varname>log_min_error_statement</>),
          character count of the error position therein,
!         location of the error in the PostgreSQL source code,
          (if <varname>log_error_verbosity</> is set to <literal>verbose</>),
!         application name,
!         (following fields to end are filled when <varname>log_error_verbosity</> is
!         set to <literal>verbose</>)
!         constraint name,
!         schema name,
!         table name,
!         and column names.
          Here is a sample table definition for storing CSV-format log output:
  
  <programlisting>
***************
*** 3950,3955 ****
--- 3956,3965 ----
    query_pos integer,
    location text,
    application_name text,
+   constraint_name text,
+   schema_name text,
+   table_name text,
+   column_names text,
    PRIMARY KEY (session_id, session_line_num)
  );
  </programlisting>
*** ./src/backend/access/common/tupdesc.c.orig	2011-06-20 18:08:39.000000000 +0200
--- ./src/backend/access/common/tupdesc.c	2011-06-20 20:30:15.477883102 +0200
***************
*** 200,205 ****
--- 200,217 ----
  					cpy->check[i].ccname = pstrdup(constr->check[i].ccname);
  				if (constr->check[i].ccbin)
  					cpy->check[i].ccbin = pstrdup(constr->check[i].ccbin);
+ 				if (constr->check[i].nkeys > 0)
+ 				{
+ 					cpy->check[i].conkey = palloc(sizeof(int16) * constr->check[i].nkeys);
+ 					memcpy(cpy->check[i].conkey, constr->check[i].conkey,
+ 										sizeof(int16) * constr->check[i].nkeys);
+ 					cpy->check[i].nkeys = constr->check[i].nkeys;
+ 				}
+ 				else
+ 				{
+ 					cpy->check[i].conkey = NULL;
+ 					constr->check[i].nkeys = 0;
+ 				}
  			}
  		}
  
***************
*** 249,254 ****
--- 261,268 ----
  					pfree(check[i].ccname);
  				if (check[i].ccbin)
  					pfree(check[i].ccbin);
+ 				if (check[i].conkey)
+ 					pfree(check[i].conkey);
  			}
  			pfree(check);
  		}
***************
*** 409,414 ****
--- 423,431 ----
  			 * Similarly, don't assume that the checks are always read in the
  			 * same order; match them up by name and contents. (The name
  			 * *should* be unique, but...)
+ 			 *
+ 			 * nkeys and conkey depends on ccbin, and there are not neccessary
+ 			 * to compare it.
  			 */
  			for (j = 0; j < n; check2++, j++)
  			{
*** ./src/backend/access/nbtree/nbtinsert.c.orig	2011-06-20 18:08:39.000000000 +0200
--- ./src/backend/access/nbtree/nbtinsert.c	2011-06-20 20:50:45.067330278 +0200
***************
*** 23,29 ****
--- 23,31 ----
  #include "storage/lmgr.h"
  #include "storage/predicate.h"
  #include "utils/inval.h"
+ #include "utils/builtins.h"
  #include "utils/tqual.h"
+ #include "utils/relcache.h"
  
  
  typedef struct
***************
*** 394,400 ****
  										RelationGetRelationName(rel)),
  								 errdetail("Key %s already exists.",
  										   BuildIndexValueDescription(rel,
! 														  values, isnull))));
  					}
  				}
  				else if (all_dead)
--- 396,405 ----
  										RelationGetRelationName(rel)),
  								 errdetail("Key %s already exists.",
  										   BuildIndexValueDescription(rel,
! 														  values, isnull)),
! 								 errconstrname(RelationGetRelationName(rel)),
! 								 errrel(IndexRelationGetParentRelation(rel)),
! 								 errcolnames(pg_get_indexdef_columns(RelationGetRelid(rel), true))));
  					}
  				}
  				else if (all_dead)
***************
*** 534,540 ****
  				   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,
--- 539,548 ----
  				   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."),
! 			errconstrname(RelationGetRelationName(rel)),
! 			errrel(IndexRelationGetParentRelation(rel)),
! 			errcolnames(pg_get_indexdef_columns(RelationGetRelid(rel), true))));
  
  	/*----------
  	 * If we will need to split the page to put the item on this page,
*** ./src/backend/executor/execMain.c.orig	2011-06-20 18:08:39.117098297 +0200
--- ./src/backend/executor/execMain.c	2011-06-20 21:35:58.047263844 +0200
***************
*** 57,62 ****
--- 57,63 ----
  #include "storage/smgr.h"
  #include "tcop/utility.h"
  #include "utils/acl.h"
+ #include "utils/builtins.h"
  #include "utils/lsyscache.h"
  #include "utils/memutils.h"
  #include "utils/snapmgr.h"
***************
*** 1485,1491 ****
  /*
   * ExecRelCheck --- check that tuple meets constraints for result relation
   */
! static const char *
  ExecRelCheck(ResultRelInfo *resultRelInfo,
  			 TupleTableSlot *slot, EState *estate)
  {
--- 1486,1492 ----
  /*
   * ExecRelCheck --- check that tuple meets constraints for result relation
   */
! static ConstrCheck *
  ExecRelCheck(ResultRelInfo *resultRelInfo,
  			 TupleTableSlot *slot, EState *estate)
  {
***************
*** 1537,1543 ****
  		 * ExecQual to return TRUE for NULL.
  		 */
  		if (!ExecQual(qual, econtext, true))
! 			return check[i].ccname;
  	}
  
  	/* NULL result means no error */
--- 1538,1544 ----
  		 * ExecQual to return TRUE for NULL.
  		 */
  		if (!ExecQual(qual, econtext, true))
! 			return &check[i];
  	}
  
  	/* NULL result means no error */
***************
*** 1565,1583 ****
  				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))));
  		}
  	}
  
  	if (constr->num_check > 0)
  	{
! 		const char *failed;
  
  		if ((failed = ExecRelCheck(resultRelInfo, slot, estate)) != NULL)
  			ereport(ERROR,
  					(errcode(ERRCODE_CHECK_VIOLATION),
  					 errmsg("new row for relation \"%s\" violates check constraint \"%s\"",
! 							RelationGetRelationName(rel), failed)));
  	}
  }
  
--- 1566,1605 ----
  				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)),
! 						 errconstrname("not_null_constraint"),
! 						 errrel(rel),
! 						 errcolnames(quote_identifier(NameStr(rel->rd_att->attrs[attrChk - 1]->attname)))));
  		}
  	}
  
  	if (constr->num_check > 0)
  	{
! 		ConstrCheck *failed;
  
  		if ((failed = ExecRelCheck(resultRelInfo, slot, estate)) != NULL)
+ 		{
+ 			StringInfoData names;
+ 			int		idx;
+ 
+ 			/* Get a column names specified by conkey array */
+ 			initStringInfo(&names);
+ 			for (idx = 0; idx < failed->nkeys; idx++)
+ 			{
+ 				if (idx > 0)
+ 					appendStringInfoString(&names, ", ");
+ 				appendStringInfoString(&names, quote_identifier(NameStr(rel->rd_att->attrs[failed->conkey[idx] - 1]->attname)));
+ 			}
+ 
  			ereport(ERROR,
  					(errcode(ERRCODE_CHECK_VIOLATION),
  					 errmsg("new row for relation \"%s\" violates check constraint \"%s\"",
! 							RelationGetRelationName(rel), failed->ccname),
! 					 errconstrname(failed->ccname),
! 					 errrel(rel),
! 					 errcolnames(names.data)));
! 			pfree(names.data);
! 		}
  	}
  }
  
*** ./src/backend/executor/execUtils.c.orig	2011-06-20 18:08:39.000000000 +0200
--- ./src/backend/executor/execUtils.c	2011-06-20 20:59:42.740717038 +0200
***************
*** 44,49 ****
--- 44,50 ----
  
  #include "access/genam.h"
  #include "access/heapam.h"
+ #include "access/nbtree.h"
  #include "access/relscan.h"
  #include "access/transam.h"
  #include "catalog/index.h"
***************
*** 51,56 ****
--- 52,58 ----
  #include "nodes/nodeFuncs.h"
  #include "parser/parsetree.h"
  #include "storage/lmgr.h"
+ #include "utils/builtins.h"
  #include "utils/memutils.h"
  #include "utils/relcache.h"
  #include "utils/tqual.h"
***************
*** 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);
--- 1306,1325 ----
  					 errmsg("could not create exclusion constraint \"%s\"",
  							RelationGetRelationName(index)),
  					 errdetail("Key %s conflicts with key %s.",
! 							   error_new, error_existing),
! 					 errconstrname(RelationGetRelationName(index)),
! 					 errrel(IndexRelationGetParentRelation(index)),
! 					 errcolnames(pg_get_indexdef_columns(RelationGetRelid(index), true))));
  		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),
! 					 errconstrname(RelationGetRelationName(index)),
! 					 errrel(IndexRelationGetParentRelation(index)),
! 					 errcolnames(pg_get_indexdef_columns(RelationGetRelid(index), true))));
  	}
  
  	index_endscan(index_scan);
*** ./src/backend/utils/adt/ri_triggers.c.orig	2011-06-20 18:08:39.000000000 +0200
--- ./src/backend/utils/adt/ri_triggers.c	2011-06-20 20:53:25.261955709 +0200
***************
*** 2839,2845 ****
  						 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
  								RelationGetRelationName(fk_rel),
  								constrname),
! 						 errdetail("MATCH FULL does not allow mixing of null and nonnull key values.")));
  		}
  
  		/*
--- 2839,2847 ----
  						 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
  								RelationGetRelationName(fk_rel),
  								constrname),
! 						 errdetail("MATCH FULL does not allow mixing of null and nonnull key values."),
! 						 errconstrname(constrname),
! 						 errrel(fk_rel)));
  		}
  
  		/*
***************
*** 3537,3543 ****
  				 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
  						RelationGetRelationName(fk_rel), constrname),
  				 errdetail("No rows were found in \"%s\".",
! 						   RelationGetRelationName(pk_rel))));
  	}
  
  	/* Get printable versions of the keys involved */
--- 3539,3547 ----
  				 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
  						RelationGetRelationName(fk_rel), constrname),
  				 errdetail("No rows were found in \"%s\".",
! 						   RelationGetRelationName(pk_rel)),
! 				 errconstrname(constrname),
! 				 errrel(fk_rel)));
  	}
  
  	/* Get printable versions of the keys involved */
***************
*** 3570,3585 ****
  						RelationGetRelationName(fk_rel), constrname),
  				 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),
  				 errmsg("update or delete on table \"%s\" violates foreign key constraint \"%s\" on table \"%s\"",
  						RelationGetRelationName(pk_rel),
  						constrname, RelationGetRelationName(fk_rel)),
! 			errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
  					  key_names.data, key_values.data,
! 					  RelationGetRelationName(fk_rel))));
  }
  
  /* ----------
--- 3574,3595 ----
  						RelationGetRelationName(fk_rel), constrname),
  				 errdetail("Key (%s)=(%s) is not present in table \"%s\".",
  						   key_names.data, key_values.data,
! 						   RelationGetRelationName(pk_rel)),
! 				 errconstrname(constrname),
! 				 errrel(fk_rel),
! 				 errcolnames(key_names.data)));
  	else
  		ereport(ERROR,
  				(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
  				 errmsg("update or delete on table \"%s\" violates foreign key constraint \"%s\" on table \"%s\"",
  						RelationGetRelationName(pk_rel),
  						constrname, RelationGetRelationName(fk_rel)),
! 				 errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
  					  key_names.data, key_values.data,
! 					  RelationGetRelationName(fk_rel)),
! 					  errconstrname(constrname),
! 					  errrel(pk_rel),
! 					  errcolnames(key_names.data)));
  }
  
  /* ----------
*** ./src/backend/utils/cache/relcache.c.orig	2011-06-20 18:52:45.894162522 +0200
--- ./src/backend/utils/cache/relcache.c	2011-06-20 20:32:45.254598114 +0200
***************
*** 3264,3269 ****
--- 3264,3303 ----
  
  		check[found].ccbin = MemoryContextStrdup(CacheMemoryContext,
  												 TextDatumGetCString(val));
+ 		/* 
+ 		 * We should to load a conkey column. This numbers will be translated
+ 		 * to column names available via PG_COLUMN_NAMES property for GET STACKED 
+ 		 * DIAGNOSTICS statement.
+ 		 */
+ 		val = fastgetattr(htup,
+ 						  Anum_pg_constraint_conkey,
+ 						  conrel->rd_att, &isnull);
+ 		if (!isnull)
+ 		{
+ 			ArrayType   *arr;
+ 			int		numkeys;
+ 
+ 			arr = DatumGetArrayTypeP(val);	/* ensure not toasted */
+ 			numkeys = ARR_DIMS(arr)[0];
+ 			if (ARR_NDIM(arr) != 1 ||
+ 				numkeys < 0 ||
+ 				ARR_HASNULL(arr) ||
+ 				ARR_ELEMTYPE(arr) != INT2OID)
+ 
+ 				elog(ERROR, "conkey is not a 1-D smallint array");
+ 			check[found].nkeys = numkeys;
+ 			check[found].conkey = MemoryContextAlloc(CacheMemoryContext,
+ 												numkeys * sizeof(int16));
+ 			memcpy(check[found].conkey, ARR_DATA_PTR(arr), numkeys * sizeof(int16));
+ 
+ 			if ((Pointer) arr != DatumGetPointer(val))
+ 				pfree(arr);				/* free de-toasted copy, if any */
+ 		}
+ 		else
+ 		{
+ 			check[found].nkeys = 0;
+ 			check[found].conkey = NULL;
+ 		}
  		found++;
  	}
  
***************
*** 4584,4586 ****
--- 4618,4651 ----
  			elog(LOG, "could not remove cache file \"%s\": %m", initfilename);
  	}
  }
+ 
+ /*
+  * Returns a parent relation of index relation
+  */
+ Relation 
+ IndexRelationGetParentRelation(Relation indexRelation)
+ {
+ 	HeapTuple	ht_idx;
+ 	Form_pg_index idxrec;
+ 	Oid			indrelid;
+ 	Relation r;
+ 
+ 	/*
+ 	 * Fetch the pg_index tuple by the Oid of the index
+ 	 */
+ 	ht_idx = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(RelationGetRelid(indexRelation)));
+ 	if (!HeapTupleIsValid(ht_idx))
+ 		elog(ERROR, "cache lookup failed for index %u", RelationGetRelid(indexRelation));
+ 	idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
+ 
+ 	indrelid = idxrec->indrelid;
+ 	r = RelationIdGetRelation(indrelid);
+ 
+ 	if (!RelationIsValid(r))
+ 		elog(ERROR, "could not open relation with OID %u", indrelid);
+ 
+ 	/* Clean up */
+ 	ReleaseSysCache(ht_idx);
+ 
+ 	return r;
+ }
*** ./src/backend/utils/error/elog.c.orig	2011-06-20 18:08:39.000000000 +0200
--- ./src/backend/utils/error/elog.c	2011-06-20 20:13:52.818261185 +0200
***************
*** 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/relcache.h"
  
  
  #undef _
***************
*** 972,977 ****
--- 974,1056 ----
  	return 0;					/* return value does not matter */
  }
  
+ /* 
+  * errconstraintname - add name of constraint related to current error
+  */
+ int
+ errconstrname(const char *constraint_name)
+ {
+ 	ErrorData  *edata = &errordata[errordata_stack_depth];
+ 
+ 	/* we don't bother incrementing recursion_depth */
+ 	CHECK_STACK_DEPTH();
+ 
+ 	if (edata->constraint_name)
+ 	{
+ 		pfree(edata->constraint_name);
+ 		edata->constraint_name = NULL;
+ 	}
+ 
+ 	if (constraint_name)
+ 		edata->constraint_name = MemoryContextStrdup(ErrorContext, constraint_name);
+ 
+ 	return 0;					/* return value does not matter */
+ }
+ 
+ /*
+  * errrel - add a schema name and table name of table related to current error
+  */
+ int
+ errrel(Relation rel)
+ {
+ 	ErrorData  *edata = &errordata[errordata_stack_depth];
+ 	MemoryContext oldcontext;
+ 
+ 	/* we don't bother incrementing recursion_depth */
+ 	CHECK_STACK_DEPTH();
+ 
+ 	if (edata->schema_name)
+ 		pfree(edata->schema_name);
+ 
+ 	if (edata->table_name)
+ 		pfree(edata->table_name);
+ 
+ 	oldcontext = MemoryContextSwitchTo(ErrorContext);
+ 
+ 	edata->schema_name = get_namespace_name(RelationGetNamespace(rel));
+ 	edata->table_name = pstrdup(RelationGetRelationName(rel));
+ 
+ 	MemoryContextSwitchTo(oldcontext);
+ 
+ 	return 0;					/* return value does not matter */
+ }
+ 
+ 
+ /* 
+  * errcolnames - add names of columns related to current error
+  */
+ int
+ errcolnames(const char *column_names)
+ {
+ 	ErrorData  *edata = &errordata[errordata_stack_depth];
+ 
+ 	/* we don't bother incrementing recursion_depth */
+ 	CHECK_STACK_DEPTH();
+ 
+ 	if (edata->column_names)
+ 	{
+ 		pfree(edata->column_names);
+ 		edata->column_names = NULL;
+ 	}
+ 
+ 	if (column_names)
+ 		edata->column_names = MemoryContextStrdup(ErrorContext, column_names);
+ 
+ 	return 0;					/* return value does not matter */
+ }
+ 
+ 
+ 
  /*
   * errposition --- add cursor position to the current error
   */
***************
*** 1277,1282 ****
--- 1356,1369 ----
  		newedata->hint = pstrdup(newedata->hint);
  	if (newedata->context)
  		newedata->context = pstrdup(newedata->context);
+ 	if (newedata->constraint_name)
+ 		newedata->constraint_name = pstrdup(newedata->constraint_name);
+ 	if (newedata->schema_name)
+ 		newedata->schema_name = pstrdup(newedata->schema_name);
+ 	if (newedata->table_name)
+ 		newedata->table_name = pstrdup(newedata->table_name);
+ 	if (newedata->column_names)
+ 		newedata->column_names = pstrdup(newedata->column_names);
  	if (newedata->internalquery)
  		newedata->internalquery = pstrdup(newedata->internalquery);
  
***************
*** 1302,1307 ****
--- 1389,1402 ----
  		pfree(edata->hint);
  	if (edata->context)
  		pfree(edata->context);
+ 	if (edata->constraint_name)
+ 		pfree(edata->constraint_name);
+ 	if (edata->schema_name)
+ 		pfree(edata->schema_name);
+ 	if (edata->table_name)
+ 		pfree(edata->table_name);
+ 	if (edata->column_names)
+ 		pfree(edata->column_names);
  	if (edata->internalquery)
  		pfree(edata->internalquery);
  	pfree(edata);
***************
*** 1374,1379 ****
--- 1469,1482 ----
  		newedata->hint = pstrdup(newedata->hint);
  	if (newedata->context)
  		newedata->context = pstrdup(newedata->context);
+ 	if (newedata->constraint_name)
+ 		newedata->constraint_name = pstrdup(newedata->constraint_name);
+ 	if (newedata->schema_name)
+ 		newedata->schema_name = pstrdup(newedata->schema_name);
+ 	if (newedata->table_name)
+ 		newedata->table_name = pstrdup(newedata->table_name);
+ 	if (newedata->column_names)
+ 		newedata->column_names = pstrdup(newedata->column_names);
  	if (newedata->internalquery)
  		newedata->internalquery = pstrdup(newedata->internalquery);
  
***************
*** 2196,2201 ****
--- 2299,2324 ----
  	/* application name */
  	if (application_name)
  		appendCSVLiteral(&buf, application_name);
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 	/* constraint_name */
+ 	if (edata->constraint_name && Log_error_verbosity >= PGERROR_VERBOSE)
+ 		appendCSVLiteral(&buf, edata->constraint_name);
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 	/* schema name */
+ 	if (edata->schema_name && Log_error_verbosity >= PGERROR_VERBOSE)
+ 		appendCSVLiteral(&buf, edata->schema_name);
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 	/* table name */
+ 	if (edata->table_name && Log_error_verbosity >= PGERROR_VERBOSE)
+ 		appendCSVLiteral(&buf, edata->table_name);
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 	/* column names */
+ 		if (edata->column_names && Log_error_verbosity >= PGERROR_VERBOSE)
+ 	appendCSVLiteral(&buf, edata->column_names);
  
  	appendStringInfoChar(&buf, '\n');
  
***************
*** 2314,2319 ****
--- 2437,2471 ----
  				appendStringInfo(&buf, _("LOCATION:  %s:%d\n"),
  								 edata->filename, edata->lineno);
  			}
+ 
+ 			if (edata->constraint_name)
+ 			{
+ 				log_line_prefix(&buf, edata);
+ 				appendStringInfoString(&buf, _("CONSTRAINT:  "));
+ 				append_with_tabs(&buf, edata->constraint_name);
+ 				appendStringInfoChar(&buf, '\n');
+ 			}
+ 			if (edata->schema_name)
+ 			{
+ 				log_line_prefix(&buf, edata);
+ 				appendStringInfoString(&buf, _("SCHEMA:  "));
+ 				append_with_tabs(&buf, edata->schema_name);
+ 				appendStringInfoChar(&buf, '\n');
+ 			}
+ 			if (edata->table_name)
+ 			{
+ 				log_line_prefix(&buf, edata);
+ 				appendStringInfoString(&buf, _("TABLE:  "));
+ 				append_with_tabs(&buf, edata->table_name);
+ 				appendStringInfoChar(&buf, '\n');
+ 			}
+ 			if (edata->column_names)
+ 			{
+ 				log_line_prefix(&buf, edata);
+ 				appendStringInfoString(&buf, _("COLUMN:  "));
+ 				append_with_tabs(&buf, edata->column_names);
+ 				appendStringInfoChar(&buf, '\n');
+ 			}
  		}
  	}
  
***************
*** 2552,2557 ****
--- 2704,2733 ----
  			err_sendstring(&msgbuf, edata->context);
  		}
  
+ 		if (edata->constraint_name)
+ 		{
+ 			pq_sendbyte(&msgbuf, PG_DIAG_CONSTRAINT_NAME);
+ 			err_sendstring(&msgbuf, edata->constraint_name);
+ 		}
+ 
+ 		if (edata->schema_name)
+ 		{
+ 			pq_sendbyte(&msgbuf, PG_DIAG_SCHEMA_NAME);
+ 			err_sendstring(&msgbuf, edata->schema_name);
+ 		}
+ 
+ 		if (edata->table_name)
+ 		{
+ 			pq_sendbyte(&msgbuf, PG_DIAG_TABLE_NAME);
+ 			err_sendstring(&msgbuf, edata->table_name);
+ 		}
+ 
+ 		if (edata->column_names)
+ 		{
+ 			pq_sendbyte(&msgbuf, PG_DIAG_COLUMN_NAMES);
+ 			err_sendstring(&msgbuf, edata->column_names);
+ 		}
+ 
  		if (edata->cursorpos > 0)
  		{
  			snprintf(tbuf, sizeof(tbuf), "%d", edata->cursorpos);
*** ./src/include/access/tupdesc.h.orig	2011-06-20 19:45:49.000000000 +0200
--- ./src/include/access/tupdesc.h	2011-06-20 19:49:26.854819079 +0200
***************
*** 29,34 ****
--- 29,36 ----
  {
  	char	   *ccname;
  	char	   *ccbin;			/* nodeToString representation of expr */
+ 	int16	   nkeys;			/* numbers of columns related to constraint */
+ 	int16	   *conkey;			/* array numbers of columns related to constraint */
  } ConstrCheck;
  
  /* This structure contains constraints of a tuple */
*** ./src/include/postgres_ext.h.orig	2011-06-20 19:45:49.000000000 +0200
--- ./src/include/postgres_ext.h	2011-06-20 19:49:26.854819079 +0200
***************
*** 55,59 ****
--- 55,63 ----
  #define PG_DIAG_SOURCE_FILE		'F'
  #define PG_DIAG_SOURCE_LINE		'L'
  #define PG_DIAG_SOURCE_FUNCTION 'R'
+ #define PG_DIAG_SCHEMA_NAME	's'
+ #define PG_DIAG_TABLE_NAME	't'
+ #define PG_DIAG_COLUMN_NAMES	'c'
+ #define PG_DIAG_CONSTRAINT_NAME 'n'
  
  #endif
*** ./src/include/utils/elog.h.orig	2011-06-20 18:08:39.000000000 +0200
--- ./src/include/utils/elog.h	2011-06-20 19:49:26.855819068 +0200
***************
*** 15,20 ****
--- 15,21 ----
  #define ELOG_H
  
  #include <setjmp.h>
+ #include "utils/relcache.h"
  
  /* Error level codes */
  #define DEBUG5		10			/* Debugging messages, in categories of
***************
*** 177,182 ****
--- 178,187 ----
  extern int	errfunction(const char *funcname);
  extern int	errposition(int cursorpos);
  
+ extern int	errconstrname(const char *constraint_name);
+ extern int	errrel(Relation rel);
+ extern int	errcolnames(const char *column_names);
+ 
  extern int	internalerrposition(int cursorpos);
  extern int	internalerrquery(const char *query);
  
***************
*** 315,320 ****
--- 320,329 ----
  	char	   *detail_log;		/* detail error message for server log only */
  	char	   *hint;			/* hint message */
  	char	   *context;		/* context message */
+ 	char		*constraint_name;	/* name of constraint related to error */
+ 	char		*schema_name;		/* schema of table related to error */
+ 	char		*table_name;		/* name of table related to error */
+ 	char		*column_names;		/* names of columns related to error */
  	int			cursorpos;		/* cursor index into query string */
  	int			internalpos;	/* cursor index into internalquery */
  	char	   *internalquery;	/* text of internally-generated query */
*** ./src/include/utils/relcache.h.orig	2011-06-20 18:08:39.000000000 +0200
--- ./src/include/utils/relcache.h	2011-06-20 18:55:00.718993324 +0200
***************
*** 53,58 ****
--- 53,60 ----
  
  extern void RelationInitIndexAccessInfo(Relation relation);
  
+ extern Relation IndexRelationGetParentRelation(Relation indexRelation);
+ 
  /*
   * Routines for backend startup
   */
*** ./src/interfaces/libpq/fe-protocol3.c.orig	2011-06-20 19:45:49.000000000 +0200
--- ./src/interfaces/libpq/fe-protocol3.c	2011-06-20 19:49:26.856819058 +0200
***************
*** 856,861 ****
--- 856,874 ----
  								  valf, vall);
  			appendPQExpBufferChar(&workBuf, '\n');
  		}
+ 
+ 		val = PQresultErrorField(res, PG_DIAG_CONSTRAINT_NAME);
+ 		if (val)
+ 			appendPQExpBuffer(&workBuf, libpq_gettext("CONSTRAINT:  %s\n"), val);
+ 		val = PQresultErrorField(res, PG_DIAG_SCHEMA_NAME);
+ 		if (val)
+ 			appendPQExpBuffer(&workBuf, libpq_gettext("SCHEMA:  %s\n"), val);
+ 		val = PQresultErrorField(res, PG_DIAG_TABLE_NAME);
+ 		if (val)
+ 			appendPQExpBuffer(&workBuf, libpq_gettext("TABLE:  %s\n"), val);
+ 		val = PQresultErrorField(res, PG_DIAG_COLUMN_NAMES);
+ 		if (val)
+ 			appendPQExpBuffer(&workBuf, libpq_gettext("COLUMNS:  %s\n"), val);
  	}
  
  	/*
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to