From 74aaf068a2d071f6fefab27309d7a0252f28ccee Mon Sep 17 00:00:00 2001
From: coreyhuinker <corey.huinker@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

