Attached is my work in progress to implement the changes to the CAST()
function as proposed by Vik Fearing.

This work builds upon the Error-safe User Functions work currently ongoing.

The proposed changes are as follows:

CAST(expr AS typename)
    continues to behave as before.

CAST(expr AS typename ERROR ON ERROR)
    has the identical behavior as the unadorned CAST() above.

CAST(expr AS typename NULL ON ERROR)
    will use error-safe functions to do the cast of expr, and will return
NULL if the cast fails.

CAST(expr AS typename DEFAULT expr2 ON ERROR)
    will use error-safe functions to do the cast of expr, and will return
expr2 if the cast fails.

There is an additional FORMAT parameter that I have not yet implemented, my
understanding is that it is largely intended for DATE/TIME field
conversions, but others are certainly possible.
CAST(expr AS typename FORMAT fmt DEFAULT expr2 ON ERROR)

What is currently working:
- Any scalar expression that can be evaluated at parse time. These tests
from cast.sql all currently work:

VALUES (CAST('error' AS integer));
VALUES (CAST('error' AS integer ERROR ON ERROR));
VALUES (CAST('error' AS integer NULL ON ERROR));
VALUES (CAST('error' AS integer DEFAULT 42 ON ERROR));

SELECT CAST('{123,abc,456}' AS integer[] DEFAULT '{-789}' ON ERROR) as
array_test1;

- Scalar values evaluated at runtime.

CREATE TEMPORARY TABLE t(t text);
INSERT INTO t VALUES ('a'), ('1'), ('b'), (2);
SELECT CAST(t.t AS integer DEFAULT -1 ON ERROR) AS foo FROM t;
 foo
-----
  -1
   1
  -1
   2
(4 rows)


Along the way, I made a few design decisions, each of which is up for
debate:

First, I created OidInputFunctionCallSafe, which is to OidInputFunctionCall
what InputFunctionCallSafe is to InputFunctionCall. Given that the only
place I ended up using it was stringTypeDatumSafe(), it may be possible to
just move that code inside stringTypeDatumSafe.

Next, I had a need for FuncExpr, CoerceViaIO, and ArrayCoerce to all report
if their expr argument failed, and if not, just past the evaluation of
expr2. Rather than duplicate this logic in several places, I chose instead
to modify CoalesceExpr to allow for an error-test mode in addition to its
default null-test mode, and then to provide this altered node with two
expressions, the first being the error-safe typecast of expr and the second
being the non-error-safe typecast of expr2.

I still don't have array-to-array casts working, as the changed I would
likely need to make to ArrayCoerce get somewhat invasive, so this seemed
like a good time to post my work so far and solicit some feedback beyond
what I've already been getting from Jeff Davis and Michael Paquier.

I've sidestepped domains as well for the time being as well as avoiding JIT
issues entirely.

No documentation is currently prepared. All but one of the regression test
queries work, the one that is currently failing is:

SELECT CAST('{234,def,567}'::text[] AS integer[] DEFAULT '{-1011}' ON
ERROR) as array_test2;

Other quirks:
- an unaliased CAST ON DEFAULT will return the column name of "coalesce",
which internally is true, but obviously would be quite confusing to a user.

As a side observation, I noticed that the optimizer already tries to
resolve expressions based on constants and to collapse expression trees
where possible, which makes me wonder if the work done to do the same in
transformTypeCast/ and coerce_to_target_type and coerce_type isn't also
wasted.
From 897c9c68a29ad0fa57f28734df0c77553e026d80 Mon Sep 17 00:00:00 2001
From: coreyhuinker <corey.huin...@gmail.com>
Date: Sun, 18 Dec 2022 16:20:01 -0500
Subject: [PATCH 1/3] add OidInputFunctionCall

---
 src/backend/utils/fmgr/fmgr.c | 13 +++++++++++++
 src/include/fmgr.h            |  4 ++++
 2 files changed, 17 insertions(+)

diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 0d37f69298..e9a19ce653 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -1701,6 +1701,19 @@ OidInputFunctionCall(Oid functionId, char *str, Oid typioparam, int32 typmod)
 	return InputFunctionCall(&flinfo, str, typioparam, typmod);
 }
 
+bool
+OidInputFunctionCallSafe(Oid functionId, char *str, Oid typioparam,
+						 int32 typmod, fmNodePtr escontext,
+						 Datum *result)
+{
+	FmgrInfo			flinfo;
+
+	fmgr_info(functionId, &flinfo);
+
+	return InputFunctionCallSafe(&flinfo, str, typioparam, typmod,
+								 escontext, result);
+}
+
 char *
 OidOutputFunctionCall(Oid functionId, Datum val)
 {
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index b7832d0aa2..b835ef72b5 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -706,6 +706,10 @@ extern bool InputFunctionCallSafe(FmgrInfo *flinfo, char *str,
 								  Datum *result);
 extern Datum OidInputFunctionCall(Oid functionId, char *str,
 								  Oid typioparam, int32 typmod);
+extern bool
+OidInputFunctionCallSafe(Oid functionId, char *str, Oid typioparam,
+						 int32 typmod, fmNodePtr escontext,
+						 Datum *result);
 extern char *OutputFunctionCall(FmgrInfo *flinfo, Datum val);
 extern char *OidOutputFunctionCall(Oid functionId, Datum val);
 extern Datum ReceiveFunctionCall(FmgrInfo *flinfo, fmStringInfo buf,
-- 
2.38.1

From 1153c977ba153f116170ca2922e0785f9d2604c8 Mon Sep 17 00:00:00 2001
From: coreyhuinker <corey.huin...@gmail.com>
Date: Mon, 19 Dec 2022 13:39:16 -0500
Subject: [PATCH 2/3] add stringTypeDatumSafe

---
 src/backend/parser/parse_type.c | 13 +++++++++++++
 src/include/parser/parse_type.h |  2 ++
 2 files changed, 15 insertions(+)

diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index f7ad689459..0e6dfe3b49 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -19,6 +19,7 @@
 #include "catalog/pg_type.h"
 #include "lib/stringinfo.h"
 #include "nodes/makefuncs.h"
+#include "nodes/miscnodes.h"
 #include "parser/parse_type.h"
 #include "parser/parser.h"
 #include "utils/array.h"
@@ -660,6 +661,18 @@ stringTypeDatum(Type tp, char *string, int32 atttypmod)
 	return OidInputFunctionCall(typinput, string, typioparam, atttypmod);
 }
 
+bool
+stringTypeDatumSafe(Type tp, char *string, int32 atttypmod, Datum *result)
+{
+	Form_pg_type typform = (Form_pg_type) GETSTRUCT(tp);
+	Oid			typinput = typform->typinput;
+	Oid			typioparam = getTypeIOParam(tp);
+	ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+	return OidInputFunctionCallSafe(typinput, string, typioparam, atttypmod,
+									(fmNodePtr) &escontext, result);
+}
+
 /*
  * Given a typeid, return the type's typrelid (associated relation), if any.
  * Returns InvalidOid if type is not a composite type.
diff --git a/src/include/parser/parse_type.h b/src/include/parser/parse_type.h
index 4e5624d721..7468f75823 100644
--- a/src/include/parser/parse_type.h
+++ b/src/include/parser/parse_type.h
@@ -47,6 +47,8 @@ extern char *typeTypeName(Type t);
 extern Oid	typeTypeRelid(Type typ);
 extern Oid	typeTypeCollation(Type typ);
 extern Datum stringTypeDatum(Type tp, char *string, int32 atttypmod);
+extern bool stringTypeDatumSafe(Type tp, char *string, int32 atttypmod,
+								Datum *result);
 
 extern Oid	typeidTypeRelid(Oid type_id);
 extern Oid	typeOrDomainTypeRelid(Oid type_id);
-- 
2.38.1

From 74aaf068a2d071f6fefab27309d7a0252f28ccee Mon Sep 17 00:00:00 2001
From: coreyhuinker <corey.huin...@gmail.com>
Date: Mon, 19 Dec 2022 17:11:49 -0500
Subject: [PATCH 3/3] CAST ON DEFAULT work in progress

---
 src/backend/executor/execExpr.c          | 173 +++++++++++-------
 src/backend/executor/execExprInterp.c    |  35 +++-
 src/backend/jit/llvm/llvmjit_expr.c      |  15 ++
 src/backend/nodes/makefuncs.c            |   4 +-
 src/backend/nodes/nodeFuncs.c            |  15 +-
 src/backend/optimizer/util/clauses.c     |   4 +-
 src/backend/parser/gram.y                |  46 ++++-
 src/backend/parser/parse_agg.c           |  15 +-
 src/backend/parser/parse_coerce.c        | 215 ++++++++++++++++++-----
 src/backend/parser/parse_expr.c          |  87 +++++++--
 src/backend/partitioning/partbounds.c    |   3 +-
 src/backend/rewrite/rewriteSearchCycle.c |   4 +-
 src/include/executor/execExpr.h          |   4 +
 src/include/nodes/execnodes.h            |   4 +
 src/include/nodes/makefuncs.h            |   3 +-
 src/include/nodes/parsenodes.h           |   1 +
 src/include/nodes/primnodes.h            |  12 +-
 src/include/parser/kwlist.h              |   1 +
 src/include/parser/parse_coerce.h        |  15 ++
 src/test/regress/expected/cast.out       |  40 +++++
 src/test/regress/parallel_schedule       |   2 +-
 src/test/regress/sql/cast.sql            |  11 ++
 22 files changed, 551 insertions(+), 158 deletions(-)
 create mode 100644 src/test/regress/expected/cast.out
 create mode 100644 src/test/regress/sql/cast.sql

diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 81429b9f05..9aaa53b67e 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -40,6 +40,7 @@
 #include "jit/jit.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
+#include "nodes/miscnodes.h"
 #include "nodes/nodeFuncs.h"
 #include "nodes/subscripting.h"
 #include "optimizer/optimizer.h"
@@ -61,10 +62,10 @@ typedef struct LastAttnumInfo
 
 static void ExecReadyExpr(ExprState *state);
 static void ExecInitExprRec(Expr *node, ExprState *state,
-							Datum *resv, bool *resnull);
+							Datum *resv, bool *resnull, bool *reserror);
 static void ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args,
 						 Oid funcid, Oid inputcollid,
-						 ExprState *state);
+						 ExprState *state, ErrorSaveContext *escontext);
 static void ExecInitExprSlots(ExprState *state, Node *node);
 static void ExecPushExprSlots(ExprState *state, LastAttnumInfo *info);
 static bool get_last_attnums_walker(Node *node, LastAttnumInfo *info);
@@ -74,11 +75,11 @@ static void ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable,
 static void ExecInitSubscriptingRef(ExprEvalStep *scratch,
 									SubscriptingRef *sbsref,
 									ExprState *state,
-									Datum *resv, bool *resnull);
+									Datum *resv, bool *resnull, bool *reserror);
 static bool isAssignmentIndirectionExpr(Expr *expr);
 static void ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
 								   ExprState *state,
-								   Datum *resv, bool *resnull);
+								   Datum *resv, bool *resnull, bool *reserror);
 static void ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
 								  ExprEvalStep *scratch,
 								  FunctionCallInfo fcinfo, AggStatePerTrans pertrans,
@@ -140,7 +141,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
 	ExecInitExprSlots(state, (Node *) node);
 
 	/* Compile the expression proper */
-	ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
+	ExecInitExprRec(node, state, &state->resvalue, &state->resnull, &state->reserror);
 
 	/* Finally, append a DONE step */
 	scratch.opcode = EEOP_DONE;
@@ -177,7 +178,7 @@ ExecInitExprWithParams(Expr *node, ParamListInfo ext_params)
 	ExecInitExprSlots(state, (Node *) node);
 
 	/* Compile the expression proper */
-	ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
+	ExecInitExprRec(node, state, &state->resvalue, &state->resnull, &state->reserror);
 
 	/* Finally, append a DONE step */
 	scratch.opcode = EEOP_DONE;
@@ -251,7 +252,7 @@ ExecInitQual(List *qual, PlanState *parent)
 		Expr	   *node = (Expr *) lfirst(lc);
 
 		/* first evaluate expression */
-		ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
+		ExecInitExprRec(node, state, &state->resvalue, &state->resnull, &state->reserror);
 
 		/* then emit EEOP_QUAL to detect if it's false (or null) */
 		scratch.d.qualexpr.jumpdone = -1;
@@ -455,7 +456,7 @@ ExecBuildProjectionInfo(List *targetList,
 			 * into the ExprState's resvalue/resnull and then move.
 			 */
 			ExecInitExprRec(tle->expr, state,
-							&state->resvalue, &state->resnull);
+							&state->resvalue, &state->resnull, &state->reserror);
 
 			/*
 			 * Column might be referenced multiple times in upper nodes, so
@@ -659,7 +660,7 @@ ExecBuildUpdateProjection(List *targetList,
 			 * path and it doesn't seem worth expending code for that.
 			 */
 			ExecInitExprRec(tle->expr, state,
-							&state->resvalue, &state->resnull);
+							&state->resvalue, &state->resnull, &state->reserror);
 			/* Needn't worry about read-only-ness here, either. */
 			scratch.opcode = EEOP_ASSIGN_TMP;
 			scratch.d.assign_tmp.resultnum = targetattnum - 1;
@@ -688,7 +689,7 @@ ExecBuildUpdateProjection(List *targetList,
 
 			Assert(tle->resjunk);
 			ExecInitExprRec(tle->expr, state,
-							&state->resvalue, &state->resnull);
+							&state->resvalue, &state->resnull, &state->reserror);
 		}
 	}
 
@@ -899,7 +900,7 @@ ExecReadyExpr(ExprState *state)
  */
 static void
 ExecInitExprRec(Expr *node, ExprState *state,
-				Datum *resv, bool *resnull)
+				Datum *resv, bool *resnull, bool *reserror)
 {
 	ExprEvalStep scratch = {0};
 
@@ -910,6 +911,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 	Assert(resv != NULL && resnull != NULL);
 	scratch.resvalue = resv;
 	scratch.resnull = resnull;
+	scratch.reserror = reserror;
 
 	/* cases should be ordered as they are in enum NodeTag */
 	switch (nodeTag(node))
@@ -1126,17 +1128,24 @@ ExecInitExprRec(Expr *node, ExprState *state,
 			{
 				SubscriptingRef *sbsref = (SubscriptingRef *) node;
 
-				ExecInitSubscriptingRef(&scratch, sbsref, state, resv, resnull);
+				ExecInitSubscriptingRef(&scratch, sbsref, state, resv, resnull, NULL);
 				break;
 			}
 
 		case T_FuncExpr:
 			{
-				FuncExpr   *func = (FuncExpr *) node;
+				FuncExpr		   *func = (FuncExpr *) node;
+				ErrorSaveContext   *escontext = NULL;
+
+				if (unlikely(func->safe_mode))
+				{
+					escontext = palloc0(sizeof(ErrorSaveContext));
+					escontext->type = T_ErrorSaveContext;
+				}
 
 				ExecInitFunc(&scratch, node,
 							 func->args, func->funcid, func->inputcollid,
-							 state);
+							 state, escontext);
 				ExprEvalPushStep(state, &scratch);
 				break;
 			}
@@ -1147,7 +1156,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 				ExecInitFunc(&scratch, node,
 							 op->args, op->opfuncid, op->inputcollid,
-							 state);
+							 state, NULL);
 				ExprEvalPushStep(state, &scratch);
 				break;
 			}
@@ -1158,7 +1167,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 				ExecInitFunc(&scratch, node,
 							 op->args, op->opfuncid, op->inputcollid,
-							 state);
+							 state, NULL);
 
 				/*
 				 * Change opcode of call instruction to EEOP_DISTINCT.
@@ -1180,7 +1189,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 				ExecInitFunc(&scratch, node,
 							 op->args, op->opfuncid, op->inputcollid,
-							 state);
+							 state, NULL);
 
 				/*
 				 * Change opcode of call instruction to EEOP_NULLIF.
@@ -1263,7 +1272,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 				{
 					/* Evaluate scalar directly into left function argument */
 					ExecInitExprRec(scalararg, state,
-									&fcinfo->args[0].value, &fcinfo->args[0].isnull);
+									&fcinfo->args[0].value, &fcinfo->args[0].isnull, NULL);
 
 					/*
 					 * Evaluate array argument into our return value.  There's
@@ -1272,7 +1281,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 					 * EEOP_HASHED_SCALARARRAYOP, and will not be passed to
 					 * any other expression.
 					 */
-					ExecInitExprRec(arrayarg, state, resv, resnull);
+					ExecInitExprRec(arrayarg, state, resv, resnull, NULL);
 
 					/* And perform the operation */
 					scratch.opcode = EEOP_HASHED_SCALARARRAYOP;
@@ -1289,7 +1298,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 					/* Evaluate scalar directly into left function argument */
 					ExecInitExprRec(scalararg, state,
 									&fcinfo->args[0].value,
-									&fcinfo->args[0].isnull);
+									&fcinfo->args[0].isnull, NULL);
 
 					/*
 					 * Evaluate array argument into our return value.  There's
@@ -1297,7 +1306,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 					 * guaranteed to be overwritten by EEOP_SCALARARRAYOP, and
 					 * will not be passed to any other expression.
 					 */
-					ExecInitExprRec(arrayarg, state, resv, resnull);
+					ExecInitExprRec(arrayarg, state, resv, resnull, NULL);
 
 					/* And perform the operation */
 					scratch.opcode = EEOP_SCALARARRAYOP;
@@ -1342,7 +1351,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 					Expr	   *arg = (Expr *) lfirst(lc);
 
 					/* Evaluate argument into our output variable */
-					ExecInitExprRec(arg, state, resv, resnull);
+					ExecInitExprRec(arg, state, resv, resnull, NULL);
 
 					/* Perform the appropriate step type */
 					switch (boolexpr->boolop)
@@ -1423,7 +1432,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 				FieldSelect *fselect = (FieldSelect *) node;
 
 				/* evaluate row/record argument into result area */
-				ExecInitExprRec(fselect->arg, state, resv, resnull);
+				ExecInitExprRec(fselect->arg, state, resv, resnull, NULL);
 
 				/* and extract field */
 				scratch.opcode = EEOP_FIELDSELECT;
@@ -1460,7 +1469,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 				rowcachep->cacheptr = NULL;
 
 				/* emit code to evaluate the composite input value */
-				ExecInitExprRec(fstore->arg, state, resv, resnull);
+				ExecInitExprRec(fstore->arg, state, resv, resnull, NULL);
 
 				/* next, deform the input tuple into our workspace */
 				scratch.opcode = EEOP_FIELDSTORE_DEFORM;
@@ -1514,7 +1523,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 					ExecInitExprRec(e, state,
 									&values[fieldnum - 1],
-									&nulls[fieldnum - 1]);
+									&nulls[fieldnum - 1], NULL);
 
 					state->innermost_caseval = save_innermost_caseval;
 					state->innermost_casenull = save_innermost_casenull;
@@ -1536,7 +1545,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 				/* relabel doesn't need to do anything at runtime */
 				RelabelType *relabel = (RelabelType *) node;
 
-				ExecInitExprRec(relabel->arg, state, resv, resnull);
+				ExecInitExprRec(relabel->arg, state, resv, resnull, NULL);
 				break;
 			}
 
@@ -1547,9 +1556,10 @@ ExecInitExprRec(Expr *node, ExprState *state,
 				bool		typisvarlena;
 				Oid			typioparam;
 				FunctionCallInfo fcinfo_in;
+				ErrorSaveContext *escontext = NULL;
 
 				/* evaluate argument into step's result area */
-				ExecInitExprRec(iocoerce->arg, state, resv, resnull);
+				ExecInitExprRec(iocoerce->arg, state, resv, resnull, reserror);
 
 				/*
 				 * Prepare both output and input function calls, to be
@@ -1581,9 +1591,17 @@ ExecInitExprRec(Expr *node, ExprState *state,
 								 &iofunc, &typioparam);
 				fmgr_info(iofunc, scratch.d.iocoerce.finfo_in);
 				fmgr_info_set_expr((Node *) node, scratch.d.iocoerce.finfo_in);
+
+				/* if this is a safe-mode coerce */
+				if (unlikely(iocoerce->safe_mode))
+				{
+					escontext = palloc0(sizeof(ErrorSaveContext));
+					escontext->type = T_ErrorSaveContext;
+				}
+
 				InitFunctionCallInfoData(*scratch.d.iocoerce.fcinfo_data_in,
 										 scratch.d.iocoerce.finfo_in,
-										 3, InvalidOid, NULL, NULL);
+										 3, InvalidOid, (fmNodePtr) escontext, NULL);
 
 				/*
 				 * We can preload the second and third arguments for the input
@@ -1606,7 +1624,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 				ExprState  *elemstate;
 
 				/* evaluate argument into step's result area */
-				ExecInitExprRec(acoerce->arg, state, resv, resnull);
+				ExecInitExprRec(acoerce->arg, state, resv, resnull, reserror);
 
 				resultelemtype = get_element_type(acoerce->resulttype);
 				if (!OidIsValid(resultelemtype))
@@ -1627,9 +1645,11 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 				elemstate->innermost_caseval = (Datum *) palloc(sizeof(Datum));
 				elemstate->innermost_casenull = (bool *) palloc(sizeof(bool));
+				elemstate->innermost_caseerror = (bool *) palloc(sizeof(bool));
 
 				ExecInitExprRec(acoerce->elemexpr, elemstate,
-								&elemstate->resvalue, &elemstate->resnull);
+								&elemstate->resvalue, &elemstate->resnull,
+								&elemstate->reserror);
 
 				if (elemstate->steps_len == 1 &&
 					elemstate->steps[0].opcode == EEOP_CASE_TESTVAL)
@@ -1677,7 +1697,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 				rowcachep[1].cacheptr = NULL;
 
 				/* evaluate argument into step's result area */
-				ExecInitExprRec(convert->arg, state, resv, resnull);
+				ExecInitExprRec(convert->arg, state, resv, resnull, NULL);
 
 				/* and push conversion step */
 				scratch.opcode = EEOP_CONVERT_ROWTYPE;
@@ -1699,6 +1719,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 				List	   *adjust_jumps = NIL;
 				Datum	   *caseval = NULL;
 				bool	   *casenull = NULL;
+				bool	   *caseerror = NULL;
 				ListCell   *lc;
 
 				/*
@@ -1711,9 +1732,10 @@ ExecInitExprRec(Expr *node, ExprState *state,
 					/* Evaluate testexpr into caseval/casenull workspace */
 					caseval = palloc(sizeof(Datum));
 					casenull = palloc(sizeof(bool));
+					caseerror = palloc(sizeof(bool));
 
 					ExecInitExprRec(caseExpr->arg, state,
-									caseval, casenull);
+									caseval, casenull, caseerror);
 
 					/*
 					 * Since value might be read multiple times, force to R/O
@@ -1725,8 +1747,10 @@ ExecInitExprRec(Expr *node, ExprState *state,
 						scratch.opcode = EEOP_MAKE_READONLY;
 						scratch.resvalue = caseval;
 						scratch.resnull = casenull;
+						scratch.reserror = caseerror;
 						scratch.d.make_readonly.value = caseval;
 						scratch.d.make_readonly.isnull = casenull;
+						scratch.d.make_readonly.iserror = caseerror;
 						ExprEvalPushStep(state, &scratch);
 						/* restore normal settings of scratch fields */
 						scratch.resvalue = resv;
@@ -1745,6 +1769,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 					CaseWhen   *when = (CaseWhen *) lfirst(lc);
 					Datum	   *save_innermost_caseval;
 					bool	   *save_innermost_casenull;
+					bool	   *save_innermost_caseerror;
 					int			whenstep;
 
 					/*
@@ -1759,14 +1784,17 @@ ExecInitExprRec(Expr *node, ExprState *state,
 					 */
 					save_innermost_caseval = state->innermost_caseval;
 					save_innermost_casenull = state->innermost_casenull;
+					save_innermost_caseerror = state->innermost_caseerror;
 					state->innermost_caseval = caseval;
 					state->innermost_casenull = casenull;
+					state->innermost_caseerror = caseerror;
 
 					/* evaluate condition into CASE's result variables */
-					ExecInitExprRec(when->expr, state, resv, resnull);
+					ExecInitExprRec(when->expr, state, resv, resnull, NULL);
 
 					state->innermost_caseval = save_innermost_caseval;
 					state->innermost_casenull = save_innermost_casenull;
+					state->innermost_caseerror = save_innermost_caseerror;
 
 					/* If WHEN result isn't true, jump to next CASE arm */
 					scratch.opcode = EEOP_JUMP_IF_NOT_TRUE;
@@ -1778,7 +1806,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 					 * If WHEN result is true, evaluate THEN result, storing
 					 * it into the CASE's result variables.
 					 */
-					ExecInitExprRec(when->result, state, resv, resnull);
+					ExecInitExprRec(when->result, state, resv, resnull, reserror);
 
 					/* Emit JUMP step to jump to end of CASE's code */
 					scratch.opcode = EEOP_JUMP;
@@ -1804,7 +1832,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 				/* evaluate ELSE expr into CASE's result variables */
 				ExecInitExprRec(caseExpr->defresult, state,
-								resv, resnull);
+								resv, resnull, reserror);
 
 				/* adjust jump targets */
 				foreach(lc, adjust_jumps)
@@ -1833,6 +1861,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 				scratch.opcode = EEOP_CASE_TESTVAL;
 				scratch.d.casetest.value = state->innermost_caseval;
 				scratch.d.casetest.isnull = state->innermost_casenull;
+				scratch.d.casetest.iserror = state->innermost_caseerror;
 
 				ExprEvalPushStep(state, &scratch);
 				break;
@@ -1875,7 +1904,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 					ExecInitExprRec(e, state,
 									&scratch.d.arrayexpr.elemvalues[elemoff],
-									&scratch.d.arrayexpr.elemnulls[elemoff]);
+									&scratch.d.arrayexpr.elemnulls[elemoff], NULL);
 					elemoff++;
 				}
 
@@ -1969,7 +1998,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 					/* Evaluate column expr into appropriate workspace slot */
 					ExecInitExprRec(e, state,
 									&scratch.d.row.elemvalues[i],
-									&scratch.d.row.elemnulls[i]);
+									&scratch.d.row.elemnulls[i], NULL);
 					i++;
 				}
 
@@ -2047,9 +2076,9 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 					/* evaluate left and right args directly into fcinfo */
 					ExecInitExprRec(left_expr, state,
-									&fcinfo->args[0].value, &fcinfo->args[0].isnull);
+									&fcinfo->args[0].value, &fcinfo->args[0].isnull, NULL);
 					ExecInitExprRec(right_expr, state,
-									&fcinfo->args[1].value, &fcinfo->args[1].isnull);
+									&fcinfo->args[1].value, &fcinfo->args[1].isnull, NULL);
 
 					scratch.opcode = EEOP_ROWCOMPARE_STEP;
 					scratch.d.rowcompare_step.finfo = finfo;
@@ -2104,6 +2133,13 @@ ExecInitExprRec(Expr *node, ExprState *state,
 				CoalesceExpr *coalesce = (CoalesceExpr *) node;
 				List	   *adjust_jumps = NIL;
 				ListCell   *lc;
+				ExprEvalOp jump_test_opcode;
+
+				/* Coalesce can handle resnull and reserror tests */
+				if (likely(coalesce->op == NULL_TEST))
+					jump_test_opcode = EEOP_JUMP_IF_NOT_NULL;
+				else
+					jump_test_opcode = EEOP_JUMP_IF_NOT_ERROR;
 
 				/* We assume there's at least one arg */
 				Assert(coalesce->args != NIL);
@@ -2117,10 +2153,10 @@ ExecInitExprRec(Expr *node, ExprState *state,
 					Expr	   *e = (Expr *) lfirst(lc);
 
 					/* evaluate argument, directly into result datum */
-					ExecInitExprRec(e, state, resv, resnull);
+					ExecInitExprRec(e, state, resv, resnull, reserror);
 
-					/* if it's not null, skip to end of COALESCE expr */
-					scratch.opcode = EEOP_JUMP_IF_NOT_NULL;
+					/* if it's not null/error, skip to end of COALESCE expr */
+					scratch.opcode = jump_test_opcode;
 					scratch.d.jump.jumpdone = -1;	/* adjust later */
 					ExprEvalPushStep(state, &scratch);
 
@@ -2139,7 +2175,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 				{
 					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
 
-					Assert(as->opcode == EEOP_JUMP_IF_NOT_NULL);
+					Assert(as->opcode == jump_test_opcode);
 					Assert(as->d.jump.jumpdone == -1);
 					as->d.jump.jumpdone = state->steps_len;
 				}
@@ -2201,7 +2237,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 					ExecInitExprRec(e, state,
 									&scratch.d.minmax.values[off],
-									&scratch.d.minmax.nulls[off]);
+									&scratch.d.minmax.nulls[off], NULL);
 					off++;
 				}
 
@@ -2256,7 +2292,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 					ExecInitExprRec(e, state,
 									&scratch.d.xmlexpr.named_argvalue[off],
-									&scratch.d.xmlexpr.named_argnull[off]);
+									&scratch.d.xmlexpr.named_argnull[off], NULL);
 					off++;
 				}
 
@@ -2267,7 +2303,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 					ExecInitExprRec(e, state,
 									&scratch.d.xmlexpr.argvalue[off],
-									&scratch.d.xmlexpr.argnull[off]);
+									&scratch.d.xmlexpr.argnull[off], NULL);
 					off++;
 				}
 
@@ -2304,7 +2340,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 				/* first evaluate argument into result variable */
 				ExecInitExprRec(ntest->arg, state,
-								resv, resnull);
+								resv, resnull, NULL);
 
 				/* then push the test of that argument */
 				ExprEvalPushStep(state, &scratch);
@@ -2321,7 +2357,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 				 * and will get overwritten by the below EEOP_BOOLTEST_IS_*
 				 * step.
 				 */
-				ExecInitExprRec(btest->arg, state, resv, resnull);
+				ExecInitExprRec(btest->arg, state, resv, resnull, NULL);
 
 				switch (btest->booltesttype)
 				{
@@ -2359,7 +2395,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 				CoerceToDomain *ctest = (CoerceToDomain *) node;
 
 				ExecInitCoerceToDomain(&scratch, ctest, state,
-									   resv, resnull);
+									   resv, resnull, NULL);
 				break;
 			}
 
@@ -2442,7 +2478,7 @@ ExprEvalPushStep(ExprState *es, const ExprEvalStep *s)
  */
 static void
 ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
-			 Oid inputcollid, ExprState *state)
+			 Oid inputcollid, ExprState *state, ErrorSaveContext *escontext)
 {
 	int			nargs = list_length(args);
 	AclResult	aclresult;
@@ -2483,7 +2519,7 @@ ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
 
 	/* Initialize function call parameter structure too */
 	InitFunctionCallInfoData(*fcinfo, flinfo,
-							 nargs, inputcollid, NULL, NULL);
+							 nargs, inputcollid, (fmNodePtr) escontext, NULL);
 
 	/* Keep extra copies of this info to save an indirection at runtime */
 	scratch->d.func.fn_addr = flinfo->fn_addr;
@@ -2519,7 +2555,7 @@ ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
 		{
 			ExecInitExprRec(arg, state,
 							&fcinfo->args[argno].value,
-							&fcinfo->args[argno].isnull);
+							&fcinfo->args[argno].isnull, NULL);
 		}
 		argno++;
 	}
@@ -2570,6 +2606,7 @@ ExecPushExprSlots(ExprState *state, LastAttnumInfo *info)
 
 	scratch.resvalue = NULL;
 	scratch.resnull = NULL;
+	scratch.reserror = NULL;
 
 	/* Emit steps as needed */
 	if (info->last_inner > 0)
@@ -2834,7 +2871,7 @@ ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable, ExprState *state)
  */
 static void
 ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
-						ExprState *state, Datum *resv, bool *resnull)
+						ExprState *state, Datum *resv, bool *resnull, bool *reserror)
 {
 	bool		isAssignment = (sbsref->refassgnexpr != NULL);
 	int			nupper = list_length(sbsref->refupperindexpr);
@@ -2897,7 +2934,7 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
 	 * be overwritten by the final EEOP_SBSREF_FETCH/ASSIGN step, which is
 	 * pushed last.
 	 */
-	ExecInitExprRec(sbsref->refexpr, state, resv, resnull);
+	ExecInitExprRec(sbsref->refexpr, state, resv, resnull, NULL);
 
 	/*
 	 * If refexpr yields NULL, and the operation should be strict, then result
@@ -2931,7 +2968,7 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
 			/* Each subscript is evaluated into appropriate array entry */
 			ExecInitExprRec(e, state,
 							&sbsrefstate->upperindex[i],
-							&sbsrefstate->upperindexnull[i]);
+							&sbsrefstate->upperindexnull[i], NULL);
 		}
 		i++;
 	}
@@ -2954,7 +2991,7 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
 			/* Each subscript is evaluated into appropriate array entry */
 			ExecInitExprRec(e, state,
 							&sbsrefstate->lowerindex[i],
-							&sbsrefstate->lowerindexnull[i]);
+							&sbsrefstate->lowerindexnull[i], NULL);
 		}
 		i++;
 	}
@@ -3018,7 +3055,7 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
 
 		/* evaluate replacement value into replacevalue/replacenull */
 		ExecInitExprRec(sbsref->refassgnexpr, state,
-						&sbsrefstate->replacevalue, &sbsrefstate->replacenull);
+						&sbsrefstate->replacevalue, &sbsrefstate->replacenull, NULL);
 
 		state->innermost_caseval = save_innermost_caseval;
 		state->innermost_casenull = save_innermost_casenull;
@@ -3107,11 +3144,12 @@ isAssignmentIndirectionExpr(Expr *expr)
  */
 static void
 ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
-					   ExprState *state, Datum *resv, bool *resnull)
+					   ExprState *state, Datum *resv, bool *resnull, bool *reserror)
 {
 	DomainConstraintRef *constraint_ref;
 	Datum	   *domainval = NULL;
 	bool	   *domainnull = NULL;
+	bool	   *domainerror = NULL;
 	ListCell   *l;
 
 	scratch->d.domaincheck.resulttype = ctest->resulttype;
@@ -3124,7 +3162,7 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
 	 * if there's constraint failures there'll be errors, otherwise it's what
 	 * needs to be returned.
 	 */
-	ExecInitExprRec(ctest->arg, state, resv, resnull);
+	ExecInitExprRec(ctest->arg, state, resv, resnull, NULL);
 
 	/*
 	 * Note: if the argument is of varlena type, it could be a R/W expanded
@@ -3196,11 +3234,13 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
 						/* Yes, so make output workspace for MAKE_READONLY */
 						domainval = (Datum *) palloc(sizeof(Datum));
 						domainnull = (bool *) palloc(sizeof(bool));
+						domainerror = (bool *) palloc(sizeof(bool));
 
 						/* Emit MAKE_READONLY */
 						scratch2.opcode = EEOP_MAKE_READONLY;
 						scratch2.resvalue = domainval;
 						scratch2.resnull = domainnull;
+						scratch2.reserror = domainerror;
 						scratch2.d.make_readonly.value = resv;
 						scratch2.d.make_readonly.isnull = resnull;
 						ExprEvalPushStep(state, &scratch2);
@@ -3227,7 +3267,7 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
 				/* evaluate check expression value */
 				ExecInitExprRec(con->check_expr, state,
 								scratch->d.domaincheck.checkvalue,
-								scratch->d.domaincheck.checknull);
+								scratch->d.domaincheck.checknull, NULL);
 
 				state->innermost_domainval = save_innermost_domainval;
 				state->innermost_domainnull = save_innermost_domainnull;
@@ -3319,7 +3359,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 		{
 			/* evaluate filter expression */
 			ExecInitExprRec(pertrans->aggref->aggfilter, state,
-							&state->resvalue, &state->resnull);
+							&state->resvalue, &state->resnull, NULL);
 			/* and jump out if false */
 			scratch.opcode = EEOP_JUMP_IF_NOT_TRUE;
 			scratch.d.jump.jumpdone = -1;	/* adjust later */
@@ -3359,7 +3399,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 				 */
 				ExecInitExprRec(source_tle->expr, state,
 								&trans_fcinfo->args[argno + 1].value,
-								&trans_fcinfo->args[argno + 1].isnull);
+								&trans_fcinfo->args[argno + 1].isnull, NULL);
 			}
 			else
 			{
@@ -3368,7 +3408,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 				/* evaluate argument */
 				ExecInitExprRec(source_tle->expr, state,
 								&ds_fcinfo->args[0].value,
-								&ds_fcinfo->args[0].isnull);
+								&ds_fcinfo->args[0].isnull, NULL);
 
 				/* Dummy second argument for type-safety reasons */
 				ds_fcinfo->args[1].value = PointerGetDatum(NULL);
@@ -3430,7 +3470,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 				 */
 				ExecInitExprRec(source_tle->expr, state,
 								&trans_fcinfo->args[argno + 1].value,
-								&trans_fcinfo->args[argno + 1].isnull);
+								&trans_fcinfo->args[argno + 1].isnull, NULL);
 				argno++;
 			}
 			Assert(pertrans->numTransInputs == argno);
@@ -3448,7 +3488,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 
 			ExecInitExprRec(source_tle->expr, state,
 							&state->resvalue,
-							&state->resnull);
+							&state->resnull, NULL);
 			strictnulls = &state->resnull;
 			argno++;
 
@@ -3471,7 +3511,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 				TargetEntry *source_tle = (TargetEntry *) lfirst(arg);
 
 				ExecInitExprRec(source_tle->expr, state,
-								&values[argno], &nulls[argno]);
+								&values[argno], &nulls[argno], NULL);
 				argno++;
 			}
 			Assert(pertrans->numInputs == argno);
@@ -3585,6 +3625,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
 
 	scratch.resvalue = NULL;
 	scratch.resnull = NULL;
+	scratch.reserror = NULL;
 	scratch.opcode = EEOP_DONE;
 	ExprEvalPushStep(state, &scratch);
 
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 1dab2787b7..6cc6fa1717 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -64,6 +64,7 @@
 #include "funcapi.h"
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
+#include "nodes/miscnodes.h"
 #include "parser/parsetree.h"
 #include "pgstat.h"
 #include "utils/array.h"
@@ -434,6 +435,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		&&CASE_EEOP_JUMP,
 		&&CASE_EEOP_JUMP_IF_NULL,
 		&&CASE_EEOP_JUMP_IF_NOT_NULL,
+		&&CASE_EEOP_JUMP_IF_NOT_ERROR,
 		&&CASE_EEOP_JUMP_IF_NOT_TRUE,
 		&&CASE_EEOP_NULLTEST_ISNULL,
 		&&CASE_EEOP_NULLTEST_ISNOTNULL,
@@ -729,6 +731,13 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			*op->resvalue = d;
 			*op->resnull = fcinfo->isnull;
 
+			if (SOFT_ERROR_OCCURRED(fcinfo->context))
+			{
+				ErrorSaveContext *escontext = (ErrorSaveContext *) fcinfo->context;
+				escontext->error_occurred = false;
+				*op->reserror = true;
+			}
+
 			EEO_NEXT();
 		}
 
@@ -966,6 +975,21 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			EEO_NEXT();
 		}
 
+		EEO_CASE(EEOP_JUMP_IF_NOT_ERROR)
+		{
+			/* Transfer control if current result is non-error */
+			if (!*op->reserror)
+			{
+				*op->reserror = false;
+				EEO_JUMP(op->d.jump.jumpdone);
+			}
+
+			/* reset error flag */
+			*op->reserror = false;
+
+			EEO_NEXT();
+		}
+
 		EEO_CASE(EEOP_JUMP_IF_NOT_TRUE)
 		{
 			/* Transfer control if current result is null or false */
@@ -1181,10 +1205,17 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 
 				fcinfo_in->isnull = false;
 				d = FunctionCallInvoke(fcinfo_in);
+
 				*op->resvalue = d;
 
-				/* Should get null result if and only if str is NULL */
-				if (str == NULL)
+				if (SOFT_ERROR_OCCURRED(fcinfo_in->context))
+				{
+					ErrorSaveContext *escontext = (ErrorSaveContext *) fcinfo_in->context;
+					escontext->error_occurred = false;
+					*op->reserror = true;
+				}
+				/* If no error, should get null result if and only if str is NULL */
+				else if (str == NULL)
 				{
 					Assert(*op->resnull);
 					Assert(fcinfo_in->isnull);
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index f114337f8e..f99b108ceb 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -919,6 +919,21 @@ llvm_compile_expr(ExprState *state)
 					break;
 				}
 
+			case EEOP_JUMP_IF_NOT_ERROR:
+				{
+					LLVMValueRef v_reserror;
+
+					/* Transfer control if current result is non-error */
+
+					v_resnull = LLVMBuildLoad(b, v_reserrorp, "");
+
+					LLVMBuildCondBr(b,
+									LLVMBuildICmp(b, LLVMIntEQ, v_reserror,
+												  l_sbool_const(0), ""),
+									opblocks[op->d.jump.jumpdone],
+									opblocks[opno + 1]);
+					break;
+				}
 
 			case EEOP_JUMP_IF_NOT_TRUE:
 				{
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index c85d8fe975..229d1e84c8 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -517,7 +517,8 @@ makeColumnDef(const char *colname, Oid typeOid, int32 typmod, Oid collOid)
  */
 FuncExpr *
 makeFuncExpr(Oid funcid, Oid rettype, List *args,
-			 Oid funccollid, Oid inputcollid, CoercionForm fformat)
+			 Oid funccollid, Oid inputcollid, CoercionForm fformat,
+			 bool safe_mode)
 {
 	FuncExpr   *funcexpr;
 
@@ -530,6 +531,7 @@ makeFuncExpr(Oid funcid, Oid rettype, List *args,
 	funcexpr->funccollid = funccollid;
 	funcexpr->inputcollid = inputcollid;
 	funcexpr->args = args;
+	funcexpr->safe_mode = safe_mode;
 	funcexpr->location = -1;
 
 	return funcexpr;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index af8620ceb7..7bc3e13208 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -1522,13 +1522,16 @@ exprLocation(const Node *expr)
 			{
 				const TypeCast *tc = (const TypeCast *) expr;
 
-				/*
-				 * This could represent CAST(), ::, or TypeName 'literal', so
-				 * any of the components might be leftmost.
-				 */
 				loc = exprLocation(tc->arg);
-				loc = leftmostLoc(loc, tc->typeName->location);
-				loc = leftmostLoc(loc, tc->location);
+				if (likely(!tc->safe_mode))
+				{
+					/*
+					 * This could represent CAST(), ::, or TypeName 'literal',
+					 * so any of the components might be leftmost.
+					 */
+					loc = leftmostLoc(loc, tc->typeName->location);
+					loc = leftmostLoc(loc, tc->location);
+				}
 			}
 			break;
 		case T_CollateClause:
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index bffc8112aa..e0b0ffb9a6 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -2894,6 +2894,7 @@ eval_const_expressions_mutator(Node *node,
 				newexpr->resulttype = expr->resulttype;
 				newexpr->resultcollid = expr->resultcollid;
 				newexpr->coerceformat = expr->coerceformat;
+				newexpr->safe_mode = expr->safe_mode;
 				newexpr->location = expr->location;
 				return (Node *) newexpr;
 			}
@@ -3158,7 +3159,7 @@ eval_const_expressions_mutator(Node *node,
 					 * drop following arguments since they will never be
 					 * reached.
 					 */
-					if (IsA(e, Const))
+					if ((coalesceexpr->op == NULL_TEST) && IsA(e, Const))
 					{
 						if (((Const *) e)->constisnull)
 							continue;	/* drop null constant */
@@ -3183,6 +3184,7 @@ eval_const_expressions_mutator(Node *node,
 				newcoalesce->coalescetype = coalesceexpr->coalescetype;
 				newcoalesce->coalescecollid = coalesceexpr->coalescecollid;
 				newcoalesce->args = newargs;
+				newcoalesce->op = coalesceexpr->op;
 				newcoalesce->location = coalesceexpr->location;
 				return (Node *) newcoalesce;
 			}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index adc3f8ced3..3e24fe5f70 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -642,6 +642,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <partboundspec> PartitionBoundSpec
 %type <list>		hash_partbound
 %type <defelt>		hash_partbound_elem
+%type <node>	cast_on_error_clause
+%type <node>	cast_on_error_action
 
 
 /*
@@ -690,7 +692,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	DETACH DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P
 	DOUBLE_P DROP
 
-	EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
+	EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ERROR_P ESCAPE EVENT EXCEPT
 	EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXPRESSION
 	EXTENSION EXTERNAL EXTRACT
 
@@ -14399,8 +14401,7 @@ interval_second:
  * you expect!  So we use %prec annotations freely to set precedences.
  */
 a_expr:		c_expr									{ $$ = $1; }
-			| a_expr TYPECAST Typename
-					{ $$ = makeTypeCast($1, $3, @2); }
+			| a_expr TYPECAST Typename { $$ = makeTypeCast($1, $3, @2); }
 			| a_expr COLLATE any_name
 				{
 					CollateClause *n = makeNode(CollateClause);
@@ -15330,8 +15331,26 @@ func_expr_common_subexpr:
 											   COERCE_SQL_SYNTAX,
 											   @1);
 				}
-			| CAST '(' a_expr AS Typename ')'
-				{ $$ = makeTypeCast($3, $5, @1); }
+			| CAST '(' a_expr AS Typename cast_on_error_clause ')'
+				{
+					TypeCast *cast = (TypeCast *) makeTypeCast($3, $5, @1);
+					if ($6 == NULL)
+						$$ = (Node *) cast;
+					else
+					{
+						/*
+						 * On-error actions must themselves be typecast to the
+						 * same type as the original expression.
+						 */
+						TypeCast *on_err = (TypeCast *) makeTypeCast($6, $5, @6);
+						CoalesceExpr *c = makeNode(CoalesceExpr);
+						cast->safe_mode = true;
+						c->args = list_make2(cast, on_err);
+						c->op = ERROR_TEST;
+						c->location = @1;
+						$$ = (Node *) c;
+					}
+				}
 			| EXTRACT '(' extract_list ')'
 				{
 					$$ = (Node *) makeFuncCall(SystemFuncName("extract"),
@@ -15462,6 +15481,7 @@ func_expr_common_subexpr:
 					CoalesceExpr *c = makeNode(CoalesceExpr);
 
 					c->args = $3;
+					c->op = NULL_TEST;
 					c->location = @1;
 					$$ = (Node *) c;
 				}
@@ -15551,6 +15571,15 @@ func_expr_common_subexpr:
 				}
 		;
 
+cast_on_error_clause: cast_on_error_action ON ERROR_P { $$ = $1; }
+			| /* EMPTY */ { $$ = NULL; }
+		;
+
+cast_on_error_action: ERROR_P { $$ = NULL; }
+			| NULL_P { $$ = makeNullAConst(-1); }
+			| DEFAULT a_expr { $$ = $2; }
+		;
+
 /*
  * SQL/XML support
  */
@@ -16138,8 +16167,8 @@ substr_list:
 					 * is unknown or doesn't have an implicit cast to int4.
 					 */
 					$$ = list_make3($1, makeIntConst(1, -1),
-									makeTypeCast($3,
-												 SystemTypeName("int4"), -1));
+									makeTypeCast($3, SystemTypeName("int4"),
+									-1));
 				}
 			| a_expr SIMILAR a_expr ESCAPE a_expr
 				{
@@ -16799,6 +16828,7 @@ unreserved_keyword:
 			| ENCODING
 			| ENCRYPTED
 			| ENUM_P
+			| ERROR_P
 			| ESCAPE
 			| EVENT
 			| EXCLUDE
@@ -17346,6 +17376,7 @@ bare_label_keyword:
 			| ENCRYPTED
 			| END_P
 			| ENUM_P
+			| ERROR_P
 			| ESCAPE
 			| EVENT
 			| EXCLUDE
@@ -17749,6 +17780,7 @@ makeTypeCast(Node *arg, TypeName *typename, int location)
 
 	n->arg = arg;
 	n->typeName = typename;
+	n->safe_mode = false;
 	n->location = location;
 	return (Node *) n;
 }
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 3ef9e8ee5e..b08422297d 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -2004,7 +2004,8 @@ build_aggregate_transfn_expr(Oid *agg_input_types,
 						 args,
 						 InvalidOid,
 						 agg_input_collation,
-						 COERCE_EXPLICIT_CALL);
+						 COERCE_EXPLICIT_CALL,
+						 NULL);
 	fexpr->funcvariadic = agg_variadic;
 	*transfnexpr = (Expr *) fexpr;
 
@@ -2020,7 +2021,8 @@ build_aggregate_transfn_expr(Oid *agg_input_types,
 								 args,
 								 InvalidOid,
 								 agg_input_collation,
-								 COERCE_EXPLICIT_CALL);
+								 COERCE_EXPLICIT_CALL,
+								 NULL);
 			fexpr->funcvariadic = agg_variadic;
 			*invtransfnexpr = (Expr *) fexpr;
 		}
@@ -2048,7 +2050,8 @@ build_aggregate_serialfn_expr(Oid serialfn_oid,
 						 args,
 						 InvalidOid,
 						 InvalidOid,
-						 COERCE_EXPLICIT_CALL);
+						 COERCE_EXPLICIT_CALL,
+						 NULL);
 	*serialfnexpr = (Expr *) fexpr;
 }
 
@@ -2072,7 +2075,8 @@ build_aggregate_deserialfn_expr(Oid deserialfn_oid,
 						 args,
 						 InvalidOid,
 						 InvalidOid,
-						 COERCE_EXPLICIT_CALL);
+						 COERCE_EXPLICIT_CALL,
+						 NULL);
 	*deserialfnexpr = (Expr *) fexpr;
 }
 
@@ -2109,7 +2113,8 @@ build_aggregate_finalfn_expr(Oid *agg_input_types,
 										 args,
 										 InvalidOid,
 										 agg_input_collation,
-										 COERCE_EXPLICIT_CALL);
+										 COERCE_EXPLICIT_CALL,
+										 NULL);
 	/* finalfn is currently never treated as variadic */
 }
 
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 60908111c8..c00ef1bd70 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -36,13 +36,15 @@ static Node *coerce_type_typmod(Node *node,
 								Oid targetTypeId, int32 targetTypMod,
 								CoercionContext ccontext, CoercionForm cformat,
 								int location,
-								bool hideInputCoercion);
+								bool hideInputCoercion,
+								bool *cast_error_found);
 static void hide_coercion_node(Node *node);
 static Node *build_coercion_expression(Node *node,
 									   CoercionPathType pathtype,
 									   Oid funcId,
 									   Oid targetTypeId, int32 targetTypMod,
 									   CoercionContext ccontext, CoercionForm cformat,
+									   bool *cast_error_found,
 									   int location);
 static Node *coerce_record_to_complex(ParseState *pstate, Node *node,
 									  Oid targetTypeId,
@@ -80,6 +82,19 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype,
 					  CoercionContext ccontext,
 					  CoercionForm cformat,
 					  int location)
+{
+	return coerce_to_target_type_safe(pstate, expr, exprtype, targettype,
+									  targettypmod, ccontext, cformat,
+									  NULL, location);
+}
+
+Node *
+coerce_to_target_type_safe(ParseState *pstate, Node *expr, Oid exprtype,
+					  Oid targettype, int32 targettypmod,
+					  CoercionContext ccontext,
+					  CoercionForm cformat,
+					  bool *cast_error_found,
+					  int location)
 {
 	Node	   *result;
 	Node	   *origexpr;
@@ -101,19 +116,30 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype,
 	while (expr && IsA(expr, CollateExpr))
 		expr = (Node *) ((CollateExpr *) expr)->arg;
 
-	result = coerce_type(pstate, expr, exprtype,
-						 targettype, targettypmod,
-						 ccontext, cformat, location);
+	result = coerce_type_safe(pstate, expr, exprtype,
+							  targettype, targettypmod,
+							  ccontext, cformat,
+							  cast_error_found, location);
+
+	/*
+	 * If this coercion failed on bad input, we want to report that to the
+	 * caller.
+	 */
+	if (cast_error_found != NULL && *cast_error_found)
+		return NULL;
 
 	/*
 	 * If the target is a fixed-length type, it may need a length coercion as
 	 * well as a type coercion.  If we find ourselves adding both, force the
 	 * inner coercion node to implicit display form.
 	 */
-	result = coerce_type_typmod(result,
-								targettype, targettypmod,
+	result = coerce_type_typmod(result, targettype, targettypmod,
 								ccontext, cformat, location,
-								(result != expr && !IsA(result, Const)));
+								(result != expr && !IsA(result, Const)),
+								cast_error_found);
+
+	if (cast_error_found != NULL && *cast_error_found)
+		return NULL;
 
 	if (expr != origexpr && type_is_collatable(targettype))
 	{
@@ -157,6 +183,18 @@ Node *
 coerce_type(ParseState *pstate, Node *node,
 			Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod,
 			CoercionContext ccontext, CoercionForm cformat, int location)
+{
+	return coerce_type_safe(pstate, node, inputTypeId, targetTypeId,
+							targetTypeMod, ccontext, cformat,
+							NULL, location);
+}
+
+Node *
+coerce_type_safe(ParseState *pstate, Node *node,
+				 Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod,
+				 CoercionContext ccontext, CoercionForm cformat,
+				 bool *cast_error_found,
+				 int location)
 {
 	Node	   *result;
 	CoercionPathType pathtype;
@@ -255,6 +293,7 @@ coerce_type(ParseState *pstate, Node *node,
 		int32		inputTypeMod;
 		Type		baseType;
 		ParseCallbackState pcbstate;
+		bool		coerce_failed = false;
 
 		/*
 		 * If the target type is a domain, we want to call its base type's
@@ -307,21 +346,47 @@ coerce_type(ParseState *pstate, Node *node,
 		 * We assume here that UNKNOWN's internal representation is the same
 		 * as CSTRING.
 		 */
-		if (!con->constisnull)
-			newcon->constvalue = stringTypeDatum(baseType,
-												 DatumGetCString(con->constvalue),
-												 inputTypeMod);
+		if (cast_error_found == NULL)
+		{
+			if (!con->constisnull)
+				newcon->constvalue = stringTypeDatum(baseType,
+													 DatumGetCString(con->constvalue),
+													 inputTypeMod);
+			else
+				newcon->constvalue = stringTypeDatum(baseType,
+													 NULL,
+													 inputTypeMod);
+		}
 		else
-			newcon->constvalue = stringTypeDatum(baseType,
-												 NULL,
-												 inputTypeMod);
+		{
+			/* If we find an error, just carry it up to the caller */
+			Datum val2;
+			bool success;
+
+			if (!con->constisnull)
+				success = stringTypeDatumSafe(baseType,
+											  DatumGetCString(con->constvalue),
+											  inputTypeMod, &val2);
+			else
+				success = stringTypeDatumSafe(baseType, NULL, inputTypeMod,
+											  &val2);
+
+			if (success)
+				newcon->constvalue = val2;
+			else
+			{
+				*cast_error_found = true;
+				coerce_failed = true;
+				result = NULL;
+			}
+		}
 
 		/*
 		 * If it's a varlena value, force it to be in non-expanded
 		 * (non-toasted) format; this avoids any possible dependency on
 		 * external values and improves consistency of representation.
 		 */
-		if (!con->constisnull && newcon->constlen == -1)
+		if (!coerce_failed && !con->constisnull && newcon->constlen == -1)
 			newcon->constvalue =
 				PointerGetDatum(PG_DETOAST_DATUM(newcon->constvalue));
 
@@ -338,32 +403,53 @@ coerce_type(ParseState *pstate, Node *node,
 		 * identical may not get recognized as such.  See pgsql-hackers
 		 * discussion of 2008-04-04.
 		 */
-		if (!con->constisnull && !newcon->constbyval)
+		if (!coerce_failed && !con->constisnull && !newcon->constbyval)
 		{
 			Datum		val2;
 
-			val2 = stringTypeDatum(baseType,
-								   DatumGetCString(con->constvalue),
-								   inputTypeMod);
-			if (newcon->constlen == -1)
-				val2 = PointerGetDatum(PG_DETOAST_DATUM(val2));
-			if (!datumIsEqual(newcon->constvalue, val2, false, newcon->constlen))
-				elog(WARNING, "type %s has unstable input conversion for \"%s\"",
-					 typeTypeName(baseType), DatumGetCString(con->constvalue));
+			if (cast_error_found != NULL)
+			{
+				if (!stringTypeDatumSafe(baseType,
+									     DatumGetCString(con->constvalue),
+									     inputTypeMod, &val2))
+				{
+					coerce_failed = true;
+					*cast_error_found = true;
+					result = NULL;
+				}
+			}
+			else
+				val2 = stringTypeDatum(baseType,
+									   DatumGetCString(con->constvalue),
+									   inputTypeMod);
+			if (!coerce_failed)
+			{
+				if (newcon->constlen == -1)
+					val2 = PointerGetDatum(PG_DETOAST_DATUM(val2));
+				if (!datumIsEqual(newcon->constvalue, val2,
+													false, newcon->constlen))
+					elog(WARNING,
+						 "type %s has unstable input conversion for \"%s\"",
+						 typeTypeName(baseType),
+						 DatumGetCString(con->constvalue));
+			}
 		}
 #endif
 
 		cancel_parser_errposition_callback(&pcbstate);
 
-		result = (Node *) newcon;
+		if (!coerce_failed)
+		{
+			result = (Node *) newcon;
 
-		/* If target is a domain, apply constraints. */
-		if (baseTypeId != targetTypeId)
-			result = coerce_to_domain(result,
-									  baseTypeId, baseTypeMod,
-									  targetTypeId,
-									  ccontext, cformat, location,
-									  false);
+			/* If target is a domain, apply constraints. */
+			if (baseTypeId != targetTypeId)
+				result = coerce_to_domain_safe(result,
+											   baseTypeId, baseTypeMod,
+											   targetTypeId,
+											   ccontext, cformat, location,
+											   false, cast_error_found);
+		}
 
 		ReleaseSysCache(baseType);
 
@@ -396,9 +482,15 @@ coerce_type(ParseState *pstate, Node *node,
 		 */
 		CollateExpr *coll = (CollateExpr *) node;
 
-		result = coerce_type(pstate, (Node *) coll->arg,
-							 inputTypeId, targetTypeId, targetTypeMod,
-							 ccontext, cformat, location);
+		result = coerce_type_safe(pstate, (Node *) coll->arg,
+								  inputTypeId, targetTypeId, targetTypeMod,
+								  ccontext, cformat,
+								  cast_error_found,
+								  location);
+
+		if (cast_error_found != NULL && *cast_error_found)
+			return NULL;
+
 		if (type_is_collatable(targetTypeId))
 		{
 			CollateExpr *newcoll = makeNode(CollateExpr);
@@ -431,17 +523,23 @@ coerce_type(ParseState *pstate, Node *node,
 
 			result = build_coercion_expression(node, pathtype, funcId,
 											   baseTypeId, baseTypeMod,
-											   ccontext, cformat, location);
+											   ccontext, cformat,
+											   cast_error_found,
+											   location);
 
 			/*
 			 * If domain, coerce to the domain type and relabel with domain
 			 * type ID, hiding the previous coercion node.
 			 */
 			if (targetTypeId != baseTypeId)
-				result = coerce_to_domain(result, baseTypeId, baseTypeMod,
+			{
+				result = coerce_to_domain_safe(result, baseTypeId, baseTypeMod,
 										  targetTypeId,
 										  ccontext, cformat, location,
-										  true);
+										  true, cast_error_found);
+				if (cast_error_found != NULL && *cast_error_found)
+					return NULL;
+			}
 		}
 		else
 		{
@@ -454,9 +552,11 @@ coerce_type(ParseState *pstate, Node *node,
 			 * that must be accounted for.  If the destination is a domain
 			 * then we won't need a RelabelType node.
 			 */
-			result = coerce_to_domain(node, InvalidOid, -1, targetTypeId,
+			result = coerce_to_domain_safe(node, InvalidOid, -1, targetTypeId,
 									  ccontext, cformat, location,
-									  false);
+									  false, cast_error_found);
+			if (cast_error_found != NULL && *cast_error_found)
+				return NULL;
 			if (result == node)
 			{
 				/*
@@ -676,6 +776,17 @@ Node *
 coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod, Oid typeId,
 				 CoercionContext ccontext, CoercionForm cformat, int location,
 				 bool hideInputCoercion)
+{
+	return coerce_to_domain_safe(arg, baseTypeId, baseTypeMod, typeId,
+								 ccontext, cformat, location,
+								 hideInputCoercion, NULL);
+}
+
+Node *
+coerce_to_domain_safe(Node *arg, Oid baseTypeId, int32 baseTypeMod, Oid typeId,
+					  CoercionContext ccontext, CoercionForm cformat,
+					  int location, bool hideInputCoercion,
+					  bool *cast_error_found)
 {
 	CoerceToDomain *result;
 
@@ -706,7 +817,7 @@ coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod, Oid typeId,
 	 */
 	arg = coerce_type_typmod(arg, baseTypeId, baseTypeMod,
 							 ccontext, COERCE_IMPLICIT_CAST, location,
-							 false);
+							 false, cast_error_found);
 
 	/*
 	 * Now build the domain coercion node.  This represents run-time checking
@@ -753,7 +864,7 @@ static Node *
 coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod,
 				   CoercionContext ccontext, CoercionForm cformat,
 				   int location,
-				   bool hideInputCoercion)
+				   bool hideInputCoercion, bool *cast_error_found)
 {
 	CoercionPathType pathtype;
 	Oid			funcId;
@@ -780,7 +891,9 @@ coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod,
 	{
 		node = build_coercion_expression(node, pathtype, funcId,
 										 targetTypeId, targetTypMod,
-										 ccontext, cformat, location);
+										 ccontext, cformat,
+										 cast_error_found,
+										 location);
 	}
 	else
 	{
@@ -841,9 +954,10 @@ build_coercion_expression(Node *node,
 						  Oid funcId,
 						  Oid targetTypeId, int32 targetTypMod,
 						  CoercionContext ccontext, CoercionForm cformat,
-						  int location)
+						  bool *cast_error_found, int location)
 {
 	int			nargs = 0;
+	bool		safe_mode = (cast_error_found != NULL);
 
 	if (OidIsValid(funcId))
 	{
@@ -913,13 +1027,14 @@ build_coercion_expression(Node *node,
 		}
 
 		fexpr = makeFuncExpr(funcId, targetTypeId, args,
-							 InvalidOid, InvalidOid, cformat);
+							 InvalidOid, InvalidOid, cformat, safe_mode);
 		fexpr->location = location;
 		return (Node *) fexpr;
 	}
 	else if (pathtype == COERCION_PATH_ARRAYCOERCE)
 	{
 		/* We need to build an ArrayCoerceExpr */
+		/* TODO pass in Safe param */
 		ArrayCoerceExpr *acoerce = makeNode(ArrayCoerceExpr);
 		CaseTestExpr *ctest = makeNode(CaseTestExpr);
 		Oid			sourceBaseTypeId;
@@ -951,14 +1066,20 @@ build_coercion_expression(Node *node,
 		targetElementType = get_element_type(targetTypeId);
 		Assert(OidIsValid(targetElementType));
 
-		elemexpr = coerce_to_target_type(NULL,
+		/*
+		 * No node gets resolved here, so this cast cannot fail
+		 * at parse time.
+		 */
+		elemexpr = coerce_to_target_type_safe(NULL,
 										 (Node *) ctest,
 										 ctest->typeId,
 										 targetElementType,
 										 targetTypMod,
 										 ccontext,
 										 cformat,
+										 cast_error_found,
 										 location);
+
 		if (elemexpr == NULL)	/* shouldn't happen */
 			elog(ERROR, "failed to coerce array element type as expected");
 
@@ -973,6 +1094,7 @@ build_coercion_expression(Node *node,
 		acoerce->resulttypmod = exprTypmod(elemexpr);
 		/* resultcollid will be set by parse_collate.c */
 		acoerce->coerceformat = cformat;
+		acoerce->safe_mode = safe_mode;
 		acoerce->location = location;
 
 		return (Node *) acoerce;
@@ -988,6 +1110,7 @@ build_coercion_expression(Node *node,
 		iocoerce->resulttype = targetTypeId;
 		/* resultcollid will be set by parse_collate.c */
 		iocoerce->coerceformat = cformat;
+		iocoerce->safe_mode = safe_mode;
 		iocoerce->location = location;
 
 		return (Node *) iocoerce;
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 150a8099c2..54224b9f06 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -57,7 +57,8 @@ static Node *transformMultiAssignRef(ParseState *pstate, MultiAssignRef *maref);
 static Node *transformCaseExpr(ParseState *pstate, CaseExpr *c);
 static Node *transformSubLink(ParseState *pstate, SubLink *sublink);
 static Node *transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
-								Oid array_type, Oid element_type, int32 typmod);
+								Oid array_type, Oid element_type, int32 typmod,
+								bool *cast_error_found);
 static Node *transformRowExpr(ParseState *pstate, RowExpr *r, bool allowDefault);
 static Node *transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c);
 static Node *transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m);
@@ -137,7 +138,7 @@ transformExprRecurse(ParseState *pstate, Node *expr)
 
 		case T_A_ArrayExpr:
 			result = transformArrayExpr(pstate, (A_ArrayExpr *) expr,
-										InvalidOid, InvalidOid, -1);
+										InvalidOid, InvalidOid, -1, NULL);
 			break;
 
 		case T_TypeCast:
@@ -1907,7 +1908,8 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
  */
 static Node *
 transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
-				   Oid array_type, Oid element_type, int32 typmod)
+				   Oid array_type, Oid element_type, int32 typmod,
+				   bool *cast_error_found)
 {
 	ArrayExpr  *newa = makeNode(ArrayExpr);
 	List	   *newelems = NIL;
@@ -1938,7 +1940,12 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
 									  (A_ArrayExpr *) e,
 									  array_type,
 									  element_type,
-									  typmod);
+									  typmod,
+									  cast_error_found);
+
+			if (cast_error_found != NULL && *cast_error_found)
+				return NULL;
+
 			/* we certainly have an array here */
 			Assert(array_type == InvalidOid || array_type == exprType(newe));
 			newa->multidims = true;
@@ -2027,13 +2034,18 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
 
 		if (coerce_hard)
 		{
-			newe = coerce_to_target_type(pstate, e,
+			newe = coerce_to_target_type_safe(pstate, e,
 										 exprType(e),
 										 coerce_type,
 										 typmod,
 										 COERCION_EXPLICIT,
 										 COERCE_EXPLICIT_CAST,
+										 cast_error_found,
 										 -1);
+
+			if (cast_error_found != NULL && *cast_error_found)
+				return NULL;
+
 			if (newe == NULL)
 				ereport(ERROR,
 						(errcode(ERRCODE_CANNOT_COERCE),
@@ -2105,13 +2117,19 @@ transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c)
 	List	   *newcoercedargs = NIL;
 	ListCell   *args;
 
+
 	foreach(args, c->args)
 	{
 		Node	   *e = (Node *) lfirst(args);
 		Node	   *newe;
 
 		newe = transformExprRecurse(pstate, e);
-		newargs = lappend(newargs, newe);
+		/*
+		 * Some child nodes can return NULL if they would result in a
+		 * safe_mode error. Filter those out here
+		 */
+		if (newe != NULL)
+			newargs = lappend(newargs, newe);
 	}
 
 	newc->coalescetype = select_common_type(pstate, newargs, "COALESCE", NULL);
@@ -2141,6 +2159,7 @@ transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c)
 									exprLocation(pstate->p_last_srf))));
 
 	newc->args = newcoercedargs;
+	newc->op = c->op;
 	newc->location = c->location;
 	return (Node *) newc;
 }
@@ -2345,8 +2364,7 @@ transformXmlSerialize(ParseState *pstate, XmlSerialize *xs)
 	result = coerce_to_target_type(pstate, (Node *) xexpr,
 								   TEXTOID, targetType, targetTypmod,
 								   COERCION_IMPLICIT,
-								   COERCE_IMPLICIT_CAST,
-								   -1);
+								   COERCE_IMPLICIT_CAST, -1);
 	if (result == NULL)
 		ereport(ERROR,
 				(errcode(ERRCODE_CANNOT_COERCE),
@@ -2524,10 +2542,25 @@ transformTypeCast(ParseState *pstate, TypeCast *tc)
 	Oid			inputType;
 	Oid			targetType;
 	int32		targetTypmod;
-	int			location;
+	int			location = tc->location;
+	TypeName   *typeName = tc->typeName;
+	bool		cast_error;
+	bool	   *cast_error_found;
+
+
+	if (tc->safe_mode)
+	{
+		cast_error = false;
+		cast_error_found = &cast_error;
+	}
+	else
+	{
+		cast_error_found = NULL;
+	}
+
 
 	/* Look up the type name first */
-	typenameTypeIdAndMod(pstate, tc->typeName, &targetType, &targetTypmod);
+	typenameTypeIdAndMod(pstate, typeName, &targetType, &targetTypmod);
 
 	/*
 	 * If the subject of the typecast is an ARRAY[] construct and the target
@@ -2557,7 +2590,16 @@ transformTypeCast(ParseState *pstate, TypeCast *tc)
 									  (A_ArrayExpr *) arg,
 									  targetBaseType,
 									  elementType,
-									  targetBaseTypmod);
+									  targetBaseTypmod,
+									  cast_error_found);
+
+			/*
+			 * if any element of the tranformation failed, then that means that the
+			 * larger cast has failed. Returning NULL here is safe when the
+			 * parent node (CoalesceExpr) knows to look for it (and filter it out).
+			 */
+			if (cast_error_found != NULL && *cast_error_found)
+				return NULL;
 		}
 		else
 			expr = transformExprRecurse(pstate, arg);
@@ -2574,15 +2616,24 @@ transformTypeCast(ParseState *pstate, TypeCast *tc)
 	 * CAST symbol, but if there is none then use the location of the type
 	 * name (this can happen in TypeName 'string' syntax, for instance).
 	 */
-	location = tc->location;
 	if (location < 0)
-		location = tc->typeName->location;
+		location = typeName->location;
+
+	result = coerce_to_target_type_safe(pstate, expr, inputType,
+										targetType, targetTypmod,
+										COERCION_EXPLICIT,
+										COERCE_EXPLICIT_CAST,
+										cast_error_found,
+										location);
+
+	/*
+	 * If this typecast reported a cast error, and the cast failed outright,
+	 * then we want to replace this node entirely with the default,
+	 * and we signal the parent node to do that by returning NULL.
+	 */
+	if (cast_error_found != NULL && *cast_error_found)
+		return NULL;
 
-	result = coerce_to_target_type(pstate, expr, inputType,
-								   targetType, targetTypmod,
-								   COERCION_EXPLICIT,
-								   COERCE_EXPLICIT_CAST,
-								   location);
 	if (result == NULL)
 		ereport(ERROR,
 				(errcode(ERRCODE_CANNOT_COERCE),
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index 29643fb4ab..92a3c60955 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -4049,7 +4049,8 @@ get_qual_for_hash(Relation parent, PartitionBoundSpec *spec)
 						 args,
 						 InvalidOid,
 						 InvalidOid,
-						 COERCE_EXPLICIT_CALL);
+						 COERCE_EXPLICIT_CALL,
+						 NULL);
 
 	return list_make1(fexpr);
 }
diff --git a/src/backend/rewrite/rewriteSearchCycle.c b/src/backend/rewrite/rewriteSearchCycle.c
index 58f684cd52..ae93c06702 100644
--- a/src/backend/rewrite/rewriteSearchCycle.c
+++ b/src/backend/rewrite/rewriteSearchCycle.c
@@ -191,7 +191,7 @@ make_path_cat_expr(RowExpr *rowexpr, AttrNumber path_varattno)
 	fexpr = makeFuncExpr(F_ARRAY_CAT, RECORDARRAYOID,
 						 list_make2(makeVar(1, path_varattno, RECORDARRAYOID, -1, 0, 0),
 									arr),
-						 InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL);
+						 InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL, NULL);
 
 	return (Expr *) fexpr;
 }
@@ -521,7 +521,7 @@ rewriteSearchAndCycle(CommonTableExpr *cte)
 			fs->resulttype = INT8OID;
 			fs->resulttypmod = -1;
 
-			fexpr = makeFuncExpr(F_INT8INC, INT8OID, list_make1(fs), InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL);
+			fexpr = makeFuncExpr(F_INT8INC, INT8OID, list_make1(fs), InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL, NULL);
 
 			lfirst(list_head(search_col_rowexpr->args)) = fexpr;
 
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 0557302b92..3ab5f8b000 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -138,6 +138,7 @@ typedef enum ExprEvalOp
 	/* conditional jumps based on current result value */
 	EEOP_JUMP_IF_NULL,
 	EEOP_JUMP_IF_NOT_NULL,
+	EEOP_JUMP_IF_NOT_ERROR,
 	EEOP_JUMP_IF_NOT_TRUE,
 
 	/* perform NULL tests for scalar values */
@@ -273,6 +274,7 @@ typedef struct ExprEvalStep
 	/* where to store the result of this step */
 	Datum	   *resvalue;
 	bool	   *resnull;
+	bool	   *reserror;
 
 	/*
 	 * Inline data for the operation.  Inline data is faster to access, but
@@ -395,6 +397,7 @@ typedef struct ExprEvalStep
 		{
 			Datum	   *value;	/* value to return */
 			bool	   *isnull;
+			bool	   *iserror;
 		}			casetest;
 
 		/* for EEOP_MAKE_READONLY */
@@ -402,6 +405,7 @@ typedef struct ExprEvalStep
 		{
 			Datum	   *value;	/* value to coerce to read-only */
 			bool	   *isnull;
+			bool	   *iserror;
 		}			make_readonly;
 
 		/* for EEOP_IOCOERCE */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 9a64a830a2..fb9b2f7963 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -95,6 +95,9 @@ typedef struct ExprState
 #define FIELDNO_EXPRSTATE_RESULTSLOT 4
 	TupleTableSlot *resultslot;
 
+#define FIELDNO_EXPRSTATE_RESERROR 5
+	bool		reserror;
+
 	/*
 	 * Instructions to compute expression's return value.
 	 */
@@ -126,6 +129,7 @@ typedef struct ExprState
 
 	Datum	   *innermost_caseval;
 	bool	   *innermost_casenull;
+	bool	   *innermost_caseerror;
 
 	Datum	   *innermost_domainval;
 	bool	   *innermost_domainnull;
diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h
index 50de4c62af..50a9bdde59 100644
--- a/src/include/nodes/makefuncs.h
+++ b/src/include/nodes/makefuncs.h
@@ -77,7 +77,8 @@ extern ColumnDef *makeColumnDef(const char *colname,
 								Oid typeOid, int32 typmod, Oid collOid);
 
 extern FuncExpr *makeFuncExpr(Oid funcid, Oid rettype, List *args,
-							  Oid funccollid, Oid inputcollid, CoercionForm fformat);
+							  Oid funccollid, Oid inputcollid,
+							  CoercionForm fformat, bool safe_mode);
 
 extern FuncCall *makeFuncCall(List *name, List *args,
 							  CoercionForm funcformat, int location);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 8fe9b2fcfe..c4ee9c46d0 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -340,6 +340,7 @@ typedef struct TypeCast
 	NodeTag		type;
 	Node	   *arg;			/* the expression being casted */
 	TypeName   *typeName;		/* the target type */
+	bool		safe_mode;		/* cast can ereturn error vs ereport */
 	int			location;		/* token location, or -1 if unknown */
 } TypeCast;
 
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 74f228d959..2ea23914b1 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -604,6 +604,7 @@ typedef struct FuncExpr
 	Oid			funccollid;		/* OID of collation of result */
 	Oid			inputcollid;	/* OID of collation that function should use */
 	List	   *args;			/* arguments to the function */
+	bool		safe_mode;		/* use safe mode */
 	int			location;		/* token location, or -1 if unknown */
 } FuncExpr;
 
@@ -1023,6 +1024,7 @@ typedef struct CoerceViaIO
 	/* output typmod is not stored, but is presumed -1 */
 	Oid			resultcollid;	/* OID of collation, or InvalidOid if none */
 	CoercionForm coerceformat;	/* how to display this node */
+	bool		safe_mode;		/* use safe mode */
 	int			location;		/* token location, or -1 if unknown */
 } CoerceViaIO;
 
@@ -1048,6 +1050,7 @@ typedef struct ArrayCoerceExpr
 	int32		resulttypmod;	/* output typmod (also element typmod) */
 	Oid			resultcollid;	/* OID of collation, or InvalidOid if none */
 	CoercionForm coerceformat;	/* how to display this node */
+	bool		safe_mode;		/* use safe mode */
 	int			location;		/* token location, or -1 if unknown */
 } ArrayCoerceExpr;
 
@@ -1260,8 +1263,14 @@ typedef struct RowCompareExpr
 	List	   *rargs;			/* the right-hand input arguments */
 } RowCompareExpr;
 
+typedef enum CoalesceOp
+{
+	NULL_TEST,					/* Test for NULL, like SQL COALECE() */
+	ERROR_TEST					/* Test for error flag */
+} CoalesceOp;
+
 /*
- * CoalesceExpr - a COALESCE expression
+ * CoalesceExpr - a COALESCE expression or OnError expression
  */
 typedef struct CoalesceExpr
 {
@@ -1269,6 +1278,7 @@ typedef struct CoalesceExpr
 	Oid			coalescetype;	/* type of expression result */
 	Oid			coalescecollid; /* OID of collation, or InvalidOid if none */
 	List	   *args;			/* the arguments */
+	CoalesceOp	op;				/* test for NULL or test for ERROR */
 	int			location;		/* token location, or -1 if unknown */
 } CoalesceExpr;
 
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 957ee18d84..ed7d670819 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -151,6 +151,7 @@ PG_KEYWORD("encoding", ENCODING, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("encrypted", ENCRYPTED, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("end", END_P, RESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("enum", ENUM_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("error", ERROR_P, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("escape", ESCAPE, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("event", EVENT, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("except", EXCEPT, RESERVED_KEYWORD, AS_LABEL)
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index ddbc995077..db8e15f225 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -43,15 +43,30 @@ extern Node *coerce_to_target_type(ParseState *pstate,
 								   CoercionContext ccontext,
 								   CoercionForm cformat,
 								   int location);
+extern Node *coerce_to_target_type_safe(ParseState *pstate,
+										Node *expr, Oid exprtype,
+										Oid targettype, int32 targettypmod,
+										CoercionContext ccontext,
+										CoercionForm cformat,
+										bool *cast_error_found,
+										int location);
 extern bool can_coerce_type(int nargs, const Oid *input_typeids, const Oid *target_typeids,
 							CoercionContext ccontext);
 extern Node *coerce_type(ParseState *pstate, Node *node,
 						 Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod,
 						 CoercionContext ccontext, CoercionForm cformat, int location);
+extern Node *coerce_type_safe(ParseState *pstate, Node *node,
+							  Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod,
+							  CoercionContext ccontext, CoercionForm cformat,
+							  bool *cast_error_found, int location);
 extern Node *coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod,
 							  Oid typeId,
 							  CoercionContext ccontext, CoercionForm cformat, int location,
 							  bool hideInputCoercion);
+extern Node *coerce_to_domain_safe(Node *arg, Oid baseTypeId, int32 baseTypeMod,
+								   Oid typeId,
+								   CoercionContext ccontext, CoercionForm cformat, int location,
+								   bool hideInputCoercion, bool *cast_error_found);
 
 extern Node *coerce_to_boolean(ParseState *pstate, Node *node,
 							   const char *constructName);
diff --git a/src/test/regress/expected/cast.out b/src/test/regress/expected/cast.out
new file mode 100644
index 0000000000..87cd1c101b
--- /dev/null
+++ b/src/test/regress/expected/cast.out
@@ -0,0 +1,40 @@
+/* ON ERROR behavior */
+VALUES (CAST('error' AS integer));
+ERROR:  invalid input syntax for type integer: "error"
+LINE 2: VALUES (CAST('error' AS integer));
+                     ^
+VALUES (CAST('error' AS integer ERROR ON ERROR));
+ERROR:  invalid input syntax for type integer: "error"
+LINE 1: VALUES (CAST('error' AS integer ERROR ON ERROR));
+                     ^
+VALUES (CAST('error' AS integer NULL ON ERROR));
+ column1 
+---------
+        
+(1 row)
+
+VALUES (CAST('error' AS integer DEFAULT 42 ON ERROR));
+ column1 
+---------
+      42
+(1 row)
+
+SELECT CAST('{123,abc,456}' AS integer[] DEFAULT '{-789}' ON ERROR) as array_test1;
+ array_test1 
+-------------
+ {-789}
+(1 row)
+
+SELECT CAST('{234,def,567}'::text[] AS integer[] DEFAULT '{-1011}' ON ERROR) as array_test2;
+ array_test2 
+-------------
+ {-1011}
+(1 row)
+
+SELECT CAST(u.arg AS integer DEFAULT -1 ON ERROR) AS unnest_test1 FROM unnest('{345,ghi,678}'::text[]) AS u(arg);
+ unnest_test1 
+--------------
+          345
+           -1
+          678
+(3 rows)
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 9a139f1e24..1d49b9b95f 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -41,7 +41,7 @@ test: geometry horology tstypes regex type_sanity opr_sanity misc_sanity comment
 # execute two copy tests in parallel, to check that copy itself
 # is concurrent safe.
 # ----------
-test: copy copyselect copydml insert insert_conflict
+test: copy copyselect copydml insert insert_conflict cast
 
 # ----------
 # More groups of parallel tests
diff --git a/src/test/regress/sql/cast.sql b/src/test/regress/sql/cast.sql
new file mode 100644
index 0000000000..fab8cb7d33
--- /dev/null
+++ b/src/test/regress/sql/cast.sql
@@ -0,0 +1,11 @@
+/* ON ERROR behavior */
+
+VALUES (CAST('error' AS integer));
+VALUES (CAST('error' AS integer ERROR ON ERROR));
+VALUES (CAST('error' AS integer NULL ON ERROR));
+VALUES (CAST('error' AS integer DEFAULT 42 ON ERROR));
+
+SELECT CAST('{123,abc,456}' AS integer[] DEFAULT '{-789}' ON ERROR) as array_test1;
+SELECT CAST('{234,def,567}'::text[] AS integer[] DEFAULT '{-1011}' ON ERROR) as array_test2;
+
+SELECT CAST(u.arg AS integer DEFAULT -1 ON ERROR) AS unnest_test1 FROM unnest('{345,ghi,678}'::text[]) AS u(arg);
-- 
2.38.1

Reply via email to