From 73dd2cd096c91cee1b501d5f94ba81037de30fd1 Mon Sep 17 00:00:00 2001
From: Franck Verrot <franck@verrot.fr>
Date: Sun, 9 Aug 2015 14:37:59 +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');
                                         ^
    HINT:  coercion failed for column "baz" of type integer
---
 src/backend/parser/parse_target.c | 23 +++++++++++++++++++++++
 src/include/parser/parse_node.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..6915d2c 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;
+
+	errhint("coercion failed for column \"%s\" of type %s\n",
+			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_node.h b/src/include/parser/parse_node.h
index 5249945..69c77f1 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -233,4 +233,11 @@ extern ArrayRef *transformArraySubscripts(ParseState *pstate,
 						 Node *assignFrom);
 extern Const *make_const(ParseState *pstate, Value *value, int location);
 
+void TransformExprCallback(void *arg);
+typedef struct TransformExprState
+{
+  const char *column_name;
+  Oid expected_type;
+} TransformExprState;
+
 #endif   /* PARSE_NODE_H */
-- 
2.4.6

