From f32fc17c5f8c1b54c81d05953930c9dce0ad7781 Mon Sep 17 00:00:00 2001
From: Franck Verrot <franck@verrot.fr>
Date: Sun, 23 Aug 2015 11:53:28 +0200
Subject: [PATCH] Report column for which type coercion fails

This commit pushes a new `error_context_stack` callback that helps users
understanding which column type coercion fails during INSERTs:

    # create table foo(bar varchar(2), baz int4);
    CREATE TABLE

    # insert into foo values('ok', 1234);
    INSERT 0 1

    # insert into foo values('ko', 'abcd');
    ERROR:  invalid input syntax for integer: "abcd"
    LINE 1: insert into foo values('ko', 'abcd');
                                         ^
    DETAIL:  Coercion failed for column "baz" of type integer.
---
 src/backend/parser/parse_target.c | 23 +++++++++++++++++++++++
 src/include/parser/parse_target.h |  7 +++++++
 2 files changed, 30 insertions(+)

diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 1b3fcd6..c03ce7c 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -389,6 +389,17 @@ markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
  * omits the column name list.  So we should usually prefer to use
  * exprLocation(expr) for errors that can happen in a default INSERT.
  */
+
+void
+TransformExprCallback(void *arg)
+{
+	TransformExprState *state = (TransformExprState *) arg;
+
+	errdetail("Coercion failed for column \"%s\" of type %s.",
+			state->column_name,
+			format_type_be(state->expected_type));
+}
+
 Expr *
 transformAssignedExpr(ParseState *pstate,
 					  Expr *expr,
@@ -405,6 +416,14 @@ transformAssignedExpr(ParseState *pstate,
 	Oid			attrcollation;	/* collation of target column */
 	ParseExprKind sv_expr_kind;
 
+	ErrorContextCallback errcallback;
+	TransformExprState testate;
+
+	/* Set up callback to identify error line number. */
+	errcallback.callback = TransformExprCallback;
+	errcallback.arg = (void *) &testate;
+	errcallback.previous = error_context_stack;
+	error_context_stack = &errcallback;
 	/*
 	 * Save and restore identity of expression type we're parsing.  We must
 	 * set p_expr_kind here because we can parse subscripts without going
@@ -425,6 +444,9 @@ transformAssignedExpr(ParseState *pstate,
 	attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod;
 	attrcollation = rd->rd_att->attrs[attrno - 1]->attcollation;
 
+	testate.column_name = colname;
+	testate.expected_type = attrtype;
+
 	/*
 	 * If the expression is a DEFAULT placeholder, insert the attribute's
 	 * type/typmod/collation into it so that exprType etc will report the
@@ -529,6 +551,7 @@ transformAssignedExpr(ParseState *pstate,
 					 parser_errposition(pstate, exprLocation(orig_expr))));
 	}
 
+	error_context_stack = errcallback.previous;
 	pstate->p_expr_kind = sv_expr_kind;
 
 	return expr;
diff --git a/src/include/parser/parse_target.h b/src/include/parser/parse_target.h
index a86b79d..5e12c12 100644
--- a/src/include/parser/parse_target.h
+++ b/src/include/parser/parse_target.h
@@ -42,4 +42,11 @@ extern TupleDesc expandRecordVariable(ParseState *pstate, Var *var,
 extern char *FigureColname(Node *node);
 extern char *FigureIndexColname(Node *node);
 
+/* Support for TransformExprCallback */
+typedef struct TransformExprState
+{
+	const char *column_name;
+	Oid expected_type;
+} TransformExprState;
+
 #endif   /* PARSE_TARGET_H */
-- 
2.4.6

