hi.

fix the regress tests failure in
https://api.cirrus-ci.com/v1/artifact/task/5894868779663360/testrun/build/testrun/regress/regress/regression.diffs
From e60e5190511326568eba8e6748062adb47f1134c Mon Sep 17 00:00:00 2001
From: jian he <jian.universal...@gmail.com>
Date: Fri, 18 Jul 2025 13:00:19 +0800
Subject: [PATCH v4 2/3] make ArrayCoerceExpr error safe

similar to https://git.postgresql.org/cgit/postgresql.git/commit/?id=aaaf9449ec6be62cb0d30ed3588dc384f56274bf
---
 src/backend/executor/execExpr.c       | 3 +++
 src/backend/executor/execExprInterp.c | 4 ++++
 src/backend/utils/adt/arrayfuncs.c    | 7 +++++++
 3 files changed, 14 insertions(+)

diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index f1569879b5..1f3f899874 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -1702,6 +1702,9 @@ ExecInitExprRec(Expr *node, ExprState *state,
 				elemstate->innermost_caseval = (Datum *) palloc(sizeof(Datum));
 				elemstate->innermost_casenull = (bool *) palloc(sizeof(bool));
 
+				if (state->escontext != NULL)
+					elemstate->escontext = state->escontext;
+
 				ExecInitExprRec(acoerce->elemexpr, elemstate,
 								&elemstate->resvalue, &elemstate->resnull);
 
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 8a72b5e70a..636d9ef9e6 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -3644,6 +3644,10 @@ ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
 							  econtext,
 							  op->d.arraycoerce.resultelemtype,
 							  op->d.arraycoerce.amstate);
+
+	if (SOFT_ERROR_OCCURRED(op->d.arraycoerce.elemexprstate->escontext)
+		&& (*op->resvalue = (Datum) 0))
+		*op->resnull = true;
 }
 
 /*
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index c8f53c6fbe..b5f98bf22f 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -3288,6 +3288,13 @@ array_map(Datum arrayd,
 		/* Apply the given expression to source element */
 		values[i] = ExecEvalExpr(exprstate, econtext, &nulls[i]);
 
+		if (SOFT_ERROR_OCCURRED(exprstate->escontext))
+		{
+			pfree(values);
+			pfree(nulls);
+			return (Datum) 0;
+		}
+
 		if (nulls[i])
 			hasnulls = true;
 		else
-- 
2.34.1

From e3854ee1d362646ea97a8baa4e262cc3a9e95913 Mon Sep 17 00:00:00 2001
From: jian he <jian.universal...@gmail.com>
Date: Fri, 1 Aug 2025 13:22:59 +0800
Subject: [PATCH v4 1/3] make some numeric cast function error safe

The following function are changed to make it error safe.
numeric_int2
numeric_int4
numeric_int8
numeric_float4
numeric_float8
dtoi8
dtoi4
dtoi2
ftoi8
ftoi4
ftoi2
ftod
dtof
float4_numeric
float8_numeric
numeric (oid 1703)

This is need for evaulation CAST(... DEFAULT... ON CONVERSION ERROR).
discussion: https://postgr.es/m/CADkLM=fv1jfy4ufa-jcwwnbjqixnviskq8jzu3tz_p656i_...@mail.gmail.com
---
 src/backend/utils/adt/float.c   | 16 +++++---
 src/backend/utils/adt/int8.c    |  4 +-
 src/backend/utils/adt/numeric.c | 67 +++++++++++++++++++++++++++------
 3 files changed, 67 insertions(+), 20 deletions(-)

diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 7b97d2be6c..6461e9c94b 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -1199,9 +1199,13 @@ dtof(PG_FUNCTION_ARGS)
 
 	result = (float4) num;
 	if (unlikely(isinf(result)) && !isinf(num))
-		float_overflow_error();
+		ereturn(fcinfo->context, (Datum) 0,
+				errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				errmsg("value out of range: overflow"));
 	if (unlikely(result == 0.0f) && num != 0.0)
-		float_underflow_error();
+		ereturn(fcinfo->context, (Datum) 0,
+				errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				errmsg("value out of range: underflow"));
 
 	PG_RETURN_FLOAT4(result);
 }
@@ -1224,7 +1228,7 @@ dtoi4(PG_FUNCTION_ARGS)
 
 	/* Range check */
 	if (unlikely(isnan(num) || !FLOAT8_FITS_IN_INT32(num)))
-		ereport(ERROR,
+		ereturn(fcinfo->context, (Datum) 0,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("integer out of range")));
 
@@ -1249,7 +1253,7 @@ dtoi2(PG_FUNCTION_ARGS)
 
 	/* Range check */
 	if (unlikely(isnan(num) || !FLOAT8_FITS_IN_INT16(num)))
-		ereport(ERROR,
+		ereturn(fcinfo->context, (Datum) 0,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("smallint out of range")));
 
@@ -1298,7 +1302,7 @@ ftoi4(PG_FUNCTION_ARGS)
 
 	/* Range check */
 	if (unlikely(isnan(num) || !FLOAT4_FITS_IN_INT32(num)))
-		ereport(ERROR,
+		ereturn(fcinfo->context, (Datum) 0,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("integer out of range")));
 
@@ -1323,7 +1327,7 @@ ftoi2(PG_FUNCTION_ARGS)
 
 	/* Range check */
 	if (unlikely(isnan(num) || !FLOAT4_FITS_IN_INT16(num)))
-		ereport(ERROR,
+		ereturn(fcinfo->context, (Datum) 0,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("smallint out of range")));
 
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index 9dd5889f34..4b9a762067 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -1307,7 +1307,7 @@ dtoi8(PG_FUNCTION_ARGS)
 
 	/* Range check */
 	if (unlikely(isnan(num) || !FLOAT8_FITS_IN_INT64(num)))
-		ereport(ERROR,
+		ereturn(fcinfo->context, (Datum) 0,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("bigint out of range")));
 
@@ -1342,7 +1342,7 @@ ftoi8(PG_FUNCTION_ARGS)
 
 	/* Range check */
 	if (unlikely(isnan(num) || !FLOAT4_FITS_IN_INT64(num)))
-		ereport(ERROR,
+		ereturn(fcinfo->context, (Datum) 0,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("bigint out of range")));
 
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index c9233565d5..cf498627a9 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -1261,7 +1261,8 @@ numeric		(PG_FUNCTION_ARGS)
 	 */
 	if (NUMERIC_IS_SPECIAL(num))
 	{
-		(void) apply_typmod_special(num, typmod, NULL);
+		if (!apply_typmod_special(num, typmod, fcinfo->context))
+			PG_RETURN_NULL();
 		PG_RETURN_NUMERIC(duplicate_numeric(num));
 	}
 
@@ -4564,7 +4565,22 @@ numeric_int4(PG_FUNCTION_ARGS)
 {
 	Numeric		num = PG_GETARG_NUMERIC(0);
 
-	PG_RETURN_INT32(numeric_int4_opt_error(num, NULL));
+	if (likely(fcinfo->context == NULL))
+		PG_RETURN_INT32(numeric_int4_opt_error(num, NULL));
+	else
+	{
+		bool	has_error;
+		int32	result;
+		Node	*escontext = fcinfo->context;
+
+		result = numeric_int4_opt_error(num, &has_error);
+		if (has_error)
+			ereturn(escontext, (Datum) 0,
+					errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+					errmsg("integer out of range"));
+
+		PG_RETURN_INT32(result);
+	}
 }
 
 /*
@@ -4652,7 +4668,22 @@ numeric_int8(PG_FUNCTION_ARGS)
 {
 	Numeric		num = PG_GETARG_NUMERIC(0);
 
-	PG_RETURN_INT64(numeric_int8_opt_error(num, NULL));
+	if (likely(fcinfo->context == NULL))
+		PG_RETURN_INT64(numeric_int8_opt_error(num, NULL));
+	else
+	{
+		bool	has_error;
+		int64	result;
+		Node	*escontext = fcinfo->context;
+
+		result = numeric_int8_opt_error(num, &has_error);
+		if (has_error)
+			ereturn(escontext, (Datum) 0,
+					errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+					errmsg("bigint out of range"));
+
+		PG_RETURN_INT64(result);
+	}
 }
 
 
@@ -4669,6 +4700,7 @@ Datum
 numeric_int2(PG_FUNCTION_ARGS)
 {
 	Numeric		num = PG_GETARG_NUMERIC(0);
+	Node	   *escontext = fcinfo->context;
 	NumericVar	x;
 	int64		val;
 	int16		result;
@@ -4676,11 +4708,11 @@ numeric_int2(PG_FUNCTION_ARGS)
 	if (NUMERIC_IS_SPECIAL(num))
 	{
 		if (NUMERIC_IS_NAN(num))
-			ereport(ERROR,
+			ereturn(escontext, (Datum) 0,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("cannot convert NaN to %s", "smallint")));
 		else
-			ereport(ERROR,
+			ereturn(escontext, (Datum) 0,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("cannot convert infinity to %s", "smallint")));
 	}
@@ -4689,12 +4721,12 @@ numeric_int2(PG_FUNCTION_ARGS)
 	init_var_from_num(num, &x);
 
 	if (!numericvar_to_int64(&x, &val))
-		ereport(ERROR,
+		ereturn(escontext, (Datum) 0,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("smallint out of range")));
 
 	if (unlikely(val < PG_INT16_MIN) || unlikely(val > PG_INT16_MAX))
-		ereport(ERROR,
+		ereturn(escontext, (Datum) 0,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("smallint out of range")));
 
@@ -4759,10 +4791,14 @@ numeric_float8(PG_FUNCTION_ARGS)
 
 	tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
 											  NumericGetDatum(num)));
-
-	result = DirectFunctionCall1(float8in, CStringGetDatum(tmp));
-
-	pfree(tmp);
+	if (!DirectInputFunctionCallSafe(float8in, tmp,
+									 InvalidOid, -1,
+									 (Node *) fcinfo->context,
+									 &result))
+	{
+		pfree(tmp);
+		PG_RETURN_NULL();
+	}
 
 	PG_RETURN_DATUM(result);
 }
@@ -4854,7 +4890,14 @@ numeric_float4(PG_FUNCTION_ARGS)
 	tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
 											  NumericGetDatum(num)));
 
-	result = DirectFunctionCall1(float4in, CStringGetDatum(tmp));
+	if (!DirectInputFunctionCallSafe(float4in, tmp,
+									 InvalidOid, -1,
+									 (Node *) fcinfo->context,
+									 &result))
+	{
+		pfree(tmp);
+		PG_RETURN_NULL();
+	}
 
 	pfree(tmp);
 
-- 
2.34.1

From 138c6db6386d5c937fce6d903b3608f50950ee3a Mon Sep 17 00:00:00 2001
From: jian he <jian.universal...@gmail.com>
Date: Mon, 4 Aug 2025 09:03:22 +0800
Subject: [PATCH v4 3/3] CAST(expr AS newtype DEFAULT ON ERROR)

When casting data types, we generally uses one of three node types: FuncExpr,
CoerceViaIO, or ArrayCoerceExpr represent the type coercion.

* CoerceViaIO is already error-safe, see[0]
* ArrayCoerceExpr is also error safe, see previous commit.
* However, not all **FuncExpr** nodes is error-safe. For example, a function
like int84 is not error safe. In these situations, we create a CoerceViaIO node
and use the original expression as its argument, allowing us to safely handle
the cast.

Now that the type coercion node is error-safe, we also need to ensure that when
a coercion fails, it falls back to evaluating the default node.

[0]: https://git.postgresql.org/cgit/postgresql.git/commit/?id=aaaf9449ec6be62cb0d30ed3588dc384f56274bf
discussion: https://postgr.es/m/CADkLM=fv1jfy4ufa-jcwwnbjqixnviskq8jzu3tz_p656i_...@mail.gmail.com

demo:
SELECT CAST('1' AS date  DEFAULT '2011-01-01' ON ERROR),
       CAST('{234,def,567}'::text[] AS integer[] DEFAULT '{-1011}' ON ERROR);

    date    |  int4
------------+---------
 2011-01-01 | {-1011}
---
 src/backend/executor/execExpr.c       | 117 +++++++++-
 src/backend/executor/execExprInterp.c |  30 +++
 src/backend/jit/llvm/llvmjit_expr.c   |  49 ++++
 src/backend/nodes/nodeFuncs.c         |  67 ++++++
 src/backend/nodes/queryjumblefuncs.c  |  14 ++
 src/backend/optimizer/util/clauses.c  |  19 ++
 src/backend/parser/gram.y             |  31 ++-
 src/backend/parser/parse_expr.c       | 316 +++++++++++++++++++++++++-
 src/backend/parser/parse_target.c     |  14 ++
 src/backend/parser/parse_type.c       |  13 ++
 src/backend/utils/adt/arrayfuncs.c    |   6 +
 src/backend/utils/adt/ruleutils.c     |  15 ++
 src/backend/utils/fmgr/fmgr.c         |  13 ++
 src/include/executor/execExpr.h       |   8 +
 src/include/fmgr.h                    |   3 +
 src/include/nodes/execnodes.h         |  30 +++
 src/include/nodes/parsenodes.h        |   6 +
 src/include/nodes/primnodes.h         |  35 +++
 src/include/parser/parse_type.h       |   2 +
 src/test/regress/expected/cast.out    | 230 +++++++++++++++++++
 src/test/regress/parallel_schedule    |   2 +-
 src/test/regress/sql/cast.sql         | 109 +++++++++
 src/tools/pgindent/typedefs.list      |   3 +
 23 files changed, 1122 insertions(+), 10 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 1f3f899874..03cafad525 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -99,6 +99,9 @@ static void ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
 static void ExecInitJsonExpr(JsonExpr *jsexpr, ExprState *state,
 							 Datum *resv, bool *resnull,
 							 ExprEvalStep *scratch);
+static void ExecInitSafeTypeCastExpr(SafeTypeCastExpr *stcexpr, ExprState *state,
+									 Datum *resv, bool *resnull,
+									 ExprEvalStep *scratch);
 static void ExecInitJsonCoercion(ExprState *state, JsonReturning *returning,
 								 ErrorSaveContext *escontext, bool omit_quotes,
 								 bool exists_coerce,
@@ -2180,6 +2183,14 @@ ExecInitExprRec(Expr *node, ExprState *state,
 				break;
 			}
 
+		case T_SafeTypeCastExpr:
+			{
+				SafeTypeCastExpr   *stcexpr = castNode(SafeTypeCastExpr, node);
+
+				ExecInitSafeTypeCastExpr(stcexpr, state, resv, resnull, &scratch);
+				break;
+			}
+
 		case T_CoalesceExpr:
 			{
 				CoalesceExpr *coalesce = (CoalesceExpr *) node;
@@ -2746,7 +2757,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, (Node *) state->escontext, NULL);
 
 	/* Keep extra copies of this info to save an indirection at runtime */
 	scratch->d.func.fn_addr = flinfo->fn_addr;
@@ -4744,6 +4755,110 @@ ExecBuildParamSetEqual(TupleDesc desc,
 	return state;
 }
 
+/*
+ * Push steps to evaluate a SafeTypeCastExpr and its various subsidiary
+ * expressions. We already handle errors softly for coercion nodes like
+ * CoerceViaIO, CoerceToDomain, ArrayCoerceExpr, and some FuncExprs. However,
+ * most of FuncExprs node (for example, int84) is not error-safe. For these
+ * cases, we instead wrap the source expression and target type information
+ * within a CoerceViaIO node.
+ */
+static void
+ExecInitSafeTypeCastExpr(SafeTypeCastExpr *stcexpr , ExprState *state,
+						 Datum *resv, bool *resnull,
+						 ExprEvalStep *scratch)
+{
+	/*
+	 * If coercion to the target type fails, fallback to the default expression
+	 * specified in the ON CONVERSION ERROR clause.
+	*/
+	if (stcexpr->cast_expr == NULL)
+	{
+		ExecInitExprRec((Expr *) stcexpr->default_expr,
+						state, resv, resnull);
+		return;
+	}
+	else
+	{
+		CoerceViaIO *newexpr;
+		SafeTypeCastState *stcstate;
+		ErrorSaveContext *escontext;
+		ErrorSaveContext *saved_escontext;
+		List	   *jumps_to_end = NIL;
+		bool		numeric_fraction = false;
+		Oid			source_oid;
+
+		stcstate = palloc0(sizeof(SafeTypeCastState));
+		stcstate->stcexpr = stcexpr;
+		stcstate->escontext.type = T_ErrorSaveContext;
+		escontext = &stcstate->escontext;
+		state->escontext = escontext;
+
+		source_oid = getBaseType(exprType(stcexpr->source_expr));
+
+		/*
+		 * We can't use CoerceViaIO to safely cast numeric values with
+		 * fractional parts to other numerical types, because of rounding
+		 * issues. For example, '11.1'::NUMERIC::INT would fail because '11.1'
+		 * isn't a valid string representation for an integer.  Instead, we
+		 * should use FuncExpr for these cases.
+		*/
+		if (source_oid == NUMERICOID ||
+			source_oid == FLOAT4OID ||
+			source_oid == FLOAT8OID)
+			numeric_fraction = true;
+
+		/* evaluate argument expression into step's result area */
+		if (IsA(stcexpr->cast_expr, CoerceViaIO) ||
+			IsA(stcexpr->cast_expr, CoerceToDomain) ||
+			IsA(stcexpr->cast_expr, ArrayCoerceExpr) ||
+			IsA(stcexpr->cast_expr, Var) ||
+			numeric_fraction)
+			ExecInitExprRec((Expr *) stcexpr->cast_expr,
+							state, resv, resnull);
+		else
+		{
+			newexpr = makeNode(CoerceViaIO);
+			newexpr->arg = (Expr *) stcexpr->source_expr;
+			newexpr->resulttype = stcexpr->resulttype;
+			newexpr->resultcollid = exprCollation(stcexpr->source_expr);
+			newexpr->coerceformat = COERCE_EXPLICIT_CAST;
+			newexpr->location = exprLocation(stcexpr->source_expr);
+
+			ExecInitExprRec((Expr *) newexpr,
+							state, resv, resnull);
+		}
+
+		scratch->opcode = EEOP_SAFETYPE_CAST;
+		scratch->d.stcexpr.stcstate = stcstate;
+		ExprEvalPushStep(state, scratch);
+
+		stcstate->jump_error = state->steps_len;
+		/* JUMP to end if false, that is, skip the ON ERROR expression. */
+		jumps_to_end = lappend_int(jumps_to_end, state->steps_len);
+		scratch->opcode = EEOP_JUMP_IF_NOT_TRUE;
+		scratch->resvalue = &stcstate->error.value;
+		scratch->resnull = &stcstate->error.isnull;
+		scratch->d.jump.jumpdone = -1;	/* set below */
+		ExprEvalPushStep(state, scratch);
+
+		/* Steps to evaluate the ON ERROR expression */
+		saved_escontext = state->escontext;
+		state->escontext = NULL;
+		ExecInitExprRec((Expr *) stcstate->stcexpr->default_expr,
+						state, resv, resnull);
+		state->escontext = saved_escontext;
+
+		foreach_int(lc, jumps_to_end)
+		{
+			ExprEvalStep *as = &state->steps[lc];
+
+			as->d.jump.jumpdone = state->steps_len;
+		}
+		stcstate->jump_end = state->steps_len;
+	}
+}
+
 /*
  * Push steps to evaluate a JsonExpr and its various subsidiary expressions.
  */
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 636d9ef9e6..f6ed5953c2 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -568,6 +568,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		&&CASE_EEOP_XMLEXPR,
 		&&CASE_EEOP_JSON_CONSTRUCTOR,
 		&&CASE_EEOP_IS_JSON,
+		&&CASE_EEOP_SAFETYPE_CAST,
 		&&CASE_EEOP_JSONEXPR_PATH,
 		&&CASE_EEOP_JSONEXPR_COERCION,
 		&&CASE_EEOP_JSONEXPR_COERCION_FINISH,
@@ -1926,6 +1927,11 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			EEO_NEXT();
 		}
 
+		EEO_CASE(EEOP_SAFETYPE_CAST)
+		{
+			EEO_JUMP(ExecEvalSafeTypeCast(state, op));
+		}
+
 		EEO_CASE(EEOP_JSONEXPR_PATH)
 		{
 			/* too complex for an inline implementation */
@@ -5187,6 +5193,30 @@ GetJsonBehaviorValueString(JsonBehavior *behavior)
 	return pstrdup(behavior_names[behavior->btype]);
 }
 
+int
+ExecEvalSafeTypeCast(ExprState *state, ExprEvalStep *op)
+{
+	SafeTypeCastState *stcstate = op->d.stcexpr.stcstate;
+
+	if (SOFT_ERROR_OCCURRED(&stcstate->escontext))
+	{
+		*op->resvalue = (Datum) 0;
+		*op->resnull = true;
+
+		stcstate->error.value = BoolGetDatum(true);
+
+		/*
+		 * Reset for next use such as for catching errors when coercing a
+		 * stcexpr expression.
+		 */
+		stcstate->escontext.error_occurred = false;
+		stcstate->escontext.details_wanted = false;
+
+		return stcstate->jump_error;
+	}
+	return stcstate->jump_end;
+}
+
 /*
  * Checks if an error occurred in ExecEvalJsonCoercion().  If so, this sets
  * JsonExprState.error to trigger the ON ERROR handling steps, unless the
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index 890bcb0b0a..a2dfb82393 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -2256,6 +2256,55 @@ llvm_compile_expr(ExprState *state)
 				LLVMBuildBr(b, opblocks[opno + 1]);
 				break;
 
+			case EEOP_SAFETYPE_CAST:
+				{
+					SafeTypeCastState *stcstate = op->d.stcexpr.stcstate;
+					LLVMValueRef v_ret;
+
+					/*
+					 * Call ExecEvalSafeTypeCast().  It returns the address of
+					 * the step to perform next.
+					 */
+					v_ret = build_EvalXFunc(b, mod, "ExecEvalSafeTypeCast",
+											v_state, op, v_econtext);
+
+					/*
+					 * Build a switch to map the return value (v_ret above),
+					 * which is a runtime value of the step address to perform
+					 * next to jump_error
+					 */
+					if (stcstate->jump_error >= 0)
+					{
+						LLVMValueRef v_jump_error;
+						LLVMValueRef v_switch;
+						LLVMBasicBlockRef b_done,
+									b_error;
+
+						b_error =
+							l_bb_before_v(opblocks[opno + 1],
+										  "op.%d.stcexpr_error", opno);
+						b_done =
+							l_bb_before_v(opblocks[opno + 1],
+										  "op.%d.stcexpr_done", opno);
+
+						v_switch = LLVMBuildSwitch(b,
+												   v_ret,
+												   b_done,
+												   1);
+
+						/* Returned stcstate->jump_error? */
+						v_jump_error = l_int32_const(lc, stcstate->jump_error);
+						LLVMAddCase(v_switch, v_jump_error, b_error);
+
+						/* ON ERROR code */
+						LLVMPositionBuilderAtEnd(b, b_error);
+						LLVMBuildBr(b, opblocks[stcstate->jump_error]);
+
+						LLVMPositionBuilderAtEnd(b, b_done);
+					}
+					LLVMBuildBr(b, opblocks[stcstate->jump_end]);
+					break;
+				}
 			case EEOP_JSONEXPR_PATH:
 				{
 					JsonExprState *jsestate = op->d.jsonexpr.jsestate;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 7bc823507f..212f3e3c50 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -206,6 +206,9 @@ exprType(const Node *expr)
 		case T_RowCompareExpr:
 			type = BOOLOID;
 			break;
+		case T_SafeTypeCastExpr:
+			type = ((const SafeTypeCastExpr *) expr)->resulttype;
+			break;
 		case T_CoalesceExpr:
 			type = ((const CoalesceExpr *) expr)->coalescetype;
 			break;
@@ -450,6 +453,8 @@ exprTypmod(const Node *expr)
 				return typmod;
 			}
 			break;
+		case T_SafeTypeCastExpr:
+			return ((const SafeTypeCastExpr *) expr)->resulttypmod;
 		case T_CoalesceExpr:
 			{
 				/*
@@ -965,6 +970,9 @@ exprCollation(const Node *expr)
 			/* RowCompareExpr's result is boolean ... */
 			coll = InvalidOid;	/* ... so it has no collation */
 			break;
+		case T_SafeTypeCastExpr:
+			coll = ((const SafeTypeCastExpr *) expr)->resultcollid;
+			break;
 		case T_CoalesceExpr:
 			coll = ((const CoalesceExpr *) expr)->coalescecollid;
 			break;
@@ -1232,6 +1240,9 @@ exprSetCollation(Node *expr, Oid collation)
 			/* RowCompareExpr's result is boolean ... */
 			Assert(!OidIsValid(collation)); /* ... so never set a collation */
 			break;
+		case T_SafeTypeCastExpr:
+			((SafeTypeCastExpr *) expr)->resultcollid = collation;
+			break;
 		case T_CoalesceExpr:
 			((CoalesceExpr *) expr)->coalescecollid = collation;
 			break;
@@ -1554,6 +1565,15 @@ exprLocation(const Node *expr)
 			/* just use leftmost argument's location */
 			loc = exprLocation((Node *) ((const RowCompareExpr *) expr)->largs);
 			break;
+		case T_SafeTypeCastExpr:
+			{
+				const SafeTypeCastExpr *cast_expr = (const SafeTypeCastExpr *) expr;
+				if (cast_expr->cast_expr)
+					loc = exprLocation(cast_expr->cast_expr);
+				else
+					loc = exprLocation(cast_expr->default_expr);
+				break;
+			}
 		case T_CoalesceExpr:
 			/* COALESCE keyword should always be the first thing */
 			loc = ((const CoalesceExpr *) expr)->location;
@@ -2325,6 +2345,18 @@ expression_tree_walker_impl(Node *node,
 					return true;
 			}
 			break;
+		case T_SafeTypeCastExpr:
+			{
+				SafeTypeCastExpr   *scexpr = (SafeTypeCastExpr *) node;
+
+				if (WALK(scexpr->source_expr))
+					return true;
+				if (WALK(scexpr->cast_expr))
+					return true;
+				if (WALK(scexpr->default_expr))
+					return true;
+			}
+			break;
 		case T_CoalesceExpr:
 			return WALK(((CoalesceExpr *) node)->args);
 		case T_MinMaxExpr:
@@ -3334,6 +3366,19 @@ expression_tree_mutator_impl(Node *node,
 				return (Node *) newnode;
 			}
 			break;
+		case T_SafeTypeCastExpr:
+			{
+				SafeTypeCastExpr   *scexpr = (SafeTypeCastExpr *) node;
+				SafeTypeCastExpr   *newnode;
+
+				FLATCOPY(newnode, scexpr, SafeTypeCastExpr);
+				MUTATE(newnode->source_expr, scexpr->source_expr, Node *);
+				MUTATE(newnode->cast_expr, scexpr->cast_expr, Node *);
+				MUTATE(newnode->default_expr, scexpr->default_expr, Node *);
+
+				return (Node *) newnode;
+			}
+			break;
 		case T_CoalesceExpr:
 			{
 				CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
@@ -4468,6 +4513,28 @@ raw_expression_tree_walker_impl(Node *node,
 					return true;
 			}
 			break;
+		case T_SafeTypeCast:
+			{
+				SafeTypeCast   *sc = (SafeTypeCast *) node;
+
+				if (WALK(sc->cast))
+					return true;
+				if (WALK(sc->expr))
+					return true;
+			}
+			break;
+		case T_SafeTypeCastExpr:
+			{
+				SafeTypeCastExpr   *stc = (SafeTypeCastExpr *) node;
+
+				if (WALK(stc->source_expr))
+					return true;
+				if (WALK(stc->cast_expr))
+					return true;
+				if (WALK(stc->default_expr))
+					return true;
+			}
+			break;
 		case T_CollateClause:
 			return WALK(((CollateClause *) node)->arg);
 		case T_SortBy:
diff --git a/src/backend/nodes/queryjumblefuncs.c b/src/backend/nodes/queryjumblefuncs.c
index 31f9715197..76426c88e9 100644
--- a/src/backend/nodes/queryjumblefuncs.c
+++ b/src/backend/nodes/queryjumblefuncs.c
@@ -74,6 +74,7 @@ static void _jumbleElements(JumbleState *jstate, List *elements, Node *node);
 static void _jumbleParam(JumbleState *jstate, Node *node);
 static void _jumbleA_Const(JumbleState *jstate, Node *node);
 static void _jumbleVariableSetStmt(JumbleState *jstate, Node *node);
+static void _jumbleSafeTypeCastExpr(JumbleState *jstate, Node *node);
 static void _jumbleRangeTblEntry_eref(JumbleState *jstate,
 									  RangeTblEntry *rte,
 									  Alias *expr);
@@ -758,6 +759,19 @@ _jumbleVariableSetStmt(JumbleState *jstate, Node *node)
 	JUMBLE_LOCATION(location);
 }
 
+static void
+_jumbleSafeTypeCastExpr(JumbleState *jstate, Node *node)
+{
+	SafeTypeCastExpr *expr = (SafeTypeCastExpr *) node;
+
+	if (expr->cast_expr == NULL)
+		JUMBLE_NODE(source_expr);
+	else
+		JUMBLE_NODE(cast_expr);
+
+	JUMBLE_NODE(default_expr);
+}
+
 /*
  * Custom query jumble function for RangeTblEntry.eref.
  */
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index f45131c34c..a7b4e80971 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -2938,6 +2938,25 @@ eval_const_expressions_mutator(Node *node,
 												  copyObject(jve->format));
 			}
 
+		case T_SafeTypeCastExpr:
+			{
+				SafeTypeCastExpr *stc = (SafeTypeCastExpr *) node;
+				SafeTypeCastExpr	   *newexpr;
+				Node	   *source_expr = stc->source_expr;
+				Node	   *default_expr = stc->default_expr;
+
+				source_expr = eval_const_expressions_mutator(source_expr, context);
+				default_expr = eval_const_expressions_mutator(default_expr, context);
+
+				newexpr = makeNode(SafeTypeCastExpr);
+				newexpr->source_expr = source_expr;
+				newexpr->cast_expr = stc->cast_expr;
+				newexpr->default_expr = default_expr;
+				newexpr->resulttype = stc->resulttype;
+				newexpr->resulttypmod = stc->resulttypmod;
+				newexpr->resultcollid = stc->resultcollid;
+				return (Node *) newexpr;
+			}
 		case T_SubPlan:
 		case T_AlternativeSubPlan:
 
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 70a0d832a1..5176d7e4d2 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
 
 %type <node>	json_format_clause
 				json_format_clause_opt
@@ -15936,8 +15938,25 @@ func_expr_common_subexpr:
 				{
 					$$ = makeSQLValueFunction(SVFOP_CURRENT_SCHEMA, -1, @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
+					{
+						SafeTypeCast *safecast = makeNode(SafeTypeCast);
+
+						safecast->cast = (Node *) cast;
+						safecast->expr = $6;
+
+						/*
+						 * On-error actions must themselves be typecast to the
+						 * same type as the original expression.
+						 */
+						$$ = (Node *) safecast;
+					}
+				}
 			| EXTRACT '(' extract_list ')'
 				{
 					$$ = (Node *) makeFuncCall(SystemFuncName("extract"),
@@ -16323,6 +16342,14 @@ func_expr_common_subexpr:
 				}
 			;
 
+cast_on_error_clause: cast_on_error_action ON CONVERSION_P ERROR_P { $$ = $1; }
+			| /* EMPTY */ { $$ = NULL; }
+		;
+
+cast_on_error_action: ERROR_P { $$ = NULL; }
+			| NULL_P { $$ = makeNullAConst(-1); }
+			| DEFAULT a_expr { $$ = $2; }
+		;
 
 /*
  * SQL/XML support
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index d66276801c..2f5a3059ac 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "catalog/pg_aggregate.h"
+#include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "commands/dbcommands.h"
 #include "miscadmin.h"
@@ -37,6 +38,7 @@
 #include "utils/date.h"
 #include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
+#include "utils/syscache.h"
 #include "utils/timestamp.h"
 #include "utils/xml.h"
 
@@ -60,7 +62,10 @@ 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 *can_coerce);
+static Node *transformArrayExprSafe(ParseState *pstate, A_ArrayExpr *a,
+									Oid array_type, Oid element_type, int32 typmod);
 static Node *transformRowExpr(ParseState *pstate, RowExpr *r, bool allowDefault);
 static Node *transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c);
 static Node *transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m);
@@ -76,6 +81,7 @@ static Node *transformWholeRowRef(ParseState *pstate,
 								  int sublevels_up, int location);
 static Node *transformIndirection(ParseState *pstate, A_Indirection *ind);
 static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
+static Node *transformTypeSafeCast(ParseState *pstate, SafeTypeCast *tc);
 static Node *transformCollateClause(ParseState *pstate, CollateClause *c);
 static Node *transformJsonObjectConstructor(ParseState *pstate,
 											JsonObjectConstructor *ctor);
@@ -106,6 +112,8 @@ static Expr *make_distinct_op(ParseState *pstate, List *opname,
 							  Node *ltree, Node *rtree, int location);
 static Node *make_nulltest_from_distinct(ParseState *pstate,
 										 A_Expr *distincta, Node *arg);
+static bool CovertUnknownConstSafe(ParseState *pstate, Node *node,
+								   Oid targetType, int32 targetTypeMod);
 
 
 /*
@@ -163,13 +171,17 @@ 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:
 			result = transformTypeCast(pstate, (TypeCast *) expr);
 			break;
 
+		case T_SafeTypeCast:
+			result = transformTypeSafeCast(pstate, (SafeTypeCast *) expr);
+			break;
+
 		case T_CollateClause:
 			result = transformCollateClause(pstate, (CollateClause *) expr);
 			break;
@@ -2004,16 +2016,127 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
 	return result;
 }
 
+/*
+ * Return true iff successfuly coerced a Unknown Const to targetType
+*/
+static bool CovertUnknownConstSafe(ParseState *pstate, Node *node,
+								   Oid targetType, int32 targetTypeMod)
+{
+	Oid			baseTypeId;
+	int32		baseTypeMod;
+	int32		inputTypeMod;
+	Type		baseType;
+	char		*string;
+	Datum		datum;
+	bool		converted;
+	Const	   *con;
+
+	Assert(IsA(node, Const));
+	Assert(exprType(node) == UNKNOWNOID);
+
+	con = (Const *) node;
+	baseTypeMod = targetTypeMod;
+	baseTypeId = getBaseTypeAndTypmod(targetType, &baseTypeMod);
+
+	if (baseTypeId == INTERVALOID)
+		inputTypeMod = baseTypeMod;
+	else
+		inputTypeMod = -1;
+	baseType = typeidType(baseTypeId);
+
+	/*
+	 * We assume here that UNKNOWN's internal representation is the same as
+	 * CSTRING.
+	*/
+	if (!con->constisnull)
+		string = DatumGetCString(con->constvalue);
+	else
+		string = NULL;
+
+	converted = stringTypeDatumSafe(baseType,
+									string,
+									inputTypeMod,
+									&datum);
+
+	ReleaseSysCache(baseType);
+
+	return converted;
+}
+
+/*
+ * As with transformArrayExpr, we need to correctly parse back a query like
+ * CAST(ARRAY['three', 'a'] AS int[] DEFAULT '{21,22}' ON CONVERSION ERROR).  We
+ * cannot allow eval_const_expressions to fold the A_ArrayExpr into a Const
+ * node, as this may cause an error too early. The A_ArrayExpr still need
+ * transformed into an ArrayExpr for the deparse purpose.
+ */
+static Node *
+transformArrayExprSafe(ParseState *pstate, A_ArrayExpr *a,
+					   Oid array_type, Oid element_type, int32 typmod)
+{
+	ArrayExpr  *newa = makeNode(ArrayExpr);
+	List	   *newelems = NIL;
+	ListCell   *element;
+
+	newa->multidims = false;
+	foreach(element, a->elements)
+	{
+		Node	   *e = (Node *) lfirst(element);
+		Node	   *newe;
+
+		/*
+		 * If an element is itself an A_ArrayExpr, recurse directly so that we
+		 * can pass down any target type we were given.
+		 */
+		if (IsA(e, A_ArrayExpr))
+		{
+			newe = transformArrayExprSafe(pstate,(A_ArrayExpr *) e, array_type, element_type, typmod);
+			/* we certainly have an array here */
+			Assert(array_type == InvalidOid || array_type == exprType(newe));
+			newa->multidims = true;
+		}
+		else
+		{
+			newe = transformExprRecurse(pstate, e);
+
+			if (!newa->multidims)
+			{
+				Oid			newetype = exprType(newe);
+
+				if (newetype != INT2VECTOROID && newetype != OIDVECTOROID &&
+					type_is_array(newetype))
+					newa->multidims = true;
+			}
+		}
+
+		newelems = lappend(newelems, newe);
+	}
+
+	newa->array_typeid = array_type;
+	/* array_collid will be set by parse_collate.c */
+	newa->element_typeid = element_type;
+	newa->elements = newelems;
+	newa->list_start = a->list_start;
+	newa->list_end = -1;
+	newa->location = -1;
+
+	return (Node *) newa;
+}
+
 /*
  * transformArrayExpr
  *
  * If the caller specifies the target type, the resulting array will
  * be of exactly that type.  Otherwise we try to infer a common type
  * for the elements using select_common_type().
+ *
+ * can_coerce is not null only when CAST(DEFAULT... ON CONVERSION ERROR) is
+ * specified. If we found out we can not cast to target type and can_coerce is
+ * not null, return NULL earlier and set can_coerce set false.
  */
 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 *can_coerce)
 {
 	ArrayExpr  *newa = makeNode(ArrayExpr);
 	List	   *newelems = NIL;
@@ -2044,9 +2167,10 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
 									  (A_ArrayExpr *) e,
 									  array_type,
 									  element_type,
-									  typmod);
+									  typmod,
+									  can_coerce);
 			/* we certainly have an array here */
-			Assert(array_type == InvalidOid || array_type == exprType(newe));
+			Assert(can_coerce || array_type == InvalidOid || array_type == exprType(newe));
 			newa->multidims = true;
 		}
 		else
@@ -2072,6 +2196,9 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
 		newelems = lappend(newelems, newe);
 	}
 
+	if (can_coerce && !*can_coerce)
+		return NULL;
+
 	/*
 	 * Select a target type for the elements.
 	 *
@@ -2139,6 +2266,17 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
 		Node	   *e = (Node *) lfirst(element);
 		Node	   *newe;
 
+		if (can_coerce && (*can_coerce) && IsA(e, Const) && exprType(e) == UNKNOWNOID)
+		{
+			if (!CovertUnknownConstSafe(pstate, e, coerce_type, typmod))
+			{
+				*can_coerce = false;
+				list_free(newcoercedelems);
+				newcoercedelems = NIL;
+				return NULL;
+			}
+		}
+
 		if (coerce_hard)
 		{
 			newe = coerce_to_target_type(pstate, e,
@@ -2742,7 +2880,8 @@ transformTypeCast(ParseState *pstate, TypeCast *tc)
 									  (A_ArrayExpr *) arg,
 									  targetBaseType,
 									  elementType,
-									  targetBaseTypmod);
+									  targetBaseTypmod,
+									  NULL);
 		}
 		else
 			expr = transformExprRecurse(pstate, arg);
@@ -2779,6 +2918,171 @@ transformTypeCast(ParseState *pstate, TypeCast *tc)
 	return result;
 }
 
+
+/*
+ * Handle an explicit CAST(... DEFAULT ... ON CONVERSION ERROR) construct.
+ *
+ * Transform SafeTypeCast node, look up the type name, and apply any necessary
+ * coercion function(s).
+ */
+static Node *
+transformTypeSafeCast(ParseState *pstate, SafeTypeCast *tc)
+{
+	SafeTypeCastExpr   *result;
+	Node	   *def_expr;
+	Node	   *cast_expr = NULL;
+	Node	   *source_expr = NULL;
+	Node	   *array_expr = NULL;
+	Oid			inputType = InvalidOid;
+	Oid			targetType;
+	int32		targetTypmod;
+	int			location;
+	TypeCast   *tcast = (TypeCast *) tc->cast;
+	Node	   *tc_arg = tcast->arg;
+	bool		can_coerce = true;
+	int			def_expr_loc = -1;
+
+	result = makeNode(SafeTypeCastExpr);
+	/* Look up the type name first */
+	typenameTypeIdAndMod(pstate, tcast->typeName, &targetType, &targetTypmod);
+
+	result->resulttype = targetType;
+	result->resulttypmod = targetTypmod;
+
+	/* now looking at cast fail default expression */
+	def_expr_loc = exprLocation(tc->expr);
+	def_expr = transformExprRecurse(pstate, tc->expr);
+
+	if (expression_returns_set(def_expr))
+		ereport(ERROR,
+				errcode(ERRCODE_DATATYPE_MISMATCH),
+				errmsg("DEFAULT expression must not return a set"),
+				parser_coercion_errposition(pstate, def_expr_loc, def_expr));
+
+	if (IsA(def_expr, Aggref) || IsA(def_expr, WindowFunc))
+		ereport(ERROR,
+				errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				errmsg("DEFAULT expression function must be a normal function"),
+				parser_coercion_errposition(pstate, def_expr_loc, def_expr));
+
+	if (IsA(def_expr, FuncExpr))
+	{
+		if (get_func_prokind(((FuncExpr *) def_expr)->funcid) != PROKIND_FUNCTION)
+			ereport(ERROR,
+					errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+					errmsg("DEFAULT expression function must be a normal function"),
+					parser_coercion_errposition(pstate, def_expr_loc, def_expr));
+	}
+
+	def_expr = coerce_to_target_type(pstate, def_expr, exprType(def_expr),
+									 targetType, targetTypmod,
+									 COERCION_EXPLICIT,
+									 COERCE_EXPLICIT_CAST,
+									 exprLocation(def_expr));
+	if (def_expr == NULL)
+		ereport(ERROR,
+				errcode(ERRCODE_CANNOT_COERCE),
+				errmsg("cannot cast on_error default expression to type %s",
+						format_type_be(targetType)),
+				parser_coercion_errposition(pstate, def_expr_loc, def_expr));
+
+	/*
+	 * If the subject of the typecast is an ARRAY[] construct and the target
+	 * type is an array type, we invoke transformArrayExpr() directly so that
+	 * we can pass down the type information.  This avoids some cases where
+	 * transformArrayExpr() might not infer the correct type.  Otherwise, just
+	 * transform the argument normally.
+	 */
+	if (IsA(tc_arg, A_ArrayExpr))
+	{
+		Oid			targetBaseType;
+		int32		targetBaseTypmod;
+		Oid			elementType;
+
+		/*
+		 * If target is a domain over array, work with the base array type
+		 * here.  Below, we'll cast the array type to the domain.  In the
+		 * usual case that the target is not a domain, the remaining steps
+		 * will be a no-op.
+		 */
+		targetBaseTypmod = targetTypmod;
+		targetBaseType = getBaseTypeAndTypmod(targetType, &targetBaseTypmod);
+		elementType = get_element_type(targetBaseType);
+
+		if (OidIsValid(elementType))
+		{
+			array_expr = copyObject(tc_arg);
+
+			source_expr = transformArrayExpr(pstate,
+											 (A_ArrayExpr *) tc_arg,
+											 targetBaseType,
+											 elementType,
+											 targetBaseTypmod,
+											 &can_coerce);
+			if (!can_coerce)
+				source_expr =  transformArrayExprSafe(pstate,
+													  (A_ArrayExpr *) array_expr,
+													  targetBaseType,
+													  elementType,
+													  targetBaseTypmod);
+		}
+		else
+			source_expr = transformExprRecurse(pstate, tc_arg);
+	}
+	else
+		source_expr = transformExprRecurse(pstate, tc_arg);
+
+	inputType = exprType(source_expr);
+	if (inputType == InvalidOid && can_coerce)
+		return (Node *) result;			/* do nothing if NULL input */
+
+
+	if (can_coerce && IsA(source_expr, Const) && exprType(source_expr) == UNKNOWNOID)
+		can_coerce = CovertUnknownConstSafe(pstate,
+											source_expr,
+											targetType,
+											targetTypmod);
+
+	/*
+	 * Location of the coercion is preferentially the location of the :: or
+	 * 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 = tcast->location;
+	if (location < 0)
+		location = tcast->typeName->location;
+
+	if (can_coerce)
+	{
+		Oid		inputbase = getBaseType(inputType);
+		Oid 	targetbase = getBaseType(targetType);
+
+		/*
+		 * It's hard to make function numeric_cash error safe, especially callee
+		 * numeric_mul. So we have to disallow safe cast from numeric to money
+		 */
+		if (inputbase == NUMERICOID && targetbase == MONEYOID)
+			ereport(ERROR,
+					errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					errmsg("cannot cast type %s to %s when %s clause behavior is not %s",
+						   format_type_be(inputType),
+						   format_type_be(targetType),
+						   "CAST ... ON CONVERSION ERROR", "ERROR"),
+					parser_errposition(pstate, exprLocation(source_expr)));
+		cast_expr = coerce_to_target_type(pstate, source_expr, inputType,
+										  targetType, targetTypmod,
+										  COERCION_EXPLICIT,
+										  COERCE_EXPLICIT_CAST,
+										  location);
+	}
+
+	result->source_expr = source_expr;
+	result->cast_expr = cast_expr;
+	result->default_expr = def_expr;
+
+	return (Node *) result;
+}
+
 /*
  * Handle an explicit COLLATE clause.
  *
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 4aba0d9d4d..812ed18c16 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -1823,6 +1823,20 @@ FigureColnameInternal(Node *node, char **name)
 				}
 			}
 			break;
+		case T_SafeTypeCast:
+			strength = FigureColnameInternal(((SafeTypeCast *) node)->cast,
+											 name);
+			if (strength <= 1)
+			{
+				TypeCast *node_cast;
+				node_cast = (TypeCast *)((SafeTypeCast *) node)->cast;
+				if (node_cast->typeName != NULL)
+				{
+					*name = strVal(llast(node_cast->typeName->names));
+					return 1;
+				}
+			}
+			break;
 		case T_CollateClause:
 			return FigureColnameInternal(((CollateClause *) node)->arg, name);
 		case T_GroupingFunc:
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 7713bdc6af..02e5f9c92d 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/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index b5f98bf22f..6bd8a989db 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -3295,6 +3295,12 @@ array_map(Datum arrayd,
 			return (Datum) 0;
 		}
 
+		if (SOFT_ERROR_OCCURRED(exprstate->escontext))
+		{
+			pfree(values);
+			pfree(nulls);
+			return (Datum) 0;
+		}
 		if (nulls[i])
 			hasnulls = true;
 		else
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 3d6e6bdbfd..ee868de2c6 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -10534,6 +10534,21 @@ get_rule_expr(Node *node, deparse_context *context,
 			}
 			break;
 
+		case T_SafeTypeCastExpr:
+			{
+				SafeTypeCastExpr   *stcexpr = castNode(SafeTypeCastExpr, node);
+
+				appendStringInfoString(buf, "CAST(");
+				get_rule_expr(stcexpr->source_expr, context, showimplicit);
+
+				appendStringInfo(buf, " AS %s ",
+								 format_type_with_typemod(stcexpr->resulttype, stcexpr->resulttypmod));
+
+				appendStringInfoString(buf, "DEFAULT ");
+				get_rule_expr(stcexpr->default_expr, context, showimplicit);
+				appendStringInfoString(buf, " ON CONVERSION ERROR)");
+			}
+			break;
 		case T_JsonExpr:
 			{
 				JsonExpr   *jexpr = (JsonExpr *) node;
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 782291d999..9de895e682 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -1759,6 +1759,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/executor/execExpr.h b/src/include/executor/execExpr.h
index 7536620370..0afcf09c08 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -265,6 +265,7 @@ typedef enum ExprEvalOp
 	EEOP_XMLEXPR,
 	EEOP_JSON_CONSTRUCTOR,
 	EEOP_IS_JSON,
+	EEOP_SAFETYPE_CAST,
 	EEOP_JSONEXPR_PATH,
 	EEOP_JSONEXPR_COERCION,
 	EEOP_JSONEXPR_COERCION_FINISH,
@@ -750,6 +751,12 @@ typedef struct ExprEvalStep
 			JsonIsPredicate *pred;	/* original expression node */
 		}			is_json;
 
+		/* for EEOP_SAFECAST */
+		struct
+		{
+			struct SafeTypeCastState *stcstate;	/* original expression node */
+		}			stcexpr;
+
 		/* for EEOP_JSONEXPR_PATH */
 		struct
 		{
@@ -892,6 +899,7 @@ extern int	ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op,
 								 ExprContext *econtext);
 extern void ExecEvalJsonCoercion(ExprState *state, ExprEvalStep *op,
 								 ExprContext *econtext);
+int ExecEvalSafeTypeCast(ExprState *state, ExprEvalStep *op);
 extern void ExecEvalJsonCoercionFinish(ExprState *state, ExprEvalStep *op);
 extern void ExecEvalGroupingFunc(ExprState *state, ExprEvalStep *op);
 extern void ExecEvalMergeSupportFunc(ExprState *state, ExprEvalStep *op,
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 0fe7b4ebc7..299d4eef4e 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -750,6 +750,9 @@ extern bool DirectInputFunctionCallSafe(PGFunction func, 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,
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e107d6e5f8..282bfa770e 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1058,6 +1058,36 @@ typedef struct DomainConstraintState
 	ExprState  *check_exprstate;	/* check_expr's eval state, or NULL */
 } DomainConstraintState;
 
+typedef struct SafeTypeCastState
+{
+	SafeTypeCastExpr *stcexpr;
+
+	/* Set to true if type cast cause an error. */
+	NullableDatum error;
+
+	/*
+	 * Addresses of steps that implement DEFAULT expr ON CONVERSION ERROR for
+	 * safe type cast.
+	 */
+	int 		jump_error;
+
+	/*
+	 * Address to jump to when skipping all the steps to evaulate the default
+	 * expression after performing ExecEvalSafeTypeCast().
+	 */
+	int 		jump_end;
+
+	/*
+	 * For error-safe evaluation of coercions.  When DEFAULT expr ON CONVERSION
+	 * ON ERROR is specified, a pointer to this is passed to ExecInitExprRec()
+	 * when initializing the coercion expressions, see ExecInitSafeTypeCastExpr.
+	 *
+	 * Reset for each evaluation of EEOP_SAFETYPE_CAST.
+	 */
+	ErrorSaveContext escontext;
+
+} SafeTypeCastState;
+
 /*
  * State for JsonExpr evaluation, too big to inline.
  *
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 28e2e8dc0f..2e071b2abf 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -399,6 +399,12 @@ typedef struct TypeCast
 	ParseLoc	location;		/* token location, or -1 if unknown */
 } TypeCast;
 
+typedef struct SafeTypeCast
+{
+	NodeTag		type;
+	Node		*cast;
+	Node	   	*expr;		/* default expr */
+} SafeTypeCast;
 /*
  * CollateClause - a COLLATE expression
  */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 6dfca3cb35..d2df7c2893 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -756,6 +756,41 @@ typedef enum CoercionForm
 	COERCE_SQL_SYNTAX,			/* display with SQL-mandated special syntax */
 } CoercionForm;
 
+/*
+ * SafeTypeCastExpr -
+ *		Transformed representation of
+ * CAST(expr AS typename DEFAULT expr2 ON ERROR)
+ * CAST(expr AS typename NULL ON ERROR)
+ */
+typedef struct SafeTypeCastExpr
+{
+	pg_node_attr(custom_query_jumble)
+
+	Expr		xpr;
+
+	/* transformed expression being casted */
+	Node	   *source_expr;
+
+	/*
+	 * The transformed cast expression; this may be NULL if the two types can't
+	 * be cast.
+	 */
+	Node	   *cast_expr;
+
+	/* Fall back to the default expression if the cast evaluation fails. */
+	Node	   *default_expr;
+
+	/* cast result data type */
+	Oid			resulttype pg_node_attr(query_jumble_ignore);
+
+	/* cast result data type typmod (usually -1) */
+	int32		resulttypmod pg_node_attr(query_jumble_ignore);
+
+	/* cast result data type collation (usually -1) */
+	Oid			resultcollid pg_node_attr(query_jumble_ignore);
+
+} SafeTypeCastExpr;
+
 /*
  * FuncExpr - expression node for a function call
  *
diff --git a/src/include/parser/parse_type.h b/src/include/parser/parse_type.h
index 0d919d8bfa..12381aed64 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);
diff --git a/src/test/regress/expected/cast.out b/src/test/regress/expected/cast.out
new file mode 100644
index 0000000000..a7d7d6d674
--- /dev/null
+++ b/src/test/regress/expected/cast.out
@@ -0,0 +1,230 @@
+SET extra_float_digits = 0;
+-- CAST DEFAULT ON CONVERSION ERROR
+VALUES (CAST('error' AS integer ERROR ON CONVERSION ERROR)); --error
+ERROR:  invalid input syntax for type integer: "error"
+LINE 1: VALUES (CAST('error' AS integer ERROR ON CONVERSION ERROR));
+                     ^
+VALUES (CAST('error' AS integer NULL ON CONVERSION ERROR));
+ column1 
+---------
+        
+(1 row)
+
+VALUES (CAST('error' AS integer DEFAULT 42 ON CONVERSION ERROR));
+ column1 
+---------
+      42
+(1 row)
+
+SELECT CAST(1 AS date DEFAULT NULL ON CONVERSION ERROR);
+ date 
+------
+ 
+(1 row)
+
+SELECT CAST(1::numeric AS money DEFAULT NULL ON CONVERSION ERROR);
+ERROR:  cannot cast type numeric to money when CAST ... ON CONVERSION ERROR clause behavior is not ERROR
+LINE 1: SELECT CAST(1::numeric AS money DEFAULT NULL ON CONVERSION E...
+                    ^
+CREATE OR REPLACE FUNCTION ret_int8() RETURNS bigint AS
+$$
+BEGIN RETURN 2147483648; END;
+$$
+LANGUAGE plpgsql IMMUTABLE;
+SELECT CAST('a' as int DEFAULT ret_int8() ON CONVERSION ERROR); --error
+ERROR:  integer out of range
+SELECT CAST('a' as date DEFAULT ret_int8() ON CONVERSION ERROR); --error
+ERROR:  cannot cast on_error default expression to type date
+LINE 1: SELECT CAST('a' as date DEFAULT ret_int8() ON CONVERSION ERR...
+                                        ^
+-- test array coerce
+SELECT CAST('{123,abc,456}' AS integer[] DEFAULT '{-789}' ON CONVERSION ERROR);
+  int4  
+--------
+ {-789}
+(1 row)
+
+SELECT CAST('{234,def,567}'::text[] AS integer[] DEFAULT '{-1011}' ON CONVERSION ERROR);
+  int4   
+---------
+ {-1011}
+(1 row)
+
+SELECT CAST(ARRAY[['1'], ['three'],['a']] AS INTEGER[] DEFAULT '{1,2}' ON CONVERSION ERROR);
+ array 
+-------
+ {1,2}
+(1 row)
+
+SELECT CAST(ARRAY[['1', '2'], ['three', 'a']] AS text[] DEFAULT '{21,22}' ON CONVERSION ERROR);
+       array       
+-------------------
+ {{1,2},{three,a}}
+(1 row)
+
+-- test valid DEFAULT expression for CAST = ON CONVERSION ERROR
+CREATE OR REPLACE FUNCTION ret_setint() RETURNS SETOF integer AS
+$$
+BEGIN RETURN QUERY EXECUTE 'select 1 union all select 1'; END;
+$$
+LANGUAGE plpgsql IMMUTABLE;
+CREATE TABLE tcast(a text[], b int GENERATED BY DEFAULT AS IDENTITY, c text default '1');
+INSERT INTO tcast VALUES ('{12}'), ('{1,a, b}'), ('{{1,2}, {c,d}}'), ('{13}');
+SELECT CAST('a' as int DEFAULT ret_setint() ON CONVERSION ERROR) FROM tcast; --error
+ERROR:  DEFAULT expression must not return a set
+LINE 1: SELECT CAST('a' as int DEFAULT ret_setint() ON CONVERSION ER...
+                                       ^
+SELECT CAST('a' as int DEFAULT sum(1) ON CONVERSION ERROR); --error
+ERROR:  DEFAULT expression function must be a normal function
+LINE 1: SELECT CAST('a' as int DEFAULT sum(1) ON CONVERSION ERROR);
+                                       ^
+SELECT CAST('a' as int DEFAULT sum(1) over() ON CONVERSION ERROR); --error
+ERROR:  DEFAULT expression function must be a normal function
+LINE 1: SELECT CAST('a' as int DEFAULT sum(1) over() ON CONVERSION E...
+                                       ^
+SELECT CAST('a' as int DEFAULT 'b' ON CONVERSION ERROR); --error
+ERROR:  invalid input syntax for type integer: "b"
+LINE 1: SELECT CAST('a' as int DEFAULT 'b' ON CONVERSION ERROR);
+                                       ^
+SELECT CAST(t AS text[] DEFAULT '{21,22, ' || b || '}' ON CONVERSION ERROR) FROM tcast as t;
+     t     
+-----------
+ {21,22,1}
+ {21,22,2}
+ {21,22,3}
+ {21,22,4}
+(4 rows)
+
+SELECT CAST(t.a AS int[] DEFAULT '{21,22}'::int[] || b ON CONVERSION ERROR) FROM tcast as t;
+     a     
+-----------
+ {12}
+ {21,22,2}
+ {21,22,3}
+ {13}
+(4 rows)
+
+-- test with domain
+CREATE DOMAIN d_int42 as int check (value = 42) NOT NULL;
+CREATE DOMAIN d_char3_not_null as char(3) NOT NULL;
+CREATE TYPE comp_domain_with_typmod AS (a d_char3_not_null, b int);
+SELECT CAST(11 AS d_int42 DEFAULT 41 ON CONVERSION ERROR); --error
+ERROR:  value for domain d_int42 violates check constraint "d_int42_check"
+SELECT CAST(11 AS d_int42 DEFAULT 42 ON CONVERSION ERROR); --ok
+ d_int42 
+---------
+      42
+(1 row)
+
+SELECT CAST(NULL AS d_int42 DEFAULT NULL ON CONVERSION ERROR); --error
+ERROR:  domain d_int42 does not allow null values
+SELECT CAST(NULL AS d_int42 DEFAULT 42 ON CONVERSION ERROR); --ok
+ d_int42 
+---------
+      42
+(1 row)
+
+SELECT CAST('(,42)' AS comp_domain_with_typmod DEFAULT NULL ON CONVERSION ERROR);
+ comp_domain_with_typmod 
+-------------------------
+ 
+(1 row)
+
+SELECT CAST('(NULL,42)' AS comp_domain_with_typmod DEFAULT '(1,2)' ON CONVERSION ERROR);
+ comp_domain_with_typmod 
+-------------------------
+ ("1  ",2)
+(1 row)
+
+SELECT CAST('(NULL,42)' AS comp_domain_with_typmod DEFAULT '(1234,2)' ON CONVERSION ERROR); --error
+ERROR:  value too long for type character(3)
+LINE 1: ...ST('(NULL,42)' AS comp_domain_with_typmod DEFAULT '(1234,2)'...
+                                                             ^
+--test cast numeric value with fraction to another numeric value
+CREATE TABLE safecast(col1 float4, col2 float8, col3 numeric, col4 numeric[]);
+INSERT INTO safecast VALUES('11.1234', '11.1234', '11.1234', '{11.1234}'::numeric[]);
+INSERT INTO safecast VALUES('inf', 'inf', 'inf', '{11.1234, 12, inf, NaN}'::numeric[]);
+INSERT INTO safecast VALUES('-inf', '-inf', '-inf', '{11.1234, 12, -inf, NaN}'::numeric[]);
+INSERT INTO safecast VALUES('NaN', 'NaN', 'NaN', '{11.1234, 12, -inf, NaN}'::numeric[]);
+SELECT col1 as float4,
+       CAST(col1 AS int2 DEFAULT NULL ON CONVERSION ERROR) as to_int2,
+       CAST(col1 AS int4 DEFAULT NULL ON CONVERSION ERROR) as to_int4,
+       CAST(col1 as int8 DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+       CAST(col1 as float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+       CAST(col1 as float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+       CAST(col1 as numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+       CAST(col1 as numeric(10,1) DEFAULT NULL ON CONVERSION ERROR) as to_numeric_scale
+FROM safecast;
+  float4   | to_int2 | to_int4 | to_int8 | to_float4 |    to_float8     | to_numeric | to_numeric_scale 
+-----------+---------+---------+---------+-----------+------------------+------------+------------------
+   11.1234 |      11 |      11 |      11 |   11.1234 | 11.1233997344971 |    11.1234 |             11.1
+  Infinity |         |         |         |  Infinity |         Infinity |   Infinity |                 
+ -Infinity |         |         |         | -Infinity |        -Infinity |  -Infinity |                 
+       NaN |         |         |         |       NaN |              NaN |        NaN |              NaN
+(4 rows)
+
+SELECT col2 as float8,
+       CAST(col2 AS int2 DEFAULT NULL ON CONVERSION ERROR) as to_int2,
+       CAST(col2 AS int4 DEFAULT NULL ON CONVERSION ERROR) as to_int4,
+       CAST(col2 as int8 DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+       CAST(col2 as float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+       CAST(col2 as float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+       CAST(col2 as numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+       CAST(col2 as numeric(10,1) DEFAULT NULL ON CONVERSION ERROR) as to_numeric_scale
+FROM safecast;
+  float8   | to_int2 | to_int4 | to_int8 | to_float4 | to_float8 | to_numeric | to_numeric_scale 
+-----------+---------+---------+---------+-----------+-----------+------------+------------------
+   11.1234 |      11 |      11 |      11 |   11.1234 |   11.1234 |    11.1234 |             11.1
+  Infinity |         |         |         |  Infinity |  Infinity |   Infinity |                 
+ -Infinity |         |         |         | -Infinity | -Infinity |  -Infinity |                 
+       NaN |         |         |         |       NaN |       NaN |        NaN |              NaN
+(4 rows)
+
+SELECT col3 as numeric,
+       CAST(col3 AS int2 DEFAULT NULL ON CONVERSION ERROR) as to_int2,
+       CAST(col3 AS int4 DEFAULT NULL ON CONVERSION ERROR) as to_int4,
+       CAST(col3 as int8 DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+       CAST(col3 as float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+       CAST(col3 as float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+       CAST(col3 as numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+       CAST(col3 as numeric(10,1) DEFAULT NULL ON CONVERSION ERROR) as to_numeric_scale
+FROM safecast;
+  numeric  | to_int2 | to_int4 | to_int8 | to_float4 | to_float8 | to_numeric | to_numeric_scale 
+-----------+---------+---------+---------+-----------+-----------+------------+------------------
+   11.1234 |      11 |      11 |      11 |   11.1234 |   11.1234 |    11.1234 |             11.1
+  Infinity |         |         |         |  Infinity |  Infinity |   Infinity |                 
+ -Infinity |         |         |         | -Infinity | -Infinity |  -Infinity |                 
+       NaN |         |         |         |       NaN |       NaN |        NaN |              NaN
+(4 rows)
+
+SELECT col4 as num_arr,
+       CAST(col4 AS int2[] DEFAULT NULL ON CONVERSION ERROR) as int2arr,
+       CAST(col4 AS int4[] DEFAULT NULL ON CONVERSION ERROR) as int4arr,
+       CAST(col4 as int8[] DEFAULT NULL ON CONVERSION ERROR) as int8arr,
+       CAST(col4 as float4[] DEFAULT NULL ON CONVERSION ERROR) as f4arr,
+       CAST(col4 as float8[] DEFAULT NULL ON CONVERSION ERROR) as f8arr,
+       CAST(col4 as numeric(10,1)[] DEFAULT NULL ON CONVERSION ERROR) as numarr
+FROM safecast;
+          num_arr           | int2arr | int4arr | int8arr |           f4arr            |           f8arr            | numarr 
+----------------------------+---------+---------+---------+----------------------------+----------------------------+--------
+ {11.1234}                  | {11}    | {11}    | {11}    | {11.1234}                  | {11.1234}                  | {11.1}
+ {11.1234,12,Infinity,NaN}  |         |         |         | {11.1234,12,Infinity,NaN}  | {11.1234,12,Infinity,NaN}  | 
+ {11.1234,12,-Infinity,NaN} |         |         |         | {11.1234,12,-Infinity,NaN} | {11.1234,12,-Infinity,NaN} | 
+ {11.1234,12,-Infinity,NaN} |         |         |         | {11.1234,12,-Infinity,NaN} | {11.1234,12,-Infinity,NaN} | 
+(4 rows)
+
+-- test deparse
+CREATE VIEW safecastview AS
+SELECT CAST('1234' as char(3) DEFAULT -1111 ON CONVERSION ERROR),
+       CAST(1 as date DEFAULT ((now()::date + random(min=>1, max=>1::int))) ON CONVERSION ERROR) as safecast,
+       CAST(ARRAY[['1'], ['three'],['a']] AS INTEGER[] DEFAULT '{1,2}' ON CONVERSION ERROR) as cast1,
+       CAST(ARRAY[['1', '2'], ['three', 'a']] AS text[] DEFAULT '{21,22}' ON CONVERSION ERROR) as cast2;
+\sv safecastview
+CREATE OR REPLACE VIEW public.safecastview AS
+ SELECT CAST('1234' AS character(3) DEFAULT '-1111'::integer::character(3) ON CONVERSION ERROR) AS bpchar,
+    CAST(1 AS date DEFAULT now()::date + random(min => 1, max => 1) ON CONVERSION ERROR) AS safecast,
+    CAST(ARRAY[ARRAY['1'], ARRAY['three'], ARRAY['a']] AS integer[] DEFAULT '{1,2}'::integer[] ON CONVERSION ERROR) AS cast1,
+    CAST(ARRAY[ARRAY['1'::text, '2'::text], ARRAY['three'::text, 'a'::text]] AS text[] DEFAULT '{21,22}'::text[] ON CONVERSION ERROR) AS cast2
+CREATE INDEX cast_error_idx  ON tcast((cast(c as int DEFAULT random(min=>1, max=>1) ON CONVERSION ERROR))); --error
+ERROR:  functions in index expression must be marked IMMUTABLE
+RESET extra_float_digits;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index a424be2a6b..7c3ed55f90 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -81,7 +81,7 @@ test: brin_bloom brin_multi
 test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.utf8 collate.icu.utf8 incremental_sort create_role without_overlaps generated_virtual
 
 # collate.linux.utf8 and collate.icu.utf8 tests cannot be run in parallel with each other
-test: rules psql psql_crosstab psql_pipeline amutils stats_ext collate.linux.utf8 collate.windows.win1252
+test: rules psql psql_crosstab psql_pipeline amutils stats_ext collate.linux.utf8 collate.windows.win1252 cast
 
 # ----------
 # Run these alone so they don't run out of parallel workers
diff --git a/src/test/regress/sql/cast.sql b/src/test/regress/sql/cast.sql
new file mode 100644
index 0000000000..95a813e5ff
--- /dev/null
+++ b/src/test/regress/sql/cast.sql
@@ -0,0 +1,109 @@
+SET extra_float_digits = 0;
+
+-- CAST DEFAULT ON CONVERSION ERROR
+VALUES (CAST('error' AS integer ERROR ON CONVERSION ERROR)); --error
+VALUES (CAST('error' AS integer NULL ON CONVERSION ERROR));
+VALUES (CAST('error' AS integer DEFAULT 42 ON CONVERSION ERROR));
+SELECT CAST(1 AS date DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST(1::numeric AS money DEFAULT NULL ON CONVERSION ERROR);
+
+CREATE OR REPLACE FUNCTION ret_int8() RETURNS bigint AS
+$$
+BEGIN RETURN 2147483648; END;
+$$
+LANGUAGE plpgsql IMMUTABLE;
+
+SELECT CAST('a' as int DEFAULT ret_int8() ON CONVERSION ERROR); --error
+SELECT CAST('a' as date DEFAULT ret_int8() ON CONVERSION ERROR); --error
+
+-- test array coerce
+SELECT CAST('{123,abc,456}' AS integer[] DEFAULT '{-789}' ON CONVERSION ERROR);
+SELECT CAST('{234,def,567}'::text[] AS integer[] DEFAULT '{-1011}' ON CONVERSION ERROR);
+SELECT CAST(ARRAY[['1'], ['three'],['a']] AS INTEGER[] DEFAULT '{1,2}' ON CONVERSION ERROR);
+SELECT CAST(ARRAY[['1', '2'], ['three', 'a']] AS text[] DEFAULT '{21,22}' ON CONVERSION ERROR);
+
+-- test valid DEFAULT expression for CAST = ON CONVERSION ERROR
+CREATE OR REPLACE FUNCTION ret_setint() RETURNS SETOF integer AS
+$$
+BEGIN RETURN QUERY EXECUTE 'select 1 union all select 1'; END;
+$$
+LANGUAGE plpgsql IMMUTABLE;
+
+CREATE TABLE tcast(a text[], b int GENERATED BY DEFAULT AS IDENTITY, c text default '1');
+INSERT INTO tcast VALUES ('{12}'), ('{1,a, b}'), ('{{1,2}, {c,d}}'), ('{13}');
+SELECT CAST('a' as int DEFAULT ret_setint() ON CONVERSION ERROR) FROM tcast; --error
+SELECT CAST('a' as int DEFAULT sum(1) ON CONVERSION ERROR); --error
+SELECT CAST('a' as int DEFAULT sum(1) over() ON CONVERSION ERROR); --error
+SELECT CAST('a' as int DEFAULT 'b' ON CONVERSION ERROR); --error
+
+SELECT CAST(t AS text[] DEFAULT '{21,22, ' || b || '}' ON CONVERSION ERROR) FROM tcast as t;
+SELECT CAST(t.a AS int[] DEFAULT '{21,22}'::int[] || b ON CONVERSION ERROR) FROM tcast as t;
+
+-- test with domain
+CREATE DOMAIN d_int42 as int check (value = 42) NOT NULL;
+CREATE DOMAIN d_char3_not_null as char(3) NOT NULL;
+CREATE TYPE comp_domain_with_typmod AS (a d_char3_not_null, b int);
+SELECT CAST(11 AS d_int42 DEFAULT 41 ON CONVERSION ERROR); --error
+SELECT CAST(11 AS d_int42 DEFAULT 42 ON CONVERSION ERROR); --ok
+SELECT CAST(NULL AS d_int42 DEFAULT NULL ON CONVERSION ERROR); --error
+SELECT CAST(NULL AS d_int42 DEFAULT 42 ON CONVERSION ERROR); --ok
+SELECT CAST('(,42)' AS comp_domain_with_typmod DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('(NULL,42)' AS comp_domain_with_typmod DEFAULT '(1,2)' ON CONVERSION ERROR);
+SELECT CAST('(NULL,42)' AS comp_domain_with_typmod DEFAULT '(1234,2)' ON CONVERSION ERROR); --error
+
+--test cast numeric value with fraction to another numeric value
+CREATE TABLE safecast(col1 float4, col2 float8, col3 numeric, col4 numeric[]);
+INSERT INTO safecast VALUES('11.1234', '11.1234', '11.1234', '{11.1234}'::numeric[]);
+INSERT INTO safecast VALUES('inf', 'inf', 'inf', '{11.1234, 12, inf, NaN}'::numeric[]);
+INSERT INTO safecast VALUES('-inf', '-inf', '-inf', '{11.1234, 12, -inf, NaN}'::numeric[]);
+INSERT INTO safecast VALUES('NaN', 'NaN', 'NaN', '{11.1234, 12, -inf, NaN}'::numeric[]);
+
+SELECT col1 as float4,
+       CAST(col1 AS int2 DEFAULT NULL ON CONVERSION ERROR) as to_int2,
+       CAST(col1 AS int4 DEFAULT NULL ON CONVERSION ERROR) as to_int4,
+       CAST(col1 as int8 DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+       CAST(col1 as float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+       CAST(col1 as float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+       CAST(col1 as numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+       CAST(col1 as numeric(10,1) DEFAULT NULL ON CONVERSION ERROR) as to_numeric_scale
+FROM safecast;
+
+SELECT col2 as float8,
+       CAST(col2 AS int2 DEFAULT NULL ON CONVERSION ERROR) as to_int2,
+       CAST(col2 AS int4 DEFAULT NULL ON CONVERSION ERROR) as to_int4,
+       CAST(col2 as int8 DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+       CAST(col2 as float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+       CAST(col2 as float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+       CAST(col2 as numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+       CAST(col2 as numeric(10,1) DEFAULT NULL ON CONVERSION ERROR) as to_numeric_scale
+FROM safecast;
+
+SELECT col3 as numeric,
+       CAST(col3 AS int2 DEFAULT NULL ON CONVERSION ERROR) as to_int2,
+       CAST(col3 AS int4 DEFAULT NULL ON CONVERSION ERROR) as to_int4,
+       CAST(col3 as int8 DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+       CAST(col3 as float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+       CAST(col3 as float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+       CAST(col3 as numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+       CAST(col3 as numeric(10,1) DEFAULT NULL ON CONVERSION ERROR) as to_numeric_scale
+FROM safecast;
+
+SELECT col4 as num_arr,
+       CAST(col4 AS int2[] DEFAULT NULL ON CONVERSION ERROR) as int2arr,
+       CAST(col4 AS int4[] DEFAULT NULL ON CONVERSION ERROR) as int4arr,
+       CAST(col4 as int8[] DEFAULT NULL ON CONVERSION ERROR) as int8arr,
+       CAST(col4 as float4[] DEFAULT NULL ON CONVERSION ERROR) as f4arr,
+       CAST(col4 as float8[] DEFAULT NULL ON CONVERSION ERROR) as f8arr,
+       CAST(col4 as numeric(10,1)[] DEFAULT NULL ON CONVERSION ERROR) as numarr
+FROM safecast;
+
+-- test deparse
+CREATE VIEW safecastview AS
+SELECT CAST('1234' as char(3) DEFAULT -1111 ON CONVERSION ERROR),
+       CAST(1 as date DEFAULT ((now()::date + random(min=>1, max=>1::int))) ON CONVERSION ERROR) as safecast,
+       CAST(ARRAY[['1'], ['three'],['a']] AS INTEGER[] DEFAULT '{1,2}' ON CONVERSION ERROR) as cast1,
+       CAST(ARRAY[['1', '2'], ['three', 'a']] AS text[] DEFAULT '{21,22}' ON CONVERSION ERROR) as cast2;
+\sv safecastview
+
+CREATE INDEX cast_error_idx  ON tcast((cast(c as int DEFAULT random(min=>1, max=>1) ON CONVERSION ERROR))); --error
+RESET extra_float_digits;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 8319203857..c22f3ed9e6 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2648,6 +2648,9 @@ STRLEN
 SV
 SYNCHRONIZATION_BARRIER
 SYSTEM_INFO
+SafeTypeCast
+SafeTypeCastExpr
+SafeTypeCastState
 SampleScan
 SampleScanGetSampleSize_function
 SampleScanState
-- 
2.34.1

Reply via email to