public inbox for [email protected]  
help / color / mirror / Atom feed
From: jian he <[email protected]>
To: Corey Huinker <[email protected]>
Cc: Vik Fearing <[email protected]>
Cc: Isaac Morland <[email protected]>
Cc: [email protected]
Subject: Re: CAST(... ON DEFAULT) - WIP build on top of Error-Safe User Functions
Date: Mon, 11 Aug 2025 14:18:44 +0800
Message-ID: <CACJufxE053=bO3pDUpGba6Yz3VGpU_XCbg4HO6Rew5EJ7k7VnQ@mail.gmail.com> (raw)
In-Reply-To: <CADkLM=chahh6ddZFjLL6AUdqzL_Px0raTu-5Jzn2WN8yELtmJw@mail.gmail.com>
References: <CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@mail.gmail.com>
	<CAMsGm5dpfm2PHL8XZvC-JSd+UPkgx3rpReUA=G=4+rUCH+Ntcw@mail.gmail.com>
	<CADkLM=eD_S8mGhPfu5+hXXvXgR0-cxGpGd9dgPzD+nCuO7HFaQ@mail.gmail.com>
	<CACJufxHCMzrHOW=wRe8L30rMhB3sjwAv1LE928Fa7sxMu1Tx-g@mail.gmail.com>
	<[email protected]>
	<CACJufxGRAnwJzu7nMq4ZP=yqa1Sz=qR+mR1TmY0aCDjJoJRRtg@mail.gmail.com>
	<[email protected]>
	<CACJufxFy+DFpJ2e-czyCTAgSJXNFaQGWFKA4mjbW-LAMGc1YBA@mail.gmail.com>
	<CADkLM=f1Jv81=s5Ckazx3zZq=M5KoBJMJkOZux_-L+gezODCEQ@mail.gmail.com>
	<CACJufxGw_OY7K3rfG4kDb902O2guhT-wgTjTJQ=pWeVWRTHpHQ@mail.gmail.com>
	<CADkLM=cFSg3+6Sk00dLAF7Q7jnrKBk6+N5gRxT5BCxRvaGtR-g@mail.gmail.com>
	<CACJufxE_aO5FtBGwhDym-Fwe7k8oJY7a8jcYDx77=t3maPvG0g@mail.gmail.com>
	<CADkLM=chahh6ddZFjLL6AUdqzL_Px0raTu-5Jzn2WN8yELtmJw@mail.gmail.com>

On Tue, Aug 5, 2025 at 12:10 PM Corey Huinker <[email protected]> wrote:
>>
>> In the end, it seems we need to make all these functions in the below
>> query error safe.
>> select castsource::regtype, casttarget::regtype, castfunc,
>> castcontext,castmethod, pp.prosrc, pp.proname from pg_cast pc join pg_proc pp on
>> pp.oid = pc.castfunc and pc.castfunc > 0
>> order by castsource::regtype;
>> It's a lot of work, but seems doable, after playing around with it.
>
> It is do-able. But that's just the cast functions that are part of core postgres.
>
hi.
it's doable for most of the data types.
but I found geometric type related cast function refactoring to error
safe is quite challenging,
so I skip that part refactoring.

>>
>> I don't think we need to change the pg_cast catalog entry,
>> we just need to make these function (pg_cast.castmethod) errors safe.
>
> That would break any user-defined cast functions that were not also error safe, which is to say all of them.
>
> We need a way for user-defined cast functions to indicate whether or not they are error safe, and handle both situations accordingly (i.e. fail a CAST ON DEFAULT when the user-defined cast is not error-safe).
>

I understand what you mean now.
CREATE CAST can use built-in functions too, we have no way to check
whether these CREATE CAST
associated functions are error safe or not.
for example:
CREATE CAST (jsonpath  AS bytea) WITH FUNCTION jsonpath_send (jsonpath
) AS ASSIGNMENT;
select '$'::jsonpath::bytea;

To avoid this situation, we have to add a new column to pg_cast to
indicate whether a cast function is error-safe.
It's unlikely a pg_cast entry has two functions, one is error safe, one is not,
adding pg_cast.casterrorsafefunc would not be that appropriate.

so I choose pg_cast.casterrorsafe would be fine.
pg_cast.casterrorsafe true means castfunc function is error safe, we
can use it as safe cast evaluation
(CAST... DEFAULT defexpr ON CONVERSION ERROR)

please check the attached V6 script:

v6-0001 to v6-0016 is about making existing pg_cast.castfunc function
error safe.
(commit message have associated query to explain the refactoring, as mentioned
above, geometric and money associated type not refactored yet)

v6-0017-make-ArrayCoerceExpr-error-safe.patch
v6-0018-CAST-expr-AS-newtype-DEFAULT-ON-ERROR.patch
is about (CAST... DEFAULT defexpr ON CONVERSION ERROR).


Attachments:

  [text/x-patch] v6-0016-error-safe-for-casting-character-varying-to-other-types-p.patch (1.9K, 2-v6-0016-error-safe-for-casting-character-varying-to-other-types-p.patch)
  download | inline diff:
From 8efc16c4b2e1c6fa14a4851325956c10232e75e9 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Sun, 10 Aug 2025 01:36:17 +0800
Subject: [PATCH v6 16/18] error safe for casting character varying to other
 types per pg_cast

select castsource::regtype, casttarget::regtype, castfunc, castcontext,castmethod, pp.prosrc, pp.proname
from pg_cast pc join pg_proc pp on pp.oid = pc.castfunc and pc.castfunc > 0 and
(castsource::regtype = 'character varying'::regtype)
order by castsource::regtype;

    castsource     |    casttarget     | castfunc | castcontext | castmethod |    prosrc     | proname
-------------------+-------------------+----------+-------------+------------+---------------+----------
 character varying | regclass          |     1079 | i           | f          | text_regclass | regclass
 character varying | "char"            |      944 | a           | f          | text_char     | char
 character varying | name              |     1400 | i           | f          | text_name     | name
 character varying | xml               |     2896 | e           | f          | texttoxml     | xml
 character varying | character varying |      669 | i           | f          | varchar       | varchar
(5 rows)

texttoxml, text_regclass was refactored as error safe in prior patch.
text_char, text_name is already error safe.
---
 src/backend/utils/adt/varchar.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c
index 5cb5c8c46f9..08f1bf5a24d 100644
--- a/src/backend/utils/adt/varchar.c
+++ b/src/backend/utils/adt/varchar.c
@@ -634,7 +634,7 @@ varchar(PG_FUNCTION_ARGS)
 	{
 		for (i = maxmblen; i < len; i++)
 			if (s_data[i] != ' ')
-				ereport(ERROR,
+				ereturn(fcinfo->context, (Datum) 0,
 						(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
 						 errmsg("value too long for type character varying(%d)",
 								maxlen)));
-- 
2.34.1



  [text/x-patch] v6-0015-error-safe-for-casting-timestamp-to-other-types-per-pg_ca.patch (3.3K, 3-v6-0015-error-safe-for-casting-timestamp-to-other-types-per-pg_ca.patch)
  download | inline diff:
From b1cd2195beefa7a4995dcb2ec8a390ff99ca7c4d Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Sun, 10 Aug 2025 01:33:36 +0800
Subject: [PATCH v6 15/18] error safe for casting timestamp to other types per
 pg_cast

select castsource::regtype, casttarget::regtype, castfunc, castcontext,castmethod, pp.prosrc, pp.proname
from pg_cast pc join pg_proc pp on pp.oid = pc.castfunc
and pc.castfunc > 0 and (castsource::regtype ='timestamp'::regtype)
order by castsource::regtype;

         castsource          |         casttarget          | castfunc | castcontext | castmethod |        prosrc         |   proname
-----------------------------+-----------------------------+----------+-------------+------------+-----------------------+-------------
 timestamp without time zone | date                        |     2029 | a           | f          | timestamp_date        | date
 timestamp without time zone | time without time zone      |     1316 | a           | f          | timestamp_time        | time
 timestamp without time zone | timestamp with time zone    |     2028 | i           | f          | timestamp_timestamptz | timestamptz
 timestamp without time zone | timestamp without time zone |     1961 | i           | f          | timestamp_scale       | timestamp
(4 rows)
---
 src/backend/utils/adt/date.c      | 13 +++++++++++--
 src/backend/utils/adt/timestamp.c | 17 +++++++++++++++--
 2 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 111bfd8f519..c5562b563e5 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -1373,7 +1373,16 @@ timestamp_date(PG_FUNCTION_ARGS)
 	Timestamp	timestamp = PG_GETARG_TIMESTAMP(0);
 	DateADT		result;
 
-	result = timestamp2date_opt_overflow(timestamp, NULL);
+	if (likely(!fcinfo->context))
+		result = timestamptz2date_opt_overflow(timestamp, NULL);
+	else
+	{
+		int			overflow;
+		result = timestamp2date_opt_overflow(timestamp, &overflow);
+
+		if (overflow != 0)
+			PG_RETURN_NULL();
+	}
 	PG_RETURN_DATEADT(result);
 }
 
@@ -2089,7 +2098,7 @@ timestamp_time(PG_FUNCTION_ARGS)
 		PG_RETURN_NULL();
 
 	if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
-		ereport(ERROR,
+		ereturn(fcinfo->context, (Datum) 0,
 				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 				 errmsg("timestamp out of range")));
 
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 8648a16db7f..b40dfd224d2 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -352,7 +352,8 @@ timestamp_scale(PG_FUNCTION_ARGS)
 
 	result = timestamp;
 
-	AdjustTimestampForTypmod(&result, typmod, NULL);
+	if (!AdjustTimestampForTypmod(&result, typmod, fcinfo->context))
+		PG_RETURN_NULL();
 
 	PG_RETURN_TIMESTAMP(result);
 }
@@ -6451,7 +6452,19 @@ timestamp_timestamptz(PG_FUNCTION_ARGS)
 {
 	Timestamp	timestamp = PG_GETARG_TIMESTAMP(0);
 
-	PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(timestamp));
+	if (likely(!fcinfo->context))
+		PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(timestamp));
+	else
+	{
+		TimestampTz result;
+		int			overflow;
+
+		result = timestamp2timestamptz_opt_overflow(timestamp, &overflow);
+		if (overflow != 0)
+			PG_RETURN_NULL();
+		else
+			PG_RETURN_TIMESTAMPTZ(result);
+	}
 }
 
 /*
-- 
2.34.1



  [text/x-patch] v6-0014-error-safe-for-casting-timestamptz-to-other-types-per-pg_.patch (3.7K, 4-v6-0014-error-safe-for-casting-timestamptz-to-other-types-per-pg_.patch)
  download | inline diff:
From a6e538d1ffdd0530879b7ad681180944c7390eea Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Sun, 10 Aug 2025 01:31:09 +0800
Subject: [PATCH v6 14/18] error safe for casting timestamptz to other types
 per pg_cast

select castsource::regtype, casttarget::regtype, castfunc, castcontext,castmethod, pp.prosrc, pp.proname
from pg_cast pc join pg_proc pp on pp.oid = pc.castfunc
and pc.castfunc > 0 and (castsource::regtype ='timestamptz'::regtype)
order by castsource::regtype;

        castsource        |         casttarget          | castfunc | castcontext | castmethod |        prosrc         |   proname
--------------------------+-----------------------------+----------+-------------+------------+-----------------------+-------------
 timestamp with time zone | date                        |     1178 | a           | f          | timestamptz_date      | date
 timestamp with time zone | time without time zone      |     2019 | a           | f          | timestamptz_time      | time
 timestamp with time zone | timestamp without time zone |     2027 | a           | f          | timestamptz_timestamp | timestamp
 timestamp with time zone | time with time zone         |     1388 | a           | f          | timestamptz_timetz    | timetz
 timestamp with time zone | timestamp with time zone    |     1967 | i           | f          | timestamptz_scale     | timestamptz
(5 rows)
---
 src/backend/utils/adt/date.c      | 16 +++++++++++++---
 src/backend/utils/adt/timestamp.c | 17 +++++++++++++++--
 2 files changed, 28 insertions(+), 5 deletions(-)

diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 4f0f3d26989..111bfd8f519 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -1468,7 +1468,17 @@ timestamptz_date(PG_FUNCTION_ARGS)
 	TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
 	DateADT		result;
 
-	result = timestamptz2date_opt_overflow(timestamp, NULL);
+	if (likely(!fcinfo->context))
+		result = timestamptz2date_opt_overflow(timestamp, NULL);
+	else
+	{
+		int			overflow;
+		result = timestamptz2date_opt_overflow(timestamp, &overflow);
+
+		if (overflow != 0)
+			PG_RETURN_NULL();
+	}
+
 	PG_RETURN_DATEADT(result);
 }
 
@@ -2110,7 +2120,7 @@ timestamptz_time(PG_FUNCTION_ARGS)
 		PG_RETURN_NULL();
 
 	if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
-		ereport(ERROR,
+		ereturn(fcinfo->context, (Datum) 0,
 				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 				 errmsg("timestamp out of range")));
 
@@ -3029,7 +3039,7 @@ timestamptz_timetz(PG_FUNCTION_ARGS)
 		PG_RETURN_NULL();
 
 	if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
-		ereport(ERROR,
+		ereturn(fcinfo->context, (Datum) 0,
 				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 				 errmsg("timestamp out of range")));
 
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index f1e96b84a6f..8648a16db7f 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -875,7 +875,8 @@ timestamptz_scale(PG_FUNCTION_ARGS)
 
 	result = timestamp;
 
-	AdjustTimestampForTypmod(&result, typmod, NULL);
+	if (!AdjustTimestampForTypmod(&result, typmod, fcinfo->context))
+		PG_RETURN_NULL();
 
 	PG_RETURN_TIMESTAMPTZ(result);
 }
@@ -6528,7 +6529,19 @@ timestamptz_timestamp(PG_FUNCTION_ARGS)
 {
 	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
 
-	PG_RETURN_TIMESTAMP(timestamptz2timestamp(timestamp));
+	if (likely(!fcinfo->context))
+		PG_RETURN_TIMESTAMP(timestamptz2timestamp(timestamp));
+	else
+	{
+		int			overflow;
+		Timestamp	result;
+
+		result = timestamptz2timestamp_opt_overflow(timestamp, &overflow);
+		if (overflow != 0)
+			PG_RETURN_NULL();
+		else
+			PG_RETURN_TIMESTAMP(result);
+	}
 }
 
 /*
-- 
2.34.1



  [text/x-patch] v6-0017-make-ArrayCoerceExpr-error-safe.patch (2.1K, 5-v6-0017-make-ArrayCoerceExpr-error-safe.patch)
  download | inline diff:
From 2d6daf36a294f5ff64b30b453bb319e09114c1d3 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Sun, 10 Aug 2025 01:41:14 +0800
Subject: [PATCH v6 17/18] make ArrayCoerceExpr error safe

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

diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index f1569879b52..921ec4e0bc1 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -1701,6 +1701,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 				elemstate->innermost_caseval = (Datum *) palloc(sizeof(Datum));
 				elemstate->innermost_casenull = (bool *) palloc(sizeof(bool));
+				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 67f4e00eac4..998674c180d 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -3644,6 +3644,13 @@ 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 c8f53c6fbe7..b5f98bf22f9 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



  [text/x-patch] v6-0018-CAST-expr-AS-newtype-DEFAULT-ON-ERROR.patch (131.8K, 6-v6-0018-CAST-expr-AS-newtype-DEFAULT-ON-ERROR.patch)
  download | inline diff:
From 51bef18b4cc1025848ef7600737449b86015509b Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Mon, 11 Aug 2025 14:11:42 +0800
Subject: [PATCH v6 18/18] CAST(expr AS newtype DEFAULT ON ERROR)

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.
draft doc also added.

We cannot simply prohibit user-defined functions in pg_cast for safe cast
evaluation because CREATE CAST can also utilize built-in functions. So, to
completely disallow custom casts created via CREATE CAST used in safe cast
evaluation, a new field in pg_cast would unfortunately be necessary.

[0]: https://git.postgresql.org/cgit/postgresql.git/commit/?id=aaaf9449ec6be62cb0d30ed3588dc384f56274bf
discussion: https://postgr.es/m/CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@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}
---
 doc/src/sgml/catalogs.sgml                |  11 +
 doc/src/sgml/syntax.sgml                  |  15 +
 src/backend/catalog/pg_cast.c             |   1 +
 src/backend/executor/execExpr.c           |  83 +++-
 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           | 345 +++++++++++++-
 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/catalog/pg_cast.dat           | 302 ++++++------
 src/include/catalog/pg_cast.h             |   4 +
 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        | 556 ++++++++++++++++++++++
 src/test/regress/expected/create_cast.out |   5 +
 src/test/regress/expected/opr_sanity.out  |  24 +-
 src/test/regress/parallel_schedule        |   2 +-
 src/test/regress/sql/cast.sql             | 259 ++++++++++
 src/test/regress/sql/create_cast.sql      |   1 +
 src/tools/pgindent/typedefs.list          |   3 +
 31 files changed, 1793 insertions(+), 173 deletions(-)
 create mode 100644 src/test/regress/expected/cast.out
 create mode 100644 src/test/regress/sql/cast.sql

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index da8a7882580..6398a6a0339 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1846,6 +1846,17 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
        <literal>b</literal> means that the types are binary-coercible, thus no conversion is required.
       </para></entry>
      </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>casterrorsafe</structfield> <type>bool</type>
+      </para>
+      <para>
+       This indicates whether the <structfield>castfunc</structfield> function is error-safe.
+       If true, the <structfield>castfunc</structfield> function is error-safe; if false, it is not.
+      </para></entry>
+     </row>
+
     </tbody>
    </tgroup>
   </table>
diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml
index 916189a7d68..4854b78966e 100644
--- a/doc/src/sgml/syntax.sgml
+++ b/doc/src/sgml/syntax.sgml
@@ -2104,6 +2104,21 @@ CAST ( <replaceable>expression</replaceable> AS <replaceable>type</replaceable>
     The <literal>CAST</literal> syntax conforms to SQL; the syntax with
     <literal>::</literal> is historical <productname>PostgreSQL</productname>
     usage.
+    The alternative syntax is
+<synopsis>
+CAST ( <replaceable>expression</replaceable> AS <replaceable>type</replaceable> ERROR ON CONVERSION ERROR )
+</synopsis>
+   </para>
+
+   <para>
+<synopsis>
+CAST ( <replaceable>expression</replaceable> AS <replaceable>type</replaceable> DEFAULT <replaceable>expression</replaceable> ON CONVERSION ERROR )
+</synopsis>
+    For example, the following query will evaluate the default expression and return 42.
+    TODO: more explanation
+<programlisting>
+SELECT (CAST('error' AS integer DEFAULT 42 ON CONVERSION ERROR));
+</programlisting>
    </para>
 
    <para>
diff --git a/src/backend/catalog/pg_cast.c b/src/backend/catalog/pg_cast.c
index 1773c9c5491..6fe65d24d31 100644
--- a/src/backend/catalog/pg_cast.c
+++ b/src/backend/catalog/pg_cast.c
@@ -84,6 +84,7 @@ CastCreate(Oid sourcetypeid, Oid targettypeid,
 	values[Anum_pg_cast_castfunc - 1] = ObjectIdGetDatum(funcid);
 	values[Anum_pg_cast_castcontext - 1] = CharGetDatum(castcontext);
 	values[Anum_pg_cast_castmethod - 1] = CharGetDatum(castmethod);
+	values[Anum_pg_cast_casterrorsafe - 1] = BoolGetDatum(false);
 
 	tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
 
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 921ec4e0bc1..eaed2d2be67 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,
@@ -2178,6 +2181,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;
@@ -2744,7 +2755,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;
@@ -4742,6 +4753,76 @@ 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
+	{
+		SafeTypeCastState *stcstate;
+		ErrorSaveContext *escontext;
+		ErrorSaveContext *saved_escontext;
+		List	   *jumps_to_end = NIL;
+
+		stcstate = palloc0(sizeof(SafeTypeCastState));
+		stcstate->stcexpr = stcexpr;
+		stcstate->escontext.type = T_ErrorSaveContext;
+		escontext = &stcstate->escontext;
+		state->escontext = escontext;
+
+		/* evaluate argument expression into step's result area */
+		ExecInitExprRec((Expr *) stcexpr->cast_expr,
+						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 998674c180d..351923d7ec4 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 */
@@ -5190,6 +5196,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 890bcb0b0a7..a2dfb823932 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 7bc823507f1..212f3e3c50c 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 31f97151977..76426c88e9a 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 6f0b338d2cd..c04ca88b0fb 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -2942,6 +2942,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 db43034b9db..20293d75524 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
@@ -15931,8 +15933,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"),
@@ -16318,6 +16337,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 d66276801c6..f837699d4b7 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -16,6 +16,8 @@
 #include "postgres.h"
 
 #include "catalog/pg_aggregate.h"
+#include "catalog/pg_cast.h"
+#include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "commands/dbcommands.h"
 #include "miscadmin.h"
@@ -37,6 +39,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 +63,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 +82,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 +113,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 +172,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 +2017,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 +2168,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 +2197,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 +2267,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 +2881,8 @@ transformTypeCast(ParseState *pstate, TypeCast *tc)
 									  (A_ArrayExpr *) arg,
 									  targetBaseType,
 									  elementType,
-									  targetBaseTypmod);
+									  targetBaseTypmod,
+									  NULL);
 		}
 		else
 			expr = transformExprRecurse(pstate, arg);
@@ -2779,6 +2919,199 @@ 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;
+	TypeCast   *tcast = (TypeCast *) tc->cast;
+	Node	   *tc_arg = tcast->arg;
+	Node	   *def_expr;
+	Node	   *cast_expr = NULL;
+	Node	   *source_expr = NULL;
+	Node	   *array_expr = NULL;
+	Oid			inputType = InvalidOid;
+	Oid			targetType;
+	Oid			targetBaseType;
+	int32		targetTypmod;
+	int32		targetBaseTypmod;
+	bool		can_coerce = true;
+	int			def_expr_loc = -1;
+	int			location;
+
+	result = makeNode(SafeTypeCastExpr);
+
+	/* Look up the type name first */
+	typenameTypeIdAndMod(pstate, tcast->typeName, &targetType, &targetTypmod);
+	targetBaseTypmod = targetTypmod;
+	targetBaseType = getBaseTypeAndTypmod(targetType, &targetBaseTypmod);
+
+	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 DEFAULT clause for CAST ... ON CONVERSION ERROR 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			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.
+		 */
+		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)
+			{
+				Assert(source_expr == NULL);
+				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)
+	{
+		Node	   *origexpr;
+		cast_expr = coerce_to_target_type(pstate, source_expr, inputType,
+										  targetType, targetTypmod,
+										  COERCION_EXPLICIT,
+										  COERCE_EXPLICIT_CAST,
+										  location);
+		origexpr = cast_expr;
+		while (cast_expr && IsA(cast_expr, CollateExpr))
+			cast_expr = (Node *) ((CollateExpr *) cast_expr)->arg;
+
+		if (cast_expr && IsA(cast_expr, FuncExpr))
+		{
+			HeapTuple	tuple;
+			ListCell   *lc;
+			Node	   *sexpr;
+			FuncExpr   *fexpr = (FuncExpr *) cast_expr;
+
+			lc = list_head(fexpr->args);
+			sexpr = (Node *) lfirst(lc);
+
+			/* Look in pg_cast */
+			tuple = SearchSysCache2(CASTSOURCETARGET,
+									ObjectIdGetDatum(exprType(sexpr)),
+									ObjectIdGetDatum(targetType));
+
+			if (HeapTupleIsValid(tuple))
+			{
+				Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple);
+				if (!castForm->casterrorsafe)
+					ereport(ERROR,
+							errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+							errmsg("cannot cast type %s to %s when %s clause is specified for %s",
+								   format_type_be(inputType),
+								   format_type_be(targetType),
+								   "DEFAULT",
+								   "CAST ... ON CONVERSION ERROR"),
+							errhint("Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast"),
+							parser_errposition(pstate, exprLocation(source_expr)));
+			}
+			else
+				elog(ERROR, "cache lookup failed for pg_cast entry (%s cast to %s)",
+							format_type_be(inputType),
+							format_type_be(targetType));
+			ReleaseSysCache(tuple);
+		}
+		cast_expr = origexpr;
+	}
+
+	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 4aba0d9d4d5..812ed18c162 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 7713bdc6af0..02e5f9c92d7 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 b5f98bf22f9..6bd8a989dbd 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 3d6e6bdbfd2..ee868de2c64 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 782291d9998..9de895e682f 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/catalog/pg_cast.dat b/src/include/catalog/pg_cast.dat
index fbfd669587f..3adef6b4faf 100644
--- a/src/include/catalog/pg_cast.dat
+++ b/src/include/catalog/pg_cast.dat
@@ -19,65 +19,65 @@
 # int2->int4->int8->numeric->float4->float8, while casts in the
 # reverse direction are assignment-only.
 { castsource => 'int8', casttarget => 'int2', castfunc => 'int2(int8)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int8', casttarget => 'int4', castfunc => 'int4(int8)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int8', casttarget => 'float4', castfunc => 'float4(int8)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int8', casttarget => 'float8', castfunc => 'float8(int8)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int8', casttarget => 'numeric', castfunc => 'numeric(int8)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int2', casttarget => 'int8', castfunc => 'int8(int2)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int2', casttarget => 'int4', castfunc => 'int4(int2)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int2', casttarget => 'float4', castfunc => 'float4(int2)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int2', casttarget => 'float8', castfunc => 'float8(int2)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int2', casttarget => 'numeric', castfunc => 'numeric(int2)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int4', casttarget => 'int8', castfunc => 'int8(int4)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int4', casttarget => 'int2', castfunc => 'int2(int4)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int4', casttarget => 'float4', castfunc => 'float4(int4)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int4', casttarget => 'float8', castfunc => 'float8(int4)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int4', casttarget => 'numeric', castfunc => 'numeric(int4)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'float4', casttarget => 'int8', castfunc => 'int8(float4)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'float4', casttarget => 'int2', castfunc => 'int2(float4)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'float4', casttarget => 'int4', castfunc => 'int4(float4)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'float4', casttarget => 'float8', castfunc => 'float8(float4)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'float4', casttarget => 'numeric',
-  castfunc => 'numeric(float4)', castcontext => 'a', castmethod => 'f' },
+  castfunc => 'numeric(float4)', castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'float8', casttarget => 'int8', castfunc => 'int8(float8)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'float8', casttarget => 'int2', castfunc => 'int2(float8)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'float8', casttarget => 'int4', castfunc => 'int4(float8)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'float8', casttarget => 'float4', castfunc => 'float4(float8)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'float8', casttarget => 'numeric',
-  castfunc => 'numeric(float8)', castcontext => 'a', castmethod => 'f' },
+  castfunc => 'numeric(float8)', castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'numeric', casttarget => 'int8', castfunc => 'int8(numeric)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'numeric', casttarget => 'int2', castfunc => 'int2(numeric)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'numeric', casttarget => 'int4', castfunc => 'int4(numeric)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'numeric', casttarget => 'float4',
-  castfunc => 'float4(numeric)', castcontext => 'i', castmethod => 'f' },
+  castfunc => 'float4(numeric)', castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'numeric', casttarget => 'float8',
-  castfunc => 'float8(numeric)', castcontext => 'i', castmethod => 'f' },
+  castfunc => 'float8(numeric)', castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'money', casttarget => 'numeric', castfunc => 'numeric(money)',
   castcontext => 'a', castmethod => 'f' },
 { castsource => 'numeric', casttarget => 'money', castfunc => 'money(numeric)',
@@ -89,13 +89,13 @@
 
 # Allow explicit coercions between int4 and bool
 { castsource => 'int4', casttarget => 'bool', castfunc => 'bool(int4)',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'bool', casttarget => 'int4', castfunc => 'int4(bool)',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 
 # Allow explicit coercions between xid8 and xid
 { castsource => 'xid8', casttarget => 'xid', castfunc => 'xid(xid8)',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 
 # OID category: allow implicit conversion from any integral type (including
 # int8, to support OID literals > 2G) to OID, as well as assignment coercion
@@ -106,13 +106,13 @@
 # casts from text and varchar to regclass, which exist mainly to support
 # legacy forms of nextval() and related functions.
 { castsource => 'int8', casttarget => 'oid', castfunc => 'oid',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int2', casttarget => 'oid', castfunc => 'int4(int2)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int4', casttarget => 'oid', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'oid', casttarget => 'int8', castfunc => 'int8(oid)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'oid', casttarget => 'int4', castfunc => '0',
   castcontext => 'a', castmethod => 'b' },
 { castsource => 'oid', casttarget => 'regproc', castfunc => '0',
@@ -120,13 +120,13 @@
 { castsource => 'regproc', casttarget => 'oid', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'int8', casttarget => 'regproc', castfunc => 'oid',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int2', casttarget => 'regproc', castfunc => 'int4(int2)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int4', casttarget => 'regproc', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'regproc', casttarget => 'int8', castfunc => 'int8(oid)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'regproc', casttarget => 'int4', castfunc => '0',
   castcontext => 'a', castmethod => 'b' },
 { castsource => 'regproc', casttarget => 'regprocedure', castfunc => '0',
@@ -138,13 +138,13 @@
 { castsource => 'regprocedure', casttarget => 'oid', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'int8', casttarget => 'regprocedure', castfunc => 'oid',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int2', casttarget => 'regprocedure', castfunc => 'int4(int2)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int4', casttarget => 'regprocedure', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'regprocedure', casttarget => 'int8', castfunc => 'int8(oid)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'regprocedure', casttarget => 'int4', castfunc => '0',
   castcontext => 'a', castmethod => 'b' },
 { castsource => 'oid', casttarget => 'regoper', castfunc => '0',
@@ -152,13 +152,13 @@
 { castsource => 'regoper', casttarget => 'oid', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'int8', casttarget => 'regoper', castfunc => 'oid',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int2', casttarget => 'regoper', castfunc => 'int4(int2)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int4', casttarget => 'regoper', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'regoper', casttarget => 'int8', castfunc => 'int8(oid)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'regoper', casttarget => 'int4', castfunc => '0',
   castcontext => 'a', castmethod => 'b' },
 { castsource => 'regoper', casttarget => 'regoperator', castfunc => '0',
@@ -170,13 +170,13 @@
 { castsource => 'regoperator', casttarget => 'oid', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'int8', casttarget => 'regoperator', castfunc => 'oid',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int2', casttarget => 'regoperator', castfunc => 'int4(int2)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int4', casttarget => 'regoperator', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'regoperator', casttarget => 'int8', castfunc => 'int8(oid)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'regoperator', casttarget => 'int4', castfunc => '0',
   castcontext => 'a', castmethod => 'b' },
 { castsource => 'oid', casttarget => 'regclass', castfunc => '0',
@@ -184,13 +184,13 @@
 { castsource => 'regclass', casttarget => 'oid', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'int8', casttarget => 'regclass', castfunc => 'oid',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int2', casttarget => 'regclass', castfunc => 'int4(int2)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int4', casttarget => 'regclass', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'regclass', casttarget => 'int8', castfunc => 'int8(oid)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'regclass', casttarget => 'int4', castfunc => '0',
   castcontext => 'a', castmethod => 'b' },
 { castsource => 'oid', casttarget => 'regcollation', castfunc => '0',
@@ -198,13 +198,13 @@
 { castsource => 'regcollation', casttarget => 'oid', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'int8', casttarget => 'regcollation', castfunc => 'oid',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int2', casttarget => 'regcollation', castfunc => 'int4(int2)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int4', casttarget => 'regcollation', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'regcollation', casttarget => 'int8', castfunc => 'int8(oid)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'regcollation', casttarget => 'int4', castfunc => '0',
   castcontext => 'a', castmethod => 'b' },
 { castsource => 'oid', casttarget => 'regtype', castfunc => '0',
@@ -212,13 +212,13 @@
 { castsource => 'regtype', casttarget => 'oid', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'int8', casttarget => 'regtype', castfunc => 'oid',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int2', casttarget => 'regtype', castfunc => 'int4(int2)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int4', casttarget => 'regtype', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'regtype', casttarget => 'int8', castfunc => 'int8(oid)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'regtype', casttarget => 'int4', castfunc => '0',
   castcontext => 'a', castmethod => 'b' },
 { castsource => 'oid', casttarget => 'regconfig', castfunc => '0',
@@ -226,13 +226,13 @@
 { castsource => 'regconfig', casttarget => 'oid', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'int8', casttarget => 'regconfig', castfunc => 'oid',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int2', casttarget => 'regconfig', castfunc => 'int4(int2)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int4', casttarget => 'regconfig', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'regconfig', casttarget => 'int8', castfunc => 'int8(oid)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'regconfig', casttarget => 'int4', castfunc => '0',
   castcontext => 'a', castmethod => 'b' },
 { castsource => 'oid', casttarget => 'regdictionary', castfunc => '0',
@@ -240,31 +240,31 @@
 { castsource => 'regdictionary', casttarget => 'oid', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'int8', casttarget => 'regdictionary', castfunc => 'oid',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int2', casttarget => 'regdictionary', castfunc => 'int4(int2)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int4', casttarget => 'regdictionary', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'regdictionary', casttarget => 'int8', castfunc => 'int8(oid)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'regdictionary', casttarget => 'int4', castfunc => '0',
   castcontext => 'a', castmethod => 'b' },
 { castsource => 'text', casttarget => 'regclass', castfunc => 'regclass',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'varchar', casttarget => 'regclass', castfunc => 'regclass',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'oid', casttarget => 'regrole', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'regrole', casttarget => 'oid', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'int8', casttarget => 'regrole', castfunc => 'oid',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int2', casttarget => 'regrole', castfunc => 'int4(int2)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int4', casttarget => 'regrole', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'regrole', casttarget => 'int8', castfunc => 'int8(oid)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'regrole', casttarget => 'int4', castfunc => '0',
   castcontext => 'a', castmethod => 'b' },
 { castsource => 'oid', casttarget => 'regnamespace', castfunc => '0',
@@ -272,13 +272,13 @@
 { castsource => 'regnamespace', casttarget => 'oid', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'int8', casttarget => 'regnamespace', castfunc => 'oid',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int2', casttarget => 'regnamespace', castfunc => 'int4(int2)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int4', casttarget => 'regnamespace', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'regnamespace', casttarget => 'int8', castfunc => 'int8(oid)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'regnamespace', casttarget => 'int4', castfunc => '0',
   castcontext => 'a', castmethod => 'b' },
 { castsource => 'oid', casttarget => 'regdatabase', castfunc => '0',
@@ -286,13 +286,13 @@
 { castsource => 'regdatabase', casttarget => 'oid', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'int8', casttarget => 'regdatabase', castfunc => 'oid',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int2', casttarget => 'regdatabase', castfunc => 'int4(int2)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int4', casttarget => 'regdatabase', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'regdatabase', casttarget => 'int8', castfunc => 'int8(oid)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'regdatabase', casttarget => 'int4', castfunc => '0',
   castcontext => 'a', castmethod => 'b' },
 
@@ -302,57 +302,57 @@
 { castsource => 'text', casttarget => 'varchar', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'bpchar', casttarget => 'text', castfunc => 'text(bpchar)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'bpchar', casttarget => 'varchar', castfunc => 'text(bpchar)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'varchar', casttarget => 'text', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'varchar', casttarget => 'bpchar', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'char', casttarget => 'text', castfunc => 'text(char)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'char', casttarget => 'bpchar', castfunc => 'bpchar(char)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'char', casttarget => 'varchar', castfunc => 'text(char)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'name', casttarget => 'text', castfunc => 'text(name)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'name', casttarget => 'bpchar', castfunc => 'bpchar(name)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'name', casttarget => 'varchar', castfunc => 'varchar(name)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'text', casttarget => 'char', castfunc => 'char(text)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'bpchar', casttarget => 'char', castfunc => 'char(text)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'varchar', casttarget => 'char', castfunc => 'char(text)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'text', casttarget => 'name', castfunc => 'name(text)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'bpchar', casttarget => 'name', castfunc => 'name(bpchar)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'varchar', casttarget => 'name', castfunc => 'name(varchar)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 
 # Allow explicit coercions between bytea and integer types
 { castsource => 'int2', casttarget => 'bytea', castfunc => 'bytea(int2)',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int4', casttarget => 'bytea', castfunc => 'bytea(int4)',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int8', casttarget => 'bytea', castfunc => 'bytea(int8)',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'bytea', casttarget => 'int2', castfunc => 'int2(bytea)',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'bytea', casttarget => 'int4', castfunc => 'int4(bytea)',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'bytea', casttarget => 'int8', castfunc => 'int8(bytea)',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 
 # Allow explicit coercions between int4 and "char"
 { castsource => 'char', casttarget => 'int4', castfunc => 'int4(char)',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int4', casttarget => 'char', castfunc => 'char(int4)',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 
 # pg_node_tree can be coerced to, but not from, text
 { castsource => 'pg_node_tree', casttarget => 'text', castfunc => '0',
@@ -378,31 +378,31 @@
 
 # Datetime category
 { castsource => 'date', casttarget => 'timestamp',
-  castfunc => 'timestamp(date)', castcontext => 'i', castmethod => 'f' },
+  castfunc => 'timestamp(date)', castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'date', casttarget => 'timestamptz',
-  castfunc => 'timestamptz(date)', castcontext => 'i', castmethod => 'f' },
+  castfunc => 'timestamptz(date)', castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'time', casttarget => 'interval', castfunc => 'interval(time)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'time', casttarget => 'timetz', castfunc => 'timetz(time)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'timestamp', casttarget => 'date',
-  castfunc => 'date(timestamp)', castcontext => 'a', castmethod => 'f' },
+  castfunc => 'date(timestamp)', castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'timestamp', casttarget => 'time',
-  castfunc => 'time(timestamp)', castcontext => 'a', castmethod => 'f' },
+  castfunc => 'time(timestamp)', castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'timestamp', casttarget => 'timestamptz',
-  castfunc => 'timestamptz(timestamp)', castcontext => 'i', castmethod => 'f' },
+  castfunc => 'timestamptz(timestamp)', castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'timestamptz', casttarget => 'date',
-  castfunc => 'date(timestamptz)', castcontext => 'a', castmethod => 'f' },
+  castfunc => 'date(timestamptz)', castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'timestamptz', casttarget => 'time',
-  castfunc => 'time(timestamptz)', castcontext => 'a', castmethod => 'f' },
+  castfunc => 'time(timestamptz)', castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'timestamptz', casttarget => 'timestamp',
-  castfunc => 'timestamp(timestamptz)', castcontext => 'a', castmethod => 'f' },
+  castfunc => 'timestamp(timestamptz)', castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'timestamptz', casttarget => 'timetz',
-  castfunc => 'timetz(timestamptz)', castcontext => 'a', castmethod => 'f' },
+  castfunc => 'timetz(timestamptz)', castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'interval', casttarget => 'time', castfunc => 'time(interval)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'timetz', casttarget => 'time', castfunc => 'time(timetz)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 
 # Geometric category
 { castsource => 'point', casttarget => 'box', castfunc => 'box(point)',
@@ -436,15 +436,15 @@
 
 # MAC address category
 { castsource => 'macaddr', casttarget => 'macaddr8', castfunc => 'macaddr8',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'macaddr8', casttarget => 'macaddr', castfunc => 'macaddr',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 
 # INET category
 { castsource => 'cidr', casttarget => 'inet', castfunc => '0',
   castcontext => 'i', castmethod => 'b' },
 { castsource => 'inet', casttarget => 'cidr', castfunc => 'cidr',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 
 # BitString category
 { castsource => 'bit', casttarget => 'varbit', castfunc => '0',
@@ -454,13 +454,13 @@
 
 # Cross-category casts between bit and int4, int8
 { castsource => 'int8', casttarget => 'bit', castfunc => 'bit(int8,int4)',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int4', casttarget => 'bit', castfunc => 'bit(int4,int4)',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'bit', casttarget => 'int8', castfunc => 'int8(bit)',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'bit', casttarget => 'int4', castfunc => 'int4(bit)',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 
 # Cross-category casts to and from TEXT
 # We need entries here only for a few specialized cases where the behavior
@@ -471,68 +471,68 @@
 # behavior will ensue when the automatic cast is applied instead of the
 # pg_cast entry!
 { castsource => 'cidr', casttarget => 'text', castfunc => 'text(inet)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'inet', casttarget => 'text', castfunc => 'text(inet)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'bool', casttarget => 'text', castfunc => 'text(bool)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'xml', casttarget => 'text', castfunc => '0',
   castcontext => 'a', castmethod => 'b' },
 { castsource => 'text', casttarget => 'xml', castfunc => 'xml',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 
 # Cross-category casts to and from VARCHAR
 # We support all the same casts as for TEXT.
 { castsource => 'cidr', casttarget => 'varchar', castfunc => 'text(inet)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'inet', casttarget => 'varchar', castfunc => 'text(inet)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'bool', casttarget => 'varchar', castfunc => 'text(bool)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'xml', casttarget => 'varchar', castfunc => '0',
   castcontext => 'a', castmethod => 'b' },
 { castsource => 'varchar', casttarget => 'xml', castfunc => 'xml',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 
 # Cross-category casts to and from BPCHAR
 # We support all the same casts as for TEXT.
 { castsource => 'cidr', casttarget => 'bpchar', castfunc => 'text(inet)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'inet', casttarget => 'bpchar', castfunc => 'text(inet)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'bool', casttarget => 'bpchar', castfunc => 'text(bool)',
-  castcontext => 'a', castmethod => 'f' },
+  castcontext => 'a', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'xml', casttarget => 'bpchar', castfunc => '0',
   castcontext => 'a', castmethod => 'b' },
 { castsource => 'bpchar', casttarget => 'xml', castfunc => 'xml',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 
 # Length-coercion functions
 { castsource => 'bpchar', casttarget => 'bpchar',
   castfunc => 'bpchar(bpchar,int4,bool)', castcontext => 'i',
-  castmethod => 'f' },
+  castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'varchar', casttarget => 'varchar',
   castfunc => 'varchar(varchar,int4,bool)', castcontext => 'i',
-  castmethod => 'f' },
+  castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'time', casttarget => 'time', castfunc => 'time(time,int4)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'timestamp', casttarget => 'timestamp',
   castfunc => 'timestamp(timestamp,int4)', castcontext => 'i',
-  castmethod => 'f' },
+  castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'timestamptz', casttarget => 'timestamptz',
   castfunc => 'timestamptz(timestamptz,int4)', castcontext => 'i',
-  castmethod => 'f' },
+  castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'interval', casttarget => 'interval',
   castfunc => 'interval(interval,int4)', castcontext => 'i',
-  castmethod => 'f' },
+  castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'timetz', casttarget => 'timetz',
-  castfunc => 'timetz(timetz,int4)', castcontext => 'i', castmethod => 'f' },
+  castfunc => 'timetz(timetz,int4)', castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'bit', casttarget => 'bit', castfunc => 'bit(bit,int4,bool)',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'varbit', casttarget => 'varbit', castfunc => 'varbit',
-  castcontext => 'i', castmethod => 'f' },
+  castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'numeric', casttarget => 'numeric',
-  castfunc => 'numeric(numeric,int4)', castcontext => 'i', castmethod => 'f' },
+  castfunc => 'numeric(numeric,int4)', castcontext => 'i', castmethod => 'f',  casterrorsafe => 't' },
 
 # json to/from jsonb
 { castsource => 'json', casttarget => 'jsonb', castfunc => '0',
@@ -542,36 +542,36 @@
 
 # jsonb to numeric and bool types
 { castsource => 'jsonb', casttarget => 'bool', castfunc => 'bool(jsonb)',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'jsonb', casttarget => 'numeric', castfunc => 'numeric(jsonb)',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'jsonb', casttarget => 'int2', castfunc => 'int2(jsonb)',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'jsonb', casttarget => 'int4', castfunc => 'int4(jsonb)',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'jsonb', casttarget => 'int8', castfunc => 'int8(jsonb)',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'jsonb', casttarget => 'float4', castfunc => 'float4(jsonb)',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'jsonb', casttarget => 'float8', castfunc => 'float8(jsonb)',
-  castcontext => 'e', castmethod => 'f' },
+  castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 
 # range to multirange
 { castsource => 'int4range', casttarget => 'int4multirange',
   castfunc => 'int4multirange(int4range)', castcontext => 'e',
-  castmethod => 'f' },
+  castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'int8range', casttarget => 'int8multirange',
   castfunc => 'int8multirange(int8range)', castcontext => 'e',
-  castmethod => 'f' },
+  castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'numrange', casttarget => 'nummultirange',
   castfunc => 'nummultirange(numrange)', castcontext => 'e',
-  castmethod => 'f' },
+  castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'daterange', casttarget => 'datemultirange',
   castfunc => 'datemultirange(daterange)', castcontext => 'e',
-  castmethod => 'f' },
+  castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'tsrange', casttarget => 'tsmultirange',
-  castfunc => 'tsmultirange(tsrange)', castcontext => 'e', castmethod => 'f' },
+  castfunc => 'tsmultirange(tsrange)', castcontext => 'e', castmethod => 'f',  casterrorsafe => 't' },
 { castsource => 'tstzrange', casttarget => 'tstzmultirange',
   castfunc => 'tstzmultirange(tstzrange)', castcontext => 'e',
-  castmethod => 'f' },
+  castmethod => 'f',  casterrorsafe => 't' },
 ]
diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.h
index 6a0ca337153..218d81d535a 100644
--- a/src/include/catalog/pg_cast.h
+++ b/src/include/catalog/pg_cast.h
@@ -47,6 +47,10 @@ CATALOG(pg_cast,2605,CastRelationId)
 
 	/* cast method */
 	char		castmethod;
+
+	/* cast function error safe */
+	bool		casterrorsafe BKI_DEFAULT(f);
+
 } FormData_pg_cast;
 
 /* ----------------
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 75366203706..0afcf09c086 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 0fe7b4ebc77..299d4eef4ed 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 e107d6e5f81..282bfa770ef 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 86a236bd58b..95174e0feef 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 6dfca3cb35b..d2df7c28932 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 0d919d8bfa2..12381aed64c 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 00000000000..bf62e9348f1
--- /dev/null
+++ b/src/test/regress/expected/cast.out
@@ -0,0 +1,556 @@
+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 DEFAULT clause is specified for CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(1::numeric AS money DEFAULT NULL ON CONVERSION E...
+                    ^
+HINT:  Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
+SELECT CAST(1111 AS "char" DEFAULT NULL ON CONVERSION ERROR);
+ char 
+------
+ 
+(1 row)
+
+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 DEFAULT clause for CAST ... ON CONVERSION ERROR 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)'...
+                                                             ^
+-----safe cast from bytea type to other type
+SELECT CAST ('\x112233445566778899'::bytea AS int8 DEFAULT NULL ON CONVERSION ERROR);
+ int8 
+------
+     
+(1 row)
+
+SELECT CAST('\x123456789A'::bytea AS int4 DEFAULT NULL ON CONVERSION ERROR);
+ int4 
+------
+     
+(1 row)
+
+SELECT CAST('\x123456'::bytea AS int2 DEFAULT NULL ON CONVERSION ERROR);
+ int2 
+------
+     
+(1 row)
+
+-----safe cast from range type to other type
+SELECT CAST('[1,2]'::int4range AS int4multirange DEFAULT NULL ON CONVERSION ERROR);
+ int4multirange 
+----------------
+ {[1,3)}
+(1 row)
+
+SELECT CAST('[1,2]'::int8range AS int8multirange DEFAULT NULL ON CONVERSION ERROR);
+ int8multirange 
+----------------
+ {[1,3)}
+(1 row)
+
+SELECT CAST('[1,2]'::numrange  AS nummultirange DEFAULT NULL ON CONVERSION ERROR);
+ nummultirange 
+---------------
+ {[1,2]}
+(1 row)
+
+SELECT CAST('[-infinity,infinity]'::daterange AS datemultirange DEFAULT NULL ON CONVERSION ERROR);
+     datemultirange     
+------------------------
+ {[-infinity,infinity]}
+(1 row)
+
+SELECT CAST('[-infinity,infinity]'::tsrange AS tsmultirange DEFAULT NULL ON CONVERSION ERROR);
+      tsmultirange      
+------------------------
+ {[-infinity,infinity]}
+(1 row)
+
+SELECT CAST('[-infinity,infinity]'::tstzrange AS tstzmultirange DEFAULT NULL ON CONVERSION ERROR);
+     tstzmultirange     
+------------------------
+ {[-infinity,infinity]}
+(1 row)
+
+-----safe cast from geometry to other geometry is not supported
+SELECT CAST(NULL::point AS box DEFAULT NULL ON CONVERSION ERROR); --error
+ERROR:  cannot cast type point to box when DEFAULT clause is specified for CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(NULL::point AS box DEFAULT NULL ON CONVERSION ER...
+                    ^
+HINT:  Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
+SELECT CAST(NULL::lseg AS point DEFAULT NULL ON CONVERSION ERROR); --error
+ERROR:  cannot cast type lseg to point when DEFAULT clause is specified for CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(NULL::lseg AS point DEFAULT NULL ON CONVERSION E...
+                    ^
+HINT:  Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
+SELECT CAST(NULL::path AS polygon DEFAULT NULL ON CONVERSION ERROR); --error
+ERROR:  cannot cast type path to polygon when DEFAULT clause is specified for CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(NULL::path AS polygon DEFAULT NULL ON CONVERSION...
+                    ^
+HINT:  Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
+SELECT CAST(NULL::box AS point DEFAULT NULL ON CONVERSION ERROR); --error
+ERROR:  cannot cast type box to point when DEFAULT clause is specified for CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(NULL::box AS point DEFAULT NULL ON CONVERSION ER...
+                    ^
+HINT:  Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
+SELECT CAST(NULL::box AS lseg DEFAULT NULL ON CONVERSION ERROR); --error
+ERROR:  cannot cast type box to lseg when DEFAULT clause is specified for CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(NULL::box AS lseg DEFAULT NULL ON CONVERSION ERR...
+                    ^
+HINT:  Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
+SELECT CAST(NULL::box AS polygon DEFAULT NULL ON CONVERSION ERROR); --error
+ERROR:  cannot cast type box to polygon when DEFAULT clause is specified for CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(NULL::box AS polygon DEFAULT NULL ON CONVERSION ...
+                    ^
+HINT:  Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
+SELECT CAST(NULL::box AS circle DEFAULT NULL ON CONVERSION ERROR); --error
+ERROR:  cannot cast type box to circle when DEFAULT clause is specified for CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(NULL::box AS circle DEFAULT NULL ON CONVERSION E...
+                    ^
+HINT:  Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
+SELECT CAST(NULL::polygon AS point DEFAULT NULL ON CONVERSION ERROR); --error
+ERROR:  cannot cast type polygon to point when DEFAULT clause is specified for CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(NULL::polygon AS point DEFAULT NULL ON CONVERSIO...
+                    ^
+HINT:  Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
+SELECT CAST(NULL::polygon AS path DEFAULT NULL ON CONVERSION ERROR); --error
+ERROR:  cannot cast type polygon to path when DEFAULT clause is specified for CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(NULL::polygon AS path DEFAULT NULL ON CONVERSION...
+                    ^
+HINT:  Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
+SELECT CAST(NULL::polygon AS box DEFAULT NULL ON CONVERSION ERROR); --error
+ERROR:  cannot cast type polygon to box when DEFAULT clause is specified for CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(NULL::polygon AS box DEFAULT NULL ON CONVERSION ...
+                    ^
+HINT:  Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
+SELECT CAST(NULL::polygon AS circle DEFAULT NULL ON CONVERSION ERROR); --error
+ERROR:  cannot cast type polygon to circle when DEFAULT clause is specified for CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(NULL::polygon AS circle DEFAULT NULL ON CONVERSI...
+                    ^
+HINT:  Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
+SELECT CAST(NULL::circle AS point DEFAULT NULL ON CONVERSION ERROR); --error
+ERROR:  cannot cast type circle to point when DEFAULT clause is specified for CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(NULL::circle AS point DEFAULT NULL ON CONVERSION...
+                    ^
+HINT:  Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
+SELECT CAST(NULL::circle AS box DEFAULT NULL ON CONVERSION ERROR); --error
+ERROR:  cannot cast type circle to box when DEFAULT clause is specified for CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(NULL::circle AS box DEFAULT NULL ON CONVERSION E...
+                    ^
+HINT:  Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
+SELECT CAST(NULL::circle AS polygon DEFAULT NULL ON CONVERSION ERROR); --error
+ERROR:  cannot cast type circle to polygon when DEFAULT clause is specified for CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(NULL::circle AS polygon DEFAULT NULL ON CONVERSI...
+                    ^
+HINT:  Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
+-----safe cast from money or money cast to other type is not supported
+SELECT CAST(NULL::int8 AS money DEFAULT NULL ON CONVERSION ERROR); --error
+ERROR:  cannot cast type bigint to money when DEFAULT clause is specified for CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(NULL::int8 AS money DEFAULT NULL ON CONVERSION E...
+                    ^
+HINT:  Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
+SELECT CAST(NULL::int4 AS money DEFAULT NULL ON CONVERSION ERROR); --error
+ERROR:  cannot cast type integer to money when DEFAULT clause is specified for CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(NULL::int4 AS money DEFAULT NULL ON CONVERSION E...
+                    ^
+HINT:  Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
+SELECT CAST(NULL::numeric AS money DEFAULT NULL ON CONVERSION ERROR); --error
+ERROR:  cannot cast type numeric to money when DEFAULT clause is specified for CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(NULL::numeric AS money DEFAULT NULL ON CONVERSIO...
+                    ^
+HINT:  Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
+SELECT CAST(NULL::money AS numeric DEFAULT NULL ON CONVERSION ERROR); --error
+ERROR:  cannot cast type money to numeric when DEFAULT clause is specified for CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(NULL::money AS numeric DEFAULT NULL ON CONVERSIO...
+                    ^
+HINT:  Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
+--test cast numeric value with fraction to another numeric value
+CREATE TABLE safecast(col1 float4, col2 float8, col3 numeric, col4 numeric[],
+                        col5 int2 default 32767,
+                        col6 int4 default 32768,
+                        col7 int8 default 4294967296);
+INSERT INTO safecast VALUES('11.1234', '11.1234', '9223372036854775808', '{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 oid DEFAULT NULL ON CONVERSION ERROR) as to_oid,
+       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_oid | 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 oid DEFAULT NULL ON CONVERSION ERROR) as to_oid,
+       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_oid | 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 oid DEFAULT NULL ON CONVERSION ERROR) as to_oid,
+       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_oid | to_int8 |  to_float4  |      to_float8       |     to_numeric      | to_numeric_scale 
+---------------------+---------+---------+--------+---------+-------------+----------------------+---------------------+------------------
+ 9223372036854775808 |         |         |        |         | 9.22337e+18 | 9.22337203685478e+18 | 9223372036854775808 |                 
+            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)
+
+SELECT col5 as int2,
+       CAST(col5 AS int2 DEFAULT NULL ON CONVERSION ERROR) as to_int2,
+       CAST(col5 AS int4 DEFAULT NULL ON CONVERSION ERROR) as to_int4,
+       CAST(col5 AS oid DEFAULT NULL ON CONVERSION ERROR) as to_oid,
+       CAST(col5 as int8 DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+       CAST(col5 as float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+       CAST(col5 as float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+       CAST(col5 as numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+       CAST(col5 as numeric(10,1) DEFAULT NULL ON CONVERSION ERROR) as to_numeric_scale
+FROM safecast;
+ int2  | to_int2 | to_int4 | to_oid | to_int8 | to_float4 | to_float8 | to_numeric | to_numeric_scale 
+-------+---------+---------+--------+---------+-----------+-----------+------------+------------------
+ 32767 |   32767 |   32767 |  32767 |   32767 |     32767 |     32767 |      32767 |          32767.0
+ 32767 |   32767 |   32767 |  32767 |   32767 |     32767 |     32767 |      32767 |          32767.0
+ 32767 |   32767 |   32767 |  32767 |   32767 |     32767 |     32767 |      32767 |          32767.0
+ 32767 |   32767 |   32767 |  32767 |   32767 |     32767 |     32767 |      32767 |          32767.0
+(4 rows)
+
+SELECT col6 as int4,
+       CAST(col6 AS int2 DEFAULT NULL ON CONVERSION ERROR) as to_int2,
+       CAST(col6 AS int4 DEFAULT NULL ON CONVERSION ERROR) as to_int4,
+       CAST(col6 AS oid DEFAULT NULL ON CONVERSION ERROR) as to_oid,
+       CAST(col6 as int8 DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+       CAST(col6 as float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+       CAST(col6 as float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+       CAST(col6 as numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+       CAST(col6 as numeric(10,1) DEFAULT NULL ON CONVERSION ERROR) as to_numeric_scale
+FROM safecast;
+ int4  | to_int2 | to_int4 | to_oid | to_int8 | to_float4 | to_float8 | to_numeric | to_numeric_scale 
+-------+---------+---------+--------+---------+-----------+-----------+------------+------------------
+ 32768 |         |   32768 |  32768 |   32768 |     32768 |     32768 |      32768 |          32768.0
+ 32768 |         |   32768 |  32768 |   32768 |     32768 |     32768 |      32768 |          32768.0
+ 32768 |         |   32768 |  32768 |   32768 |     32768 |     32768 |      32768 |          32768.0
+ 32768 |         |   32768 |  32768 |   32768 |     32768 |     32768 |      32768 |          32768.0
+(4 rows)
+
+SELECT col7 as int8,
+       CAST(col7 AS int2 DEFAULT NULL ON CONVERSION ERROR) as to_int2,
+       CAST(col7 AS int4 DEFAULT NULL ON CONVERSION ERROR) as to_int4,
+       CAST(col7 AS oid DEFAULT NULL ON CONVERSION ERROR) as to_oid,
+       CAST(col7 as int8 DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+       CAST(col7 as float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+       CAST(col7 as float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+       CAST(col7 as numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+       CAST(col7 as numeric(10,1) DEFAULT NULL ON CONVERSION ERROR) as to_numeric_scale
+FROM safecast;
+    int8    | to_int2 | to_int4 | to_oid |  to_int8   |  to_float4  | to_float8  | to_numeric | to_numeric_scale 
+------------+---------+---------+--------+------------+-------------+------------+------------+------------------
+ 4294967296 |         |         |        | 4294967296 | 4.29497e+09 | 4294967296 | 4294967296 |                 
+ 4294967296 |         |         |        | 4294967296 | 4.29497e+09 | 4294967296 | 4294967296 |                 
+ 4294967296 |         |         |        | 4294967296 | 4.29497e+09 | 4294967296 | 4294967296 |                 
+ 4294967296 |         |         |        | 4294967296 | 4.29497e+09 | 4294967296 | 4294967296 |                 
+(4 rows)
+
+--test date/timestamp/interval realted cast
+CREATE TABLE safecast1(col0 date, col1 timestamp, col2 timestamptz, col3 interval, col4 time, col5 timetz);
+insert into safecast1 VALUES
+('-infinity', '-infinity', '-infinity', '-infinity', '2003-03-07 15:36:39 America/New_York', '2003-07-07 15:36:39 America/New_York'),
+('-infinity', 'infinity', 'infinity', 'infinity','11:59:59.99 PM', '11:59:59.99 PM PDT');
+SELECT col0 as date,
+       CAST(col0 AS timestamptz DEFAULT NULL ON CONVERSION ERROR) as to_timestamptz,
+       CAST(col0 AS date DEFAULT NULL ON CONVERSION ERROR) as to_date,
+       CAST(col0 AS time DEFAULT NULL ON CONVERSION ERROR) as to_times,
+       CAST(col0 AS timetz DEFAULT NULL ON CONVERSION ERROR) as to_timetz,
+       CAST(col0 AS timestamp(2) DEFAULT NULL ON CONVERSION ERROR) as to_timestamp_scale
+FROM safecast1;
+   date    | to_timestamptz |  to_date  | to_times | to_timetz | to_timestamp_scale 
+-----------+----------------+-----------+----------+-----------+--------------------
+ -infinity | -infinity      | -infinity |          |           | -infinity
+ -infinity | -infinity      | -infinity |          |           | -infinity
+(2 rows)
+
+SELECT col1 as timestamp,
+       CAST(col1 AS timestamptz DEFAULT NULL ON CONVERSION ERROR) as to_timestamptz,
+       CAST(col1 AS date DEFAULT NULL ON CONVERSION ERROR) as to_date,
+       CAST(col1 AS time DEFAULT NULL ON CONVERSION ERROR) as to_times,
+       CAST(col1 AS timetz DEFAULT NULL ON CONVERSION ERROR) as to_timetz,
+       CAST(col1 AS timestamp(2) DEFAULT NULL ON CONVERSION ERROR) as to_timestamp_scale
+FROM safecast1;
+ timestamp | to_timestamptz |  to_date  | to_times | to_timetz | to_timestamp_scale 
+-----------+----------------+-----------+----------+-----------+--------------------
+ -infinity | -infinity      | -infinity |          |           | -infinity
+ infinity  | infinity       | infinity  |          |           | infinity
+(2 rows)
+
+SELECT col2 as timestamptz,
+       CAST(col2 AS timestamptz DEFAULT NULL ON CONVERSION ERROR) as to_timestamptz,
+       CAST(col2 AS date DEFAULT NULL ON CONVERSION ERROR) as to_date,
+       CAST(col2 AS time DEFAULT NULL ON CONVERSION ERROR) as to_times,
+       CAST(col2 AS timetz DEFAULT NULL ON CONVERSION ERROR) as to_timetz,
+       CAST(col2 AS timestamp(2) DEFAULT NULL ON CONVERSION ERROR) as to_timestamp_scale
+FROM safecast1;
+ timestamptz | to_timestamptz |  to_date  | to_times | to_timetz | to_timestamp_scale 
+-------------+----------------+-----------+----------+-----------+--------------------
+ -infinity   | -infinity      | -infinity |          |           | -infinity
+ infinity    | infinity       | infinity  |          |           | infinity
+(2 rows)
+
+SELECT col3 as interval,
+       CAST(col3 AS timestamptz DEFAULT NULL ON CONVERSION ERROR) as to_timestamptz,
+       CAST(col3 AS date DEFAULT NULL ON CONVERSION ERROR) as to_date,
+       CAST(col3 AS time DEFAULT NULL ON CONVERSION ERROR) as to_times,
+       CAST(col3 AS timetz DEFAULT NULL ON CONVERSION ERROR) as to_timetz,
+       CAST(col3 AS timestamp(2) DEFAULT NULL ON CONVERSION ERROR) as to_timestamp_scale,
+       CAST(col3 AS interval(2) DEFAULT NULL ON CONVERSION ERROR) as to_interval_scale
+FROM safecast1;
+ interval  | to_timestamptz | to_date | to_times | to_timetz | to_timestamp_scale | to_interval_scale 
+-----------+----------------+---------+----------+-----------+--------------------+-------------------
+ -infinity |                |         |          |           |                    | -infinity
+ infinity  |                |         |          |           |                    | infinity
+(2 rows)
+
+SELECT col4 as time,
+       CAST(col4 AS time(2) DEFAULT NULL ON CONVERSION ERROR) as to_times,
+       CAST(col4 AS timetz DEFAULT NULL ON CONVERSION ERROR) as to_timetz,
+       CAST(col4 AS interval(2) DEFAULT NULL ON CONVERSION ERROR) as to_interval,
+       CAST(col4 AS interval(2) DEFAULT NULL ON CONVERSION ERROR) as to_interval_scale
+FROM safecast1;
+    time     |  to_times   |   to_timetz    |          to_interval          |       to_interval_scale       
+-------------+-------------+----------------+-------------------------------+-------------------------------
+ 15:36:39    | 15:36:39    | 15:36:39-07    | @ 15 hours 36 mins 39 secs    | @ 15 hours 36 mins 39 secs
+ 23:59:59.99 | 23:59:59.99 | 23:59:59.99-07 | @ 23 hours 59 mins 59.99 secs | @ 23 hours 59 mins 59.99 secs
+(2 rows)
+
+SELECT col5 as timetz,
+       CAST(col5 AS time DEFAULT NULL ON CONVERSION ERROR) as to_time,
+       CAST(col5 AS time(2) DEFAULT NULL ON CONVERSION ERROR) as to_time_scale,
+       CAST(col5 AS timetz DEFAULT NULL ON CONVERSION ERROR) as to_timetz,
+       CAST(col5 AS timetz(6) DEFAULT NULL ON CONVERSION ERROR) as to_timetz_scale,
+       CAST(col5 AS interval DEFAULT NULL ON CONVERSION ERROR) as to_interval,
+       CAST(col5 AS interval(2) DEFAULT NULL ON CONVERSION ERROR) as to_interval_scale
+FROM safecast1;
+     timetz     |   to_time   | to_time_scale |   to_timetz    | to_timetz_scale | to_interval | to_interval_scale 
+----------------+-------------+---------------+----------------+-----------------+-------------+-------------------
+ 15:36:39-04    | 15:36:39    | 15:36:39      | 15:36:39-04    | 15:36:39-04     |             | 
+ 23:59:59.99-07 | 23:59:59.99 | 23:59:59.99   | 23:59:59.99-07 | 23:59:59.99-07  |             | 
+(2 rows)
+
+CREATE TABLE safecast2(col0 jsonb, col1 text );
+INSERT INTO safecast2 VALUES ('"test"', '<value>one</value');
+SELECT col1 as text,
+       CAST(col0 AS regclass DEFAULT NULL ON CONVERSION ERROR) as to_regclass,
+       CAST(col0 AS "char"  DEFAULT NULL ON CONVERSION ERROR) as to_char,
+       CAST(col0 AS name DEFAULT NULL ON CONVERSION ERROR) as to_name,
+       CAST(col0 AS xml DEFAULT NULL ON CONVERSION ERROR) as to_xml
+FROM safecast2;
+       text        | to_regclass | to_char | to_name | to_xml 
+-------------------+-------------+---------+---------+--------
+ <value>one</value |             |         | "test"  | 
+(1 row)
+
+SELECT col0 as jsonb,
+       CAST(col0 AS integer DEFAULT NULL ON CONVERSION ERROR) as to_integer,
+       CAST(col0 AS numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+       CAST(col0 AS bigint DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+       CAST(col0 AS float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+       CAST(col0 AS float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+       CAST(col0 AS boolean DEFAULT NULL ON CONVERSION ERROR) as to_bool,
+       CAST(col0 AS smallint DEFAULT NULL ON CONVERSION ERROR) as to_smallint
+FROM safecast2;
+ jsonb  | to_integer | to_numeric | to_int8 | to_float4 | to_float8 | to_bool | to_smallint 
+--------+------------+------------+---------+-----------+-----------+---------+-------------
+ "test" |            |            |         |           |           |         |            
+(1 row)
+
+-- 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/expected/create_cast.out b/src/test/regress/expected/create_cast.out
index fd4871d94db..1ec99eef670 100644
--- a/src/test/regress/expected/create_cast.out
+++ b/src/test/regress/expected/create_cast.out
@@ -86,6 +86,11 @@ SELECT 1234::int4::casttesttype; -- Should work now
  bar1234
 (1 row)
 
+SELECT CAST(1234::int4 AS casttesttype DEFAULT NULL ON CONVERSION ERROR); -- error
+ERROR:  cannot cast type integer to casttesttype when DEFAULT clause is specified for CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(1234::int4 AS casttesttype DEFAULT NULL ON CONVE...
+                    ^
+HINT:  Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
 -- check dependencies generated for that
 SELECT pg_describe_object(classid, objid, objsubid) as obj,
        pg_describe_object(refclassid, refobjid, refobjsubid) as objref,
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 20bf9ea9cdf..25478364b7c 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -943,8 +943,8 @@ SELECT *
 FROM pg_cast c
 WHERE castsource = 0 OR casttarget = 0 OR castcontext NOT IN ('e', 'a', 'i')
     OR castmethod NOT IN ('f', 'b' ,'i');
- oid | castsource | casttarget | castfunc | castcontext | castmethod 
------+------------+------------+----------+-------------+------------
+ oid | castsource | casttarget | castfunc | castcontext | castmethod | casterrorsafe 
+-----+------------+------------+----------+-------------+------------+---------------
 (0 rows)
 
 -- Check that castfunc is nonzero only for cast methods that need a function,
@@ -953,8 +953,8 @@ SELECT *
 FROM pg_cast c
 WHERE (castmethod = 'f' AND castfunc = 0)
    OR (castmethod IN ('b', 'i') AND castfunc <> 0);
- oid | castsource | casttarget | castfunc | castcontext | castmethod 
------+------------+------------+----------+-------------+------------
+ oid | castsource | casttarget | castfunc | castcontext | castmethod | casterrorsafe 
+-----+------------+------------+----------+-------------+------------+---------------
 (0 rows)
 
 -- Look for casts to/from the same type that aren't length coercion functions.
@@ -963,15 +963,15 @@ WHERE (castmethod = 'f' AND castfunc = 0)
 SELECT *
 FROM pg_cast c
 WHERE castsource = casttarget AND castfunc = 0;
- oid | castsource | casttarget | castfunc | castcontext | castmethod 
------+------------+------------+----------+-------------+------------
+ oid | castsource | casttarget | castfunc | castcontext | castmethod | casterrorsafe 
+-----+------------+------------+----------+-------------+------------+---------------
 (0 rows)
 
 SELECT c.*
 FROM pg_cast c, pg_proc p
 WHERE c.castfunc = p.oid AND p.pronargs < 2 AND castsource = casttarget;
- oid | castsource | casttarget | castfunc | castcontext | castmethod 
------+------------+------------+----------+-------------+------------
+ oid | castsource | casttarget | castfunc | castcontext | castmethod | casterrorsafe 
+-----+------------+------------+----------+-------------+------------+---------------
 (0 rows)
 
 -- Look for cast functions that don't have the right signature.  The
@@ -989,8 +989,8 @@ WHERE c.castfunc = p.oid AND
              OR (c.castsource = 'character'::regtype AND
                  p.proargtypes[0] = 'text'::regtype))
      OR NOT binary_coercible(p.prorettype, c.casttarget));
- oid | castsource | casttarget | castfunc | castcontext | castmethod 
------+------------+------------+----------+-------------+------------
+ oid | castsource | casttarget | castfunc | castcontext | castmethod | casterrorsafe 
+-----+------------+------------+----------+-------------+------------+---------------
 (0 rows)
 
 SELECT c.*
@@ -998,8 +998,8 @@ FROM pg_cast c, pg_proc p
 WHERE c.castfunc = p.oid AND
     ((p.pronargs > 1 AND p.proargtypes[1] != 'int4'::regtype) OR
      (p.pronargs > 2 AND p.proargtypes[2] != 'bool'::regtype));
- oid | castsource | casttarget | castfunc | castcontext | castmethod 
------+------------+------------+----------+-------------+------------
+ oid | castsource | casttarget | castfunc | castcontext | castmethod | casterrorsafe 
+-----+------------+------------+----------+-------------+------------+---------------
 (0 rows)
 
 -- Look for binary compatible casts that do not have the reverse
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index fbffc67ae60..ebbd454c450 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 00000000000..27334212c8c
--- /dev/null
+++ b/src/test/regress/sql/cast.sql
@@ -0,0 +1,259 @@
+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);
+SELECT CAST(1111 AS "char" 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
+
+-----safe cast from bytea type to other type
+SELECT CAST ('\x112233445566778899'::bytea AS int8 DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('\x123456789A'::bytea AS int4 DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('\x123456'::bytea AS int2 DEFAULT NULL ON CONVERSION ERROR);
+
+-----safe cast from range type to other type
+SELECT CAST('[1,2]'::int4range AS int4multirange DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('[1,2]'::int8range AS int8multirange DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('[1,2]'::numrange  AS nummultirange DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('[-infinity,infinity]'::daterange AS datemultirange DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('[-infinity,infinity]'::tsrange AS tsmultirange DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('[-infinity,infinity]'::tstzrange AS tstzmultirange DEFAULT NULL ON CONVERSION ERROR);
+
+-----safe cast from geometry to other geometry is not supported
+SELECT CAST(NULL::point AS box DEFAULT NULL ON CONVERSION ERROR); --error
+SELECT CAST(NULL::lseg AS point DEFAULT NULL ON CONVERSION ERROR); --error
+SELECT CAST(NULL::path AS polygon DEFAULT NULL ON CONVERSION ERROR); --error
+SELECT CAST(NULL::box AS point DEFAULT NULL ON CONVERSION ERROR); --error
+SELECT CAST(NULL::box AS lseg DEFAULT NULL ON CONVERSION ERROR); --error
+SELECT CAST(NULL::box AS polygon DEFAULT NULL ON CONVERSION ERROR); --error
+SELECT CAST(NULL::box AS circle DEFAULT NULL ON CONVERSION ERROR); --error
+SELECT CAST(NULL::polygon AS point DEFAULT NULL ON CONVERSION ERROR); --error
+SELECT CAST(NULL::polygon AS path DEFAULT NULL ON CONVERSION ERROR); --error
+SELECT CAST(NULL::polygon AS box DEFAULT NULL ON CONVERSION ERROR); --error
+SELECT CAST(NULL::polygon AS circle DEFAULT NULL ON CONVERSION ERROR); --error
+SELECT CAST(NULL::circle AS point DEFAULT NULL ON CONVERSION ERROR); --error
+SELECT CAST(NULL::circle AS box DEFAULT NULL ON CONVERSION ERROR); --error
+SELECT CAST(NULL::circle AS polygon DEFAULT NULL ON CONVERSION ERROR); --error
+
+-----safe cast from money or money cast to other type is not supported
+SELECT CAST(NULL::int8 AS money DEFAULT NULL ON CONVERSION ERROR); --error
+SELECT CAST(NULL::int4 AS money DEFAULT NULL ON CONVERSION ERROR); --error
+SELECT CAST(NULL::numeric AS money DEFAULT NULL ON CONVERSION ERROR); --error
+SELECT CAST(NULL::money AS numeric DEFAULT NULL 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[],
+                        col5 int2 default 32767,
+                        col6 int4 default 32768,
+                        col7 int8 default 4294967296);
+INSERT INTO safecast VALUES('11.1234', '11.1234', '9223372036854775808', '{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 oid DEFAULT NULL ON CONVERSION ERROR) as to_oid,
+       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 oid DEFAULT NULL ON CONVERSION ERROR) as to_oid,
+       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 oid DEFAULT NULL ON CONVERSION ERROR) as to_oid,
+       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;
+
+SELECT col5 as int2,
+       CAST(col5 AS int2 DEFAULT NULL ON CONVERSION ERROR) as to_int2,
+       CAST(col5 AS int4 DEFAULT NULL ON CONVERSION ERROR) as to_int4,
+       CAST(col5 AS oid DEFAULT NULL ON CONVERSION ERROR) as to_oid,
+       CAST(col5 as int8 DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+       CAST(col5 as float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+       CAST(col5 as float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+       CAST(col5 as numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+       CAST(col5 as numeric(10,1) DEFAULT NULL ON CONVERSION ERROR) as to_numeric_scale
+FROM safecast;
+
+SELECT col6 as int4,
+       CAST(col6 AS int2 DEFAULT NULL ON CONVERSION ERROR) as to_int2,
+       CAST(col6 AS int4 DEFAULT NULL ON CONVERSION ERROR) as to_int4,
+       CAST(col6 AS oid DEFAULT NULL ON CONVERSION ERROR) as to_oid,
+       CAST(col6 as int8 DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+       CAST(col6 as float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+       CAST(col6 as float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+       CAST(col6 as numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+       CAST(col6 as numeric(10,1) DEFAULT NULL ON CONVERSION ERROR) as to_numeric_scale
+FROM safecast;
+
+SELECT col7 as int8,
+       CAST(col7 AS int2 DEFAULT NULL ON CONVERSION ERROR) as to_int2,
+       CAST(col7 AS int4 DEFAULT NULL ON CONVERSION ERROR) as to_int4,
+       CAST(col7 AS oid DEFAULT NULL ON CONVERSION ERROR) as to_oid,
+       CAST(col7 as int8 DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+       CAST(col7 as float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+       CAST(col7 as float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+       CAST(col7 as numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+       CAST(col7 as numeric(10,1) DEFAULT NULL ON CONVERSION ERROR) as to_numeric_scale
+FROM safecast;
+
+--test date/timestamp/interval realted cast
+CREATE TABLE safecast1(col0 date, col1 timestamp, col2 timestamptz, col3 interval, col4 time, col5 timetz);
+insert into safecast1 VALUES
+('-infinity', '-infinity', '-infinity', '-infinity', '2003-03-07 15:36:39 America/New_York', '2003-07-07 15:36:39 America/New_York'),
+('-infinity', 'infinity', 'infinity', 'infinity','11:59:59.99 PM', '11:59:59.99 PM PDT');
+
+SELECT col0 as date,
+       CAST(col0 AS timestamptz DEFAULT NULL ON CONVERSION ERROR) as to_timestamptz,
+       CAST(col0 AS date DEFAULT NULL ON CONVERSION ERROR) as to_date,
+       CAST(col0 AS time DEFAULT NULL ON CONVERSION ERROR) as to_times,
+       CAST(col0 AS timetz DEFAULT NULL ON CONVERSION ERROR) as to_timetz,
+       CAST(col0 AS timestamp(2) DEFAULT NULL ON CONVERSION ERROR) as to_timestamp_scale
+FROM safecast1;
+
+SELECT col1 as timestamp,
+       CAST(col1 AS timestamptz DEFAULT NULL ON CONVERSION ERROR) as to_timestamptz,
+       CAST(col1 AS date DEFAULT NULL ON CONVERSION ERROR) as to_date,
+       CAST(col1 AS time DEFAULT NULL ON CONVERSION ERROR) as to_times,
+       CAST(col1 AS timetz DEFAULT NULL ON CONVERSION ERROR) as to_timetz,
+       CAST(col1 AS timestamp(2) DEFAULT NULL ON CONVERSION ERROR) as to_timestamp_scale
+FROM safecast1;
+
+SELECT col2 as timestamptz,
+       CAST(col2 AS timestamptz DEFAULT NULL ON CONVERSION ERROR) as to_timestamptz,
+       CAST(col2 AS date DEFAULT NULL ON CONVERSION ERROR) as to_date,
+       CAST(col2 AS time DEFAULT NULL ON CONVERSION ERROR) as to_times,
+       CAST(col2 AS timetz DEFAULT NULL ON CONVERSION ERROR) as to_timetz,
+       CAST(col2 AS timestamp(2) DEFAULT NULL ON CONVERSION ERROR) as to_timestamp_scale
+FROM safecast1;
+
+SELECT col3 as interval,
+       CAST(col3 AS timestamptz DEFAULT NULL ON CONVERSION ERROR) as to_timestamptz,
+       CAST(col3 AS date DEFAULT NULL ON CONVERSION ERROR) as to_date,
+       CAST(col3 AS time DEFAULT NULL ON CONVERSION ERROR) as to_times,
+       CAST(col3 AS timetz DEFAULT NULL ON CONVERSION ERROR) as to_timetz,
+       CAST(col3 AS timestamp(2) DEFAULT NULL ON CONVERSION ERROR) as to_timestamp_scale,
+       CAST(col3 AS interval(2) DEFAULT NULL ON CONVERSION ERROR) as to_interval_scale
+FROM safecast1;
+
+SELECT col4 as time,
+       CAST(col4 AS time(2) DEFAULT NULL ON CONVERSION ERROR) as to_times,
+       CAST(col4 AS timetz DEFAULT NULL ON CONVERSION ERROR) as to_timetz,
+       CAST(col4 AS interval(2) DEFAULT NULL ON CONVERSION ERROR) as to_interval,
+       CAST(col4 AS interval(2) DEFAULT NULL ON CONVERSION ERROR) as to_interval_scale
+FROM safecast1;
+
+SELECT col5 as timetz,
+       CAST(col5 AS time DEFAULT NULL ON CONVERSION ERROR) as to_time,
+       CAST(col5 AS time(2) DEFAULT NULL ON CONVERSION ERROR) as to_time_scale,
+       CAST(col5 AS timetz DEFAULT NULL ON CONVERSION ERROR) as to_timetz,
+       CAST(col5 AS timetz(6) DEFAULT NULL ON CONVERSION ERROR) as to_timetz_scale,
+       CAST(col5 AS interval DEFAULT NULL ON CONVERSION ERROR) as to_interval,
+       CAST(col5 AS interval(2) DEFAULT NULL ON CONVERSION ERROR) as to_interval_scale
+FROM safecast1;
+
+CREATE TABLE safecast2(col0 jsonb, col1 text );
+INSERT INTO safecast2 VALUES ('"test"', '<value>one</value');
+
+SELECT col1 as text,
+       CAST(col0 AS regclass DEFAULT NULL ON CONVERSION ERROR) as to_regclass,
+       CAST(col0 AS "char"  DEFAULT NULL ON CONVERSION ERROR) as to_char,
+       CAST(col0 AS name DEFAULT NULL ON CONVERSION ERROR) as to_name,
+       CAST(col0 AS xml DEFAULT NULL ON CONVERSION ERROR) as to_xml
+FROM safecast2;
+
+SELECT col0 as jsonb,
+       CAST(col0 AS integer DEFAULT NULL ON CONVERSION ERROR) as to_integer,
+       CAST(col0 AS numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+       CAST(col0 AS bigint DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+       CAST(col0 AS float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+       CAST(col0 AS float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+       CAST(col0 AS boolean DEFAULT NULL ON CONVERSION ERROR) as to_bool,
+       CAST(col0 AS smallint DEFAULT NULL ON CONVERSION ERROR) as to_smallint
+FROM safecast2;
+
+-- 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/test/regress/sql/create_cast.sql b/src/test/regress/sql/create_cast.sql
index 32187853cc7..0a15a795d87 100644
--- a/src/test/regress/sql/create_cast.sql
+++ b/src/test/regress/sql/create_cast.sql
@@ -62,6 +62,7 @@ $$ SELECT ('bar'::text || $1::text); $$;
 
 CREATE CAST (int4 AS casttesttype) WITH FUNCTION bar_int4_text(int4) AS IMPLICIT;
 SELECT 1234::int4::casttesttype; -- Should work now
+SELECT CAST(1234::int4 AS casttesttype DEFAULT NULL ON CONVERSION ERROR); -- error
 
 -- check dependencies generated for that
 SELECT pg_describe_object(classid, objid, objsubid) as obj,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index e6f2e93b2d6..5b32df3a2db 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2652,6 +2652,9 @@ STRLEN
 SV
 SYNCHRONIZATION_BARRIER
 SYSTEM_INFO
+SafeTypeCast
+SafeTypeCastExpr
+SafeTypeCastState
 SampleScan
 SampleScanGetSampleSize_function
 SampleScanState
-- 
2.34.1



  [text/x-patch] v6-0010-error-safe-for-casting-text-to-other-types-per-pg_cast.patch (10.3K, 7-v6-0010-error-safe-for-casting-text-to-other-types-per-pg_cast.patch)
  download | inline diff:
From 2efaf3b582b15d62b3250535dfbc0840a7444dcb Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Sun, 10 Aug 2025 01:56:03 +0800
Subject: [PATCH v6 10/18] error safe for casting text to other types per
 pg_cast

select castsource::regtype, casttarget::regtype, castfunc,
castcontext,castmethod, pp.prosrc, pp.proname from pg_cast pc join pg_proc pp on
pp.oid = pc.castfunc and pc.castfunc > 0
and castsource::regtype = 'text'::regtype
order by castsource::regtype;

 castsource | casttarget | castfunc | castcontext | castmethod |    prosrc     | proname
------------+------------+----------+-------------+------------+---------------+----------
 text       | regclass   |     1079 | i           | f          | text_regclass | regclass
 text       | "char"     |      944 | a           | f          | text_char     | char
 text       | name       |      407 | i           | f          | text_name     | name
 text       | xml        |     2896 | e           | f          | texttoxml     | xml
(4 rows)

already error safe: text_name, text_char.
texttoxml is refactored in character type error safe patch.
---
 src/backend/catalog/namespace.c | 167 ++++++++++++++++++++++++++++++++
 src/backend/utils/adt/regproc.c |  22 ++++-
 src/backend/utils/adt/varlena.c |  42 ++++++++
 src/include/catalog/namespace.h |   4 +
 src/include/utils/varlena.h     |   1 +
 5 files changed, 233 insertions(+), 3 deletions(-)

diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index d97d632a7ef..4a5506eeac5 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -641,6 +641,137 @@ RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode,
 	return relId;
 }
 
+/* see RangeVarGetRelidExtended also */
+Oid
+RangeVarGetRelidExtendedSafe(const RangeVar *relation, LOCKMODE lockmode,
+							 uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg,
+							 Node *escontext)
+{
+	uint64		inval_count;
+	Oid			relId;
+	Oid			oldRelId = InvalidOid;
+	bool		retry = false;
+	bool		missing_ok = (flags & RVR_MISSING_OK) != 0;
+
+	/* verify that flags do no conflict */
+	Assert(!((flags & RVR_NOWAIT) && (flags & RVR_SKIP_LOCKED)));
+
+	/*
+	 * We check the catalog name and then ignore it.
+	 */
+	if (relation->catalogname)
+	{
+		if (strcmp(relation->catalogname, get_database_name(MyDatabaseId)) != 0)
+			ereturn(escontext, InvalidOid,
+					errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
+							relation->catalogname, relation->schemaname,
+							relation->relname));
+	}
+
+	for (;;)
+	{
+		inval_count = SharedInvalidMessageCounter;
+
+		if (relation->relpersistence == RELPERSISTENCE_TEMP)
+		{
+			if (!OidIsValid(myTempNamespace))
+				relId = InvalidOid;
+			else
+			{
+				if (relation->schemaname)
+				{
+					Oid			namespaceId;
+
+					namespaceId = LookupExplicitNamespace(relation->schemaname, missing_ok);
+
+					/*
+					 * For missing_ok, allow a non-existent schema name to
+					 * return InvalidOid.
+					 */
+					if (namespaceId != myTempNamespace)
+						ereturn(escontext, InvalidOid,
+								errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+								errmsg("temporary tables cannot specify a schema name"));
+				}
+
+				relId = get_relname_relid(relation->relname, myTempNamespace);
+			}
+		}
+		else if (relation->schemaname)
+		{
+			Oid			namespaceId;
+
+			/* use exact schema given */
+			namespaceId = LookupExplicitNamespace(relation->schemaname, missing_ok);
+			if (missing_ok && !OidIsValid(namespaceId))
+				relId = InvalidOid;
+			else
+				relId = get_relname_relid(relation->relname, namespaceId);
+		}
+		else
+		{
+			/* search the namespace path */
+			relId = RelnameGetRelid(relation->relname);
+		}
+
+		if (callback)
+			callback(relation, relId, oldRelId, callback_arg);
+
+		if (lockmode == NoLock)
+			break;
+
+		if (retry)
+		{
+			if (relId == oldRelId)
+				break;
+			if (OidIsValid(oldRelId))
+				UnlockRelationOid(oldRelId, lockmode);
+		}
+
+		if (!OidIsValid(relId))
+			AcceptInvalidationMessages();
+		else if (!(flags & (RVR_NOWAIT | RVR_SKIP_LOCKED)))
+			LockRelationOid(relId, lockmode);
+		else if (!ConditionalLockRelationOid(relId, lockmode))
+		{
+			if (relation->schemaname)
+				ereport(DEBUG1,
+						errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+						errmsg("could not obtain lock on relation \"%s.%s\"",
+								relation->schemaname, relation->relname));
+			else
+				ereport(DEBUG1,
+						errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+						errmsg("could not obtain lock on relation \"%s\"",
+							   relation->relname));
+
+			return InvalidOid;
+		}
+
+		if (inval_count == SharedInvalidMessageCounter)
+			break;
+
+		retry = true;
+		oldRelId = relId;
+	}
+
+	if (!OidIsValid(relId))
+	{
+		if (relation->schemaname)
+			ereport(DEBUG1,
+					errcode(ERRCODE_UNDEFINED_TABLE),
+					errmsg("relation \"%s.%s\" does not exist",
+							relation->schemaname, relation->relname));
+		else
+			ereport(DEBUG1,
+					errcode(ERRCODE_UNDEFINED_TABLE),
+					errmsg("relation \"%s\" does not exist",
+							relation->relname));
+	}
+	return relId;
+}
+
 /*
  * RangeVarGetCreationNamespace
  *		Given a RangeVar describing a to-be-created relation,
@@ -3580,6 +3711,42 @@ makeRangeVarFromNameList(const List *names)
 	return rel;
 }
 
+/*
+ * makeRangeVarFromNameListSafe
+ *		Utility routine to convert a qualified-name list into RangeVar form.
+ * The result maybe NULL.
+ */
+RangeVar *
+makeRangeVarFromNameListSafe(const List *names, Node *escontext)
+{
+	RangeVar   *rel = makeRangeVar(NULL, NULL, -1);
+
+	switch (list_length(names))
+	{
+		case 1:
+			rel->relname = strVal(linitial(names));
+			break;
+		case 2:
+			rel->schemaname = strVal(linitial(names));
+			rel->relname = strVal(lsecond(names));
+			break;
+		case 3:
+			rel->catalogname = strVal(linitial(names));
+			rel->schemaname = strVal(lsecond(names));
+			rel->relname = strVal(lthird(names));
+			break;
+		default:
+			errsave(escontext,
+					errcode(ERRCODE_SYNTAX_ERROR),
+					errmsg("improper relation name (too many dotted names): %s",
+							NameListToString(names)));
+			rel = NULL;
+			break;
+	}
+
+	return rel;
+}
+
 /*
  * NameListToString
  *		Utility routine to convert a qualified-name list into a string.
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index b8bbe95e82e..e7f259e8a0a 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -1895,10 +1895,26 @@ text_regclass(PG_FUNCTION_ARGS)
 	Oid			result;
 	RangeVar   *rv;
 
-	rv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
+	if (likely(!fcinfo->context))
+	{
+		rv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
 
-	/* We might not even have permissions on this relation; don't lock it. */
-	result = RangeVarGetRelid(rv, NoLock, false);
+		/* We might not even have permissions on this relation; don't lock it. */
+		result = RangeVarGetRelid(rv, NoLock, false);
+	}
+	else
+	{
+		List   		*rvnames;
+
+		rvnames = textToQualifiedNameListSafe(relname, fcinfo->context);
+		if (SOFT_ERROR_OCCURRED(fcinfo->context))
+			PG_RETURN_NULL();
+
+		rv = makeRangeVarFromNameList(rvnames);
+		result = RangeVarGetRelidExtendedSafe(rv, NoLock, 0, NULL, NULL, fcinfo->context);
+		if (SOFT_ERROR_OCCURRED(fcinfo->context))
+			PG_RETURN_NULL();
+	}
 
 	PG_RETURN_OID(result);
 }
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 11b442a5941..80852d5e922 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -2724,6 +2724,48 @@ textToQualifiedNameList(text *textval)
 	return result;
 }
 
+/* see textToQualifiedNameList also */
+List *
+textToQualifiedNameListSafe(text *textval, Node *escontext)
+{
+	char	   *rawname;
+	List	   *result = NIL;
+	List	   *namelist;
+	ListCell   *l;
+
+	/* Convert to C string (handles possible detoasting). */
+	/* Note we rely on being able to modify rawname below. */
+	rawname = text_to_cstring(textval);
+
+	if (!SplitIdentifierString(rawname, '.', &namelist))
+	{
+		errsave(escontext,
+				errcode(ERRCODE_INVALID_NAME),
+				errmsg("invalid name syntax"));
+		return NIL;
+	}
+
+	if (namelist == NIL)
+	{
+		errsave(escontext,
+				errcode(ERRCODE_INVALID_NAME),
+				errmsg("invalid name syntax"));
+		return NIL;
+	}
+
+	foreach(l, namelist)
+	{
+		char	   *curname = (char *) lfirst(l);
+
+		result = lappend(result, makeString(pstrdup(curname)));
+	}
+
+	pfree(rawname);
+	list_free(namelist);
+
+	return result;
+}
+
 /*
  * SplitIdentifierString --- parse a string containing identifiers
  *
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index 8c7ccc69a3c..e095cbe70fa 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -85,6 +85,9 @@ extern Oid	RangeVarGetRelidExtended(const RangeVar *relation,
 									 LOCKMODE lockmode, uint32 flags,
 									 RangeVarGetRelidCallback callback,
 									 void *callback_arg);
+extern Oid RangeVarGetRelidExtendedSafe(const RangeVar *relation, LOCKMODE lockmode,
+										uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg,
+										Node *escontext);
 extern Oid	RangeVarGetCreationNamespace(const RangeVar *newRelation);
 extern Oid	RangeVarGetAndCheckCreationNamespace(RangeVar *relation,
 												 LOCKMODE lockmode,
@@ -148,6 +151,7 @@ extern Oid	LookupCreationNamespace(const char *nspname);
 extern void CheckSetNamespace(Oid oldNspOid, Oid nspOid);
 extern Oid	QualifiedNameGetCreationNamespace(const List *names, char **objname_p);
 extern RangeVar *makeRangeVarFromNameList(const List *names);
+extern RangeVar *makeRangeVarFromNameListSafe(const List *names, Node *escontext);
 extern char *NameListToString(const List *names);
 extern char *NameListToQuotedString(const List *names);
 
diff --git a/src/include/utils/varlena.h b/src/include/utils/varlena.h
index db9fdf72941..0cf01ae5281 100644
--- a/src/include/utils/varlena.h
+++ b/src/include/utils/varlena.h
@@ -27,6 +27,7 @@ extern int	varstr_levenshtein_less_equal(const char *source, int slen,
 										  int ins_c, int del_c, int sub_c,
 										  int max_d, bool trusted);
 extern List *textToQualifiedNameList(text *textval);
+extern List *textToQualifiedNameListSafe(text *textval, Node *escontext);
 extern bool SplitIdentifierString(char *rawstring, char separator,
 								  List **namelist);
 extern bool SplitDirectoriesString(char *rawstring, char separator,
-- 
2.34.1



  [text/x-patch] v6-0012-error-safe-for-casting-interval-to-other-types-per-pg_cas.patch (2.0K, 8-v6-0012-error-safe-for-casting-interval-to-other-types-per-pg_cas.patch)
  download | inline diff:
From f118cff3a42d350e0e2a7274ac54f6f9a8771ed9 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Sun, 10 Aug 2025 01:17:27 +0800
Subject: [PATCH v6 12/18] error safe for casting interval to other types per
 pg_cast

select castsource::regtype, casttarget::regtype, castfunc,
castcontext,castmethod, pp.prosrc, pp.proname from pg_cast pc join pg_proc pp on
pp.oid = pc.castfunc and pc.castfunc > 0
and castsource::regtype = 'interval'::regtype
order by castsource::regtype;

 castsource |       casttarget       | castfunc | castcontext | castmethod |     prosrc     | proname
------------+------------------------+----------+-------------+------------+----------------+----------
 interval   | time without time zone |     1419 | a           | f          | interval_time  | time
 interval   | interval               |     1200 | i           | f          | interval_scale | interval
(2 rows)
---
 src/backend/utils/adt/date.c      | 2 +-
 src/backend/utils/adt/timestamp.c | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index c7a3cde2d81..4f0f3d26989 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -2180,7 +2180,7 @@ interval_time(PG_FUNCTION_ARGS)
 	TimeADT		result;
 
 	if (INTERVAL_NOT_FINITE(span))
-		ereport(ERROR,
+		ereturn(fcinfo->context, (Datum) 0,
 				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
 				 errmsg("cannot convert infinite interval to time")));
 
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index e640b48205b..f1e96b84a6f 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -1334,7 +1334,8 @@ interval_scale(PG_FUNCTION_ARGS)
 	result = palloc(sizeof(Interval));
 	*result = *interval;
 
-	AdjustIntervalForTypmod(result, typmod, NULL);
+	if (!AdjustIntervalForTypmod(result, typmod, fcinfo->context))
+		PG_RETURN_NULL();
 
 	PG_RETURN_INTERVAL_P(result);
 }
-- 
2.34.1



  [text/x-patch] v6-0013-error-safe-for-casting-macaddr8-to-other-types-per-pg_cas.patch (1.4K, 9-v6-0013-error-safe-for-casting-macaddr8-to-other-types-per-pg_cas.patch)
  download | inline diff:
From 44d59f188fb4dfad880d9f5d25cd3e5625529c8e Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Tue, 5 Aug 2025 16:55:44 +0800
Subject: [PATCH v6 13/18] error safe for casting macaddr8 to other types per
 pg_cast

select castsource::regtype, casttarget::regtype, castfunc, castcontext,castmethod, pp.prosrc, pp.proname
from pg_cast pc join pg_proc pp on pp.oid = pc.castfunc
and pc.castfunc > 0 and castsource::regtype ='macaddr8'::regtype
order by castsource::regtype;

 castsource | casttarget | castfunc | castcontext | castmethod |      prosrc       | proname
------------+------------+----------+-------------+------------+-------------------+---------
 macaddr8   | macaddr    |     4124 | i           | f          | macaddr8tomacaddr | macaddr
(1 row)
---
 src/backend/utils/adt/mac8.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/backend/utils/adt/mac8.c b/src/backend/utils/adt/mac8.c
index 08e41ba4eea..1c903f152de 100644
--- a/src/backend/utils/adt/mac8.c
+++ b/src/backend/utils/adt/mac8.c
@@ -550,7 +550,7 @@ macaddr8tomacaddr(PG_FUNCTION_ARGS)
 	result = (macaddr *) palloc0(sizeof(macaddr));
 
 	if ((addr->d != 0xFF) || (addr->e != 0xFE))
-		ereport(ERROR,
+		ereturn(fcinfo->context, (Datum) 0,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("macaddr8 data out of range to convert to macaddr"),
 				 errhint("Only addresses that have FF and FE as values in the "
-- 
2.34.1



  [text/x-patch] v6-0009-error-safe-for-casting-date-to-other-types-per-pg_cast.patch (2.0K, 10-v6-0009-error-safe-for-casting-date-to-other-types-per-pg_cast.patch)
  download | inline diff:
From 5e9ced90d27245b90b80294dde7c64405abd6f5d Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Sat, 9 Aug 2025 22:35:57 +0800
Subject: [PATCH v6 09/18] error safe for casting date to other types per
 pg_cast

select castsource::regtype, casttarget::regtype, castfunc,
castcontext,castmethod, pp.prosrc, pp.proname from pg_cast pc join pg_proc pp on
pp.oid = pc.castfunc and pc.castfunc > 0
and castsource::regtype = 'date'::regtype
order by castsource::regtype;

castsource |         casttarget          | castfunc | castcontext | castmethod |      prosrc      |   proname
------------+-----------------------------+----------+-------------+------------+------------------+-------------
 date       | timestamp without time zone |     2024 | i           | f          | date_timestamp   | timestamp
 date       | timestamp with time zone    |     1174 | i           | f          | date_timestamptz | timestamptz
(2 rows)
---
 src/backend/utils/adt/date.c | 22 ++++++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 344f58b92f7..c7a3cde2d81 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -1350,7 +1350,16 @@ date_timestamp(PG_FUNCTION_ARGS)
 	DateADT		dateVal = PG_GETARG_DATEADT(0);
 	Timestamp	result;
 
-	result = date2timestamp(dateVal);
+	if (likely(!fcinfo->context))
+		result = date2timestamp(dateVal);
+	else
+	{
+		int			overflow;
+
+		result = date2timestamp_opt_overflow(dateVal, &overflow);
+		if (overflow != 0)
+			PG_RETURN_NULL();
+	}
 
 	PG_RETURN_TIMESTAMP(result);
 }
@@ -1435,7 +1444,16 @@ date_timestamptz(PG_FUNCTION_ARGS)
 	DateADT		dateVal = PG_GETARG_DATEADT(0);
 	TimestampTz result;
 
-	result = date2timestamptz(dateVal);
+	if (likely(!fcinfo->context))
+		result = date2timestamptz(dateVal);
+	else
+	{
+		int			overflow;
+		result = date2timestamptz_opt_overflow(dateVal, &overflow);
+
+		if (overflow != 0)
+			PG_RETURN_NULL();
+	}
 
 	PG_RETURN_TIMESTAMP(result);
 }
-- 
2.34.1



  [text/x-patch] v6-0011-error-safe-for-casting-inet-to-other-types-per-pg_cast.patch (1.6K, 11-v6-0011-error-safe-for-casting-inet-to-other-types-per-pg_cast.patch)
  download | inline diff:
From fe3598c63639bc81051b0127c15b1aef3e2115f5 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Tue, 5 Aug 2025 15:42:58 +0800
Subject: [PATCH v6 11/18] error safe for casting inet to other types per
 pg_cast

select castsource::regtype, casttarget::regtype, castfunc,
castcontext,castmethod, pp.prosrc, pp.proname from pg_cast pc join pg_proc pp on
pp.oid = pc.castfunc and pc.castfunc > 0
and castsource::regtype = 'inet'::regtype
order by castsource::regtype;

 castsource |    casttarget     | castfunc | castcontext | castmethod |    prosrc    | proname
------------+-------------------+----------+-------------+------------+--------------+---------
 inet       | cidr              |     1715 | a           | f          | inet_to_cidr | cidr
 inet       | text              |      730 | a           | f          | network_show | text
 inet       | character varying |      730 | a           | f          | network_show | text
 inet       | character         |      730 | a           | f          | network_show | text
(4 rows)
---
 src/backend/utils/adt/network.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/backend/utils/adt/network.c b/src/backend/utils/adt/network.c
index 9fd211b2d45..0e2021bcc3c 100644
--- a/src/backend/utils/adt/network.c
+++ b/src/backend/utils/adt/network.c
@@ -1167,7 +1167,7 @@ network_show(PG_FUNCTION_ARGS)
 
 	if (pg_inet_net_ntop(ip_family(ip), ip_addr(ip), ip_maxbits(ip),
 						 tmp, sizeof(tmp)) == NULL)
-		ereport(ERROR,
+		ereturn(fcinfo->context, (Datum) 0,
 				(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
 				 errmsg("could not format inet value: %m")));
 
-- 
2.34.1



  [text/x-patch] v6-0008-error-safe-for-casting-jsonb-to-other-types-per-pg_cast.patch (6.1K, 12-v6-0008-error-safe-for-casting-jsonb-to-other-types-per-pg_cast.patch)
  download | inline diff:
From 6a409bdb5c41ed3b0e2b8b51530bd6d6be0dc9f0 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Wed, 6 Aug 2025 12:12:31 +0800
Subject: [PATCH v6 08/18] error safe for casting jsonb to other types per
 pg_cast

select castsource::regtype, casttarget::regtype, castfunc,
castcontext,castmethod, pp.prosrc, pp.proname from pg_cast pc join pg_proc pp on
pp.oid = pc.castfunc and pc.castfunc > 0
and castsource::regtype = 'jsonb'::regtype
order by castsource::regtype;

 castsource |    casttarget    | castfunc | castcontext | castmethod |    prosrc     | proname
------------+------------------+----------+-------------+------------+---------------+---------
 jsonb      | boolean          |     3556 | e           | f          | jsonb_bool    | bool
 jsonb      | numeric          |     3449 | e           | f          | jsonb_numeric | numeric
 jsonb      | smallint         |     3450 | e           | f          | jsonb_int2    | int2
 jsonb      | integer          |     3451 | e           | f          | jsonb_int4    | int4
 jsonb      | bigint           |     3452 | e           | f          | jsonb_int8    | int8
 jsonb      | real             |     3453 | e           | f          | jsonb_float4  | float4
 jsonb      | double precision |     2580 | e           | f          | jsonb_float8  | float8
(7 rows)
---
 src/backend/utils/adt/jsonb.c | 35 ++++++++++++++++++-----------------
 1 file changed, 18 insertions(+), 17 deletions(-)

diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index da94d424d61..7f3715b1684 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -2004,8 +2004,8 @@ JsonbExtractScalar(JsonbContainer *jbc, JsonbValue *res)
 /*
  * Emit correct, translatable cast error message
  */
-static void
-cannotCastJsonbValue(enum jbvType type, const char *sqltype)
+static Datum
+cannotCastJsonbValue(enum jbvType type, const char *sqltype, Node *escontext)
 {
 	static const struct
 	{
@@ -2026,12 +2026,13 @@ cannotCastJsonbValue(enum jbvType type, const char *sqltype)
 
 	for (i = 0; i < lengthof(messages); i++)
 		if (messages[i].type == type)
-			ereport(ERROR,
+			ereturn(escontext, (Datum) 0,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg(messages[i].msg, sqltype)));
 
 	/* should be unreachable */
 	elog(ERROR, "unknown jsonb type: %d", (int) type);
+	return (Datum) 0;
 }
 
 Datum
@@ -2041,7 +2042,7 @@ jsonb_bool(PG_FUNCTION_ARGS)
 	JsonbValue	v;
 
 	if (!JsonbExtractScalar(&in->root, &v))
-		cannotCastJsonbValue(v.type, "boolean");
+		return cannotCastJsonbValue(v.type, "boolean", fcinfo->context);
 
 	if (v.type == jbvNull)
 	{
@@ -2050,7 +2051,7 @@ jsonb_bool(PG_FUNCTION_ARGS)
 	}
 
 	if (v.type != jbvBool)
-		cannotCastJsonbValue(v.type, "boolean");
+		return cannotCastJsonbValue(v.type, "boolean", fcinfo->context);
 
 	PG_FREE_IF_COPY(in, 0);
 
@@ -2065,7 +2066,7 @@ jsonb_numeric(PG_FUNCTION_ARGS)
 	Numeric		retValue;
 
 	if (!JsonbExtractScalar(&in->root, &v))
-		cannotCastJsonbValue(v.type, "numeric");
+		return cannotCastJsonbValue(v.type, "numeric", fcinfo->context);
 
 	if (v.type == jbvNull)
 	{
@@ -2074,7 +2075,7 @@ jsonb_numeric(PG_FUNCTION_ARGS)
 	}
 
 	if (v.type != jbvNumeric)
-		cannotCastJsonbValue(v.type, "numeric");
+		return cannotCastJsonbValue(v.type, "numeric", fcinfo->context);
 
 	/*
 	 * v.val.numeric points into jsonb body, so we need to make a copy to
@@ -2095,7 +2096,7 @@ jsonb_int2(PG_FUNCTION_ARGS)
 	Datum		retValue;
 
 	if (!JsonbExtractScalar(&in->root, &v))
-		cannotCastJsonbValue(v.type, "smallint");
+		return cannotCastJsonbValue(v.type, "smallint", fcinfo->context);
 
 	if (v.type == jbvNull)
 	{
@@ -2104,7 +2105,7 @@ jsonb_int2(PG_FUNCTION_ARGS)
 	}
 
 	if (v.type != jbvNumeric)
-		cannotCastJsonbValue(v.type, "smallint");
+		return cannotCastJsonbValue(v.type, "smallint", fcinfo->context);
 
 	retValue = DirectFunctionCall1(numeric_int2,
 								   NumericGetDatum(v.val.numeric));
@@ -2122,7 +2123,7 @@ jsonb_int4(PG_FUNCTION_ARGS)
 	Datum		retValue;
 
 	if (!JsonbExtractScalar(&in->root, &v))
-		cannotCastJsonbValue(v.type, "integer");
+		return cannotCastJsonbValue(v.type, "integer", fcinfo->context);
 
 	if (v.type == jbvNull)
 	{
@@ -2131,7 +2132,7 @@ jsonb_int4(PG_FUNCTION_ARGS)
 	}
 
 	if (v.type != jbvNumeric)
-		cannotCastJsonbValue(v.type, "integer");
+		return cannotCastJsonbValue(v.type, "integer", fcinfo->context);
 
 	retValue = DirectFunctionCall1(numeric_int4,
 								   NumericGetDatum(v.val.numeric));
@@ -2149,7 +2150,7 @@ jsonb_int8(PG_FUNCTION_ARGS)
 	Datum		retValue;
 
 	if (!JsonbExtractScalar(&in->root, &v))
-		cannotCastJsonbValue(v.type, "bigint");
+		return cannotCastJsonbValue(v.type, "bigint", fcinfo->context);
 
 	if (v.type == jbvNull)
 	{
@@ -2158,7 +2159,7 @@ jsonb_int8(PG_FUNCTION_ARGS)
 	}
 
 	if (v.type != jbvNumeric)
-		cannotCastJsonbValue(v.type, "bigint");
+		return cannotCastJsonbValue(v.type, "bigint", fcinfo->context);
 
 	retValue = DirectFunctionCall1(numeric_int8,
 								   NumericGetDatum(v.val.numeric));
@@ -2176,7 +2177,7 @@ jsonb_float4(PG_FUNCTION_ARGS)
 	Datum		retValue;
 
 	if (!JsonbExtractScalar(&in->root, &v))
-		cannotCastJsonbValue(v.type, "real");
+		return cannotCastJsonbValue(v.type, "real", fcinfo->context);
 
 	if (v.type == jbvNull)
 	{
@@ -2185,7 +2186,7 @@ jsonb_float4(PG_FUNCTION_ARGS)
 	}
 
 	if (v.type != jbvNumeric)
-		cannotCastJsonbValue(v.type, "real");
+		return cannotCastJsonbValue(v.type, "real", fcinfo->context);
 
 	retValue = DirectFunctionCall1(numeric_float4,
 								   NumericGetDatum(v.val.numeric));
@@ -2203,7 +2204,7 @@ jsonb_float8(PG_FUNCTION_ARGS)
 	Datum		retValue;
 
 	if (!JsonbExtractScalar(&in->root, &v))
-		cannotCastJsonbValue(v.type, "double precision");
+		return cannotCastJsonbValue(v.type, "double precision", fcinfo->context);
 
 	if (v.type == jbvNull)
 	{
@@ -2212,7 +2213,7 @@ jsonb_float8(PG_FUNCTION_ARGS)
 	}
 
 	if (v.type != jbvNumeric)
-		cannotCastJsonbValue(v.type, "double precision");
+		return cannotCastJsonbValue(v.type, "double precision", fcinfo->context);
 
 	retValue = DirectFunctionCall1(numeric_float8,
 								   NumericGetDatum(v.val.numeric));
-- 
2.34.1



  [text/x-patch] v6-0006-error-safe-for-casting-float4-to-other-types-per-pg_cast.patch (2.5K, 13-v6-0006-error-safe-for-casting-float4-to-other-types-per-pg_cast.patch)
  download | inline diff:
From 60c9c8cac8cd24803ec7319eb19ff4cdd838cb72 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Sat, 9 Aug 2025 22:17:48 +0800
Subject: [PATCH v6 06/18] error safe for casting float4 to other types per
 pg_cast

select castsource::regtype, casttarget::regtype, castfunc,
castcontext,castmethod, pp.prosrc, pp.proname from pg_cast pc join pg_proc pp on
pp.oid = pc.castfunc and pc.castfunc > 0
and castsource::regtype = 'float4'::regtype
order by castsource::regtype;

 castsource |    casttarget    | castfunc | castcontext | castmethod |     prosrc     | proname
------------+------------------+----------+-------------+------------+----------------+---------
 real       | bigint           |      653 | a           | f          | ftoi8          | int8
 real       | smallint         |      238 | a           | f          | ftoi2          | int2
 real       | integer          |      319 | a           | f          | ftoi4          | int4
 real       | double precision |      311 | i           | f          | ftod           | float8
 real       | numeric          |     1742 | a           | f          | float4_numeric | numeric
(5 rows)

float4 to float8, numeric is error safe, so no need refactor ftod,
float4_numeric.
---
 src/backend/utils/adt/float.c | 4 ++--
 src/backend/utils/adt/int8.c  | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 7b97d2be6ca..ab5c38d723d 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -1298,7 +1298,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 +1323,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 96a6737a95c..8c38d02d011 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -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")));
 
-- 
2.34.1



  [text/x-patch] v6-0007-error-safe-for-casting-float8-to-other-types-per-pg_cast.patch (3.0K, 14-v6-0007-error-safe-for-casting-float8-to-other-types-per-pg_cast.patch)
  download | inline diff:
From 5b4d86921d852efda26e3483c85e92ca77581f9c Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Sat, 9 Aug 2025 22:26:13 +0800
Subject: [PATCH v6 07/18] error safe for casting float8 to other types per
 pg_cast

select castsource::regtype, casttarget::regtype, castfunc,
castcontext,castmethod, pp.prosrc, pp.proname from pg_cast pc join pg_proc pp on
pp.oid = pc.castfunc and pc.castfunc > 0
and castsource::regtype = 'float8'::regtype
order by castsource::regtype;

    castsource    | casttarget | castfunc | castcontext | castmethod |     prosrc     | proname
------------------+------------+----------+-------------+------------+----------------+---------
 double precision | bigint     |      483 | a           | f          | dtoi8          | int8
 double precision | smallint   |      237 | a           | f          | dtoi2          | int2
 double precision | integer    |      317 | a           | f          | dtoi4          | int4
 double precision | real       |      312 | a           | f          | dtof           | float4
 double precision | numeric    |     1743 | a           | f          | float8_numeric | numeric
(5 rows)
---
 src/backend/utils/adt/float.c | 12 ++++++++----
 src/backend/utils/adt/int8.c  |  2 +-
 2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index ab5c38d723d..6461e9c94b1 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")));
 
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index 8c38d02d011..5bddcccf378 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")));
 
-- 
2.34.1



  [text/x-patch] v6-0005-error-safe-for-casting-numeric-to-other-types-per-pg_cast.patch (5.1K, 15-v6-0005-error-safe-for-casting-numeric-to-other-types-per-pg_cast.patch)
  download | inline diff:
From 3738b36237e21a9341abea8a599e7ed7fa899307 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Sat, 9 Aug 2025 21:33:56 +0800
Subject: [PATCH v6 05/18] error safe for casting numeric to other types per
 pg_cast

select castsource::regtype, casttarget::regtype, castfunc,
castcontext,castmethod, pp.prosrc, pp.proname from pg_cast pc join pg_proc pp on
pp.oid = pc.castfunc and pc.castfunc > 0
and castsource::regtype = 'numeric'::regtype
order by castsource::regtype;

castsource |    casttarget    | castfunc | castcontext | castmethod |     prosrc     | proname
------------+------------------+----------+-------------+------------+----------------+---------
 numeric    | bigint           |     1779 | a           | f          | numeric_int8   | int8
 numeric    | smallint         |     1783 | a           | f          | numeric_int2   | int2
 numeric    | integer          |     1744 | a           | f          | numeric_int4   | int4
 numeric    | real             |     1745 | i           | f          | numeric_float4 | float4
 numeric    | double precision |     1746 | i           | f          | numeric_float8 | float8
 numeric    | money            |     3824 | a           | f          | numeric_cash   | money
 numeric    | numeric          |     1703 | i           | f          | numeric        | numeric
(7 rows)

current safe cast for money data type is not supported, so no realted function
refactoring.
---
 src/backend/utils/adt/numeric.c | 69 ++++++++++++++++++++++++++-------
 1 file changed, 56 insertions(+), 13 deletions(-)

diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 122f2efab8b..2ab26d59e41 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -1259,7 +1259,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));
 	}
 
@@ -1310,7 +1311,8 @@ numeric		(PG_FUNCTION_ARGS)
 	init_var(&var);
 
 	set_var_from_num(num, &var);
-	(void) apply_typmod(&var, typmod, NULL);
+	if (!apply_typmod(&var, typmod, fcinfo->context))
+		PG_RETURN_NULL();
 	new = make_result(&var);
 
 	free_var(&var);
@@ -4550,7 +4552,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))
+		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);
+	}
 }
 
 /*
@@ -4638,7 +4655,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))
+		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);
+	}
 }
 
 
@@ -4662,11 +4694,11 @@ numeric_int2(PG_FUNCTION_ARGS)
 	if (NUMERIC_IS_SPECIAL(num))
 	{
 		if (NUMERIC_IS_NAN(num))
-			ereport(ERROR,
+			ereturn(fcinfo->context, (Datum) 0,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("cannot convert NaN to %s", "smallint")));
 		else
-			ereport(ERROR,
+			ereturn(fcinfo->context, (Datum) 0,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("cannot convert infinity to %s", "smallint")));
 	}
@@ -4675,12 +4707,12 @@ numeric_int2(PG_FUNCTION_ARGS)
 	init_var_from_num(num, &x);
 
 	if (!numericvar_to_int64(&x, &val))
-		ereport(ERROR,
+		ereturn(fcinfo->context, (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(fcinfo->context, (Datum) 0,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("smallint out of range")));
 
@@ -4745,10 +4777,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);
 }
@@ -4840,7 +4876,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



  [text/x-patch] v6-0004-error-safe-for-casting-bigint-to-other-types-per-pg_cast.patch (3.8K, 16-v6-0004-error-safe-for-casting-bigint-to-other-types-per-pg_cast.patch)
  download | inline diff:
From 9ca7cb44e80f642f1e602ce55012bbbb6cca80f4 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Sat, 9 Aug 2025 21:25:38 +0800
Subject: [PATCH v6 04/18] error safe for casting bigint to other types per
 pg_cast

select castsource::regtype, casttarget::regtype, castfunc,
castcontext,castmethod, pp.prosrc, pp.proname from pg_cast pc join pg_proc pp on
pp.oid = pc.castfunc and pc.castfunc > 0
and castsource::regtype = 'bigint'::regtype
order by castsource::regtype;

 castsource |    casttarget    | castfunc | castcontext | castmethod |    prosrc    | proname
------------+------------------+----------+-------------+------------+--------------+---------
 bigint     | smallint         |      714 | a           | f          | int82        | int2
 bigint     | integer          |      480 | a           | f          | int84        | int4
 bigint     | real             |      652 | i           | f          | i8tof        | float4
 bigint     | double precision |      482 | i           | f          | i8tod        | float8
 bigint     | numeric          |     1781 | i           | f          | int8_numeric | numeric
 bigint     | money            |     3812 | a           | f          | int8_cash    | money
 bigint     | oid              |     1287 | i           | f          | i8tooid      | oid
 bigint     | regproc          |     1287 | i           | f          | i8tooid      | oid
 bigint     | regprocedure     |     1287 | i           | f          | i8tooid      | oid
 bigint     | regoper          |     1287 | i           | f          | i8tooid      | oid
 bigint     | regoperator      |     1287 | i           | f          | i8tooid      | oid
 bigint     | regclass         |     1287 | i           | f          | i8tooid      | oid
 bigint     | regcollation     |     1287 | i           | f          | i8tooid      | oid
 bigint     | regtype          |     1287 | i           | f          | i8tooid      | oid
 bigint     | regconfig        |     1287 | i           | f          | i8tooid      | oid
 bigint     | regdictionary    |     1287 | i           | f          | i8tooid      | oid
 bigint     | regrole          |     1287 | i           | f          | i8tooid      | oid
 bigint     | regnamespace     |     1287 | i           | f          | i8tooid      | oid
 bigint     | regdatabase      |     1287 | i           | f          | i8tooid      | oid
 bigint     | bytea            |     6369 | e           | f          | int8_bytea   | bytea
 bigint     | bit              |     2075 | e           | f          | bitfromint8  | bit
(21 rows)

already error safe: i8tof, i8tod, int8_numeric, int8_bytea, bitfromint8
---
 src/backend/utils/adt/int8.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index 9dd5889f34c..96a6737a95c 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -1251,7 +1251,7 @@ int84(PG_FUNCTION_ARGS)
 	int64		arg = PG_GETARG_INT64(0);
 
 	if (unlikely(arg < PG_INT32_MIN) || unlikely(arg > PG_INT32_MAX))
-		ereport(ERROR,
+		ereturn(fcinfo->context, (Datum) 0,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("integer out of range")));
 
@@ -1272,7 +1272,7 @@ int82(PG_FUNCTION_ARGS)
 	int64		arg = PG_GETARG_INT64(0);
 
 	if (unlikely(arg < PG_INT16_MIN) || unlikely(arg > PG_INT16_MAX))
-		ereport(ERROR,
+		ereturn(fcinfo->context, (Datum) 0,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("smallint out of range")));
 
@@ -1355,7 +1355,7 @@ i8tooid(PG_FUNCTION_ARGS)
 	int64		arg = PG_GETARG_INT64(0);
 
 	if (unlikely(arg < 0) || unlikely(arg > PG_UINT32_MAX))
-		ereport(ERROR,
+		ereturn(fcinfo->context, (Datum) 0,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("OID out of range")));
 
-- 
2.34.1



  [text/x-patch] v6-0003-error-safe-for-casting-integer-to-other-types-per-pg_cast.patch (2.8K, 17-v6-0003-error-safe-for-casting-integer-to-other-types-per-pg_cast.patch)
  download | inline diff:
From bb6577b6491b0cd78760c068fc26521437662513 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Sat, 9 Aug 2025 21:20:32 +0800
Subject: [PATCH v6 03/18] error safe for casting integer to other types per
 pg_cast

select castsource::regtype, casttarget::regtype, castfunc,
castcontext,castmethod, pp.prosrc, pp.proname from pg_cast pc join pg_proc pp on
pp.oid = pc.castfunc and pc.castfunc > 0
and castsource::regtype = 'integer'::regtype
order by castsource::regtype;

 castsource |    casttarget    | castfunc | castcontext | castmethod |    prosrc    | proname
------------+------------------+----------+-------------+------------+--------------+---------
 integer    | bigint           |      481 | i           | f          | int48        | int8
 integer    | smallint         |      314 | a           | f          | i4toi2       | int2
 integer    | real             |      318 | i           | f          | i4tof        | float4
 integer    | double precision |      316 | i           | f          | i4tod        | float8
 integer    | numeric          |     1740 | i           | f          | int4_numeric | numeric
 integer    | money            |     3811 | a           | f          | int4_cash    | money
 integer    | boolean          |     2557 | e           | f          | int4_bool    | bool
 integer    | bytea            |     6368 | e           | f          | int4_bytea   | bytea
 integer    | "char"           |       78 | e           | f          | i4tochar     | char
 integer    | bit              |     1683 | e           | f          | bitfromint4  | bit
(10 rows)

only int4_cash, i4toi2, i4tochar need take care of error handling.  but support
for cash data type is not easy, so only i4toi2, i4tochar function refactoring.
---
 src/backend/utils/adt/char.c | 2 +-
 src/backend/utils/adt/int.c  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/backend/utils/adt/char.c b/src/backend/utils/adt/char.c
index 22dbfc950b1..e90844a29f0 100644
--- a/src/backend/utils/adt/char.c
+++ b/src/backend/utils/adt/char.c
@@ -192,7 +192,7 @@ i4tochar(PG_FUNCTION_ARGS)
 	int32		arg1 = PG_GETARG_INT32(0);
 
 	if (arg1 < SCHAR_MIN || arg1 > SCHAR_MAX)
-		ereport(ERROR,
+		ereturn(fcinfo->context, (Datum) 0,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("\"char\" out of range")));
 
diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c
index b5781989a64..b45599d402d 100644
--- a/src/backend/utils/adt/int.c
+++ b/src/backend/utils/adt/int.c
@@ -350,7 +350,7 @@ i4toi2(PG_FUNCTION_ARGS)
 	int32		arg1 = PG_GETARG_INT32(0);
 
 	if (unlikely(arg1 < SHRT_MIN) || unlikely(arg1 > SHRT_MAX))
-		ereport(ERROR,
+		ereturn(fcinfo->context, (Datum) 0,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("smallint out of range")));
 
-- 
2.34.1



  [text/x-patch] v6-0001-error-safe-for-casting-bytea-to-other-types-per-pg_cast.patch (2.0K, 18-v6-0001-error-safe-for-casting-bytea-to-other-types-per-pg_cast.patch)
  download | inline diff:
From efc37e956c0af2d0a255b8f047f68d8b67784761 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Mon, 4 Aug 2025 20:11:53 +0800
Subject: [PATCH v6 01/18] error safe for casting bytea to other types per
 pg_cast

select castsource::regtype, casttarget::regtype, castfunc, castcontext,castmethod, pp.prosrc, pp.proname
from pg_cast pc join pg_proc pp on pp.oid = pc.castfunc and pc.castfunc > 0 and castsource::regtype ='bytea'::regtype
order by castsource::regtype;

 castsource | casttarget | castfunc | castcontext | castmethod |   prosrc   | proname
------------+------------+----------+-------------+------------+------------+---------
 bytea      | smallint   |     6370 | e           | f          | bytea_int2 | int2
 bytea      | integer    |     6371 | e           | f          | bytea_int4 | int4
 bytea      | bigint     |     6372 | e           | f          | bytea_int8 | int8
(3 rows)
---
 src/backend/utils/adt/bytea.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/backend/utils/adt/bytea.c b/src/backend/utils/adt/bytea.c
index 6e7b914c563..f43ad6b4aff 100644
--- a/src/backend/utils/adt/bytea.c
+++ b/src/backend/utils/adt/bytea.c
@@ -1027,7 +1027,7 @@ bytea_int2(PG_FUNCTION_ARGS)
 
 	/* Check that the byte array is not too long */
 	if (len > sizeof(result))
-		ereport(ERROR,
+		ereturn(fcinfo->context, (Datum) 0,
 				errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				errmsg("smallint out of range"));
 
@@ -1052,7 +1052,7 @@ bytea_int4(PG_FUNCTION_ARGS)
 
 	/* Check that the byte array is not too long */
 	if (len > sizeof(result))
-		ereport(ERROR,
+		ereturn(fcinfo->context, (Datum) 0,
 				errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				errmsg("integer out of range"));
 
@@ -1077,7 +1077,7 @@ bytea_int8(PG_FUNCTION_ARGS)
 
 	/* Check that the byte array is not too long */
 	if (len > sizeof(result))
-		ereport(ERROR,
+		ereturn(fcinfo->context, (Datum) 0,
 				errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				errmsg("bigint out of range"));
 
-- 
2.34.1



  [text/x-patch] v6-0002-error-safe-for-casting-character-to-other-types-per-pg_ca.patch (4.3K, 19-v6-0002-error-safe-for-casting-character-to-other-types-per-pg_ca.patch)
  download | inline diff:
From 4d0fc95b1fc9690b7a631b98d3d0f392a2a293bd Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Sat, 9 Aug 2025 17:49:04 +0800
Subject: [PATCH v6 02/18] error safe for casting character to other types per
 pg_cast

select castsource::regtype, casttarget::regtype, castfunc,
castcontext,castmethod, pp.prosrc, pp.proname from pg_cast pc join pg_proc pp on
pp.oid = pc.castfunc and pc.castfunc > 0
and castsource::regtype ='character'::regtype
order by castsource::regtype;

 castsource |    casttarget     | castfunc | castcontext | castmethod |   prosrc    | proname
------------+-------------------+----------+-------------+------------+-------------+---------
 character  | text              |      401 | i           | f          | rtrim1      | text
 character  | character varying |      401 | i           | f          | rtrim1      | text
 character  | "char"            |      944 | a           | f          | text_char   | char
 character  | name              |      409 | i           | f          | bpchar_name | name
 character  | xml               |     2896 | e           | f          | texttoxml   | xml
 character  | character         |      668 | i           | f          | bpchar      | bpchar
(6 rows)

only texttoxml, bpchar(PG_FUNCTION_ARGS) need take care of error handling.
other already error safe.
---
 src/backend/executor/execExprInterp.c |  2 +-
 src/backend/utils/adt/varchar.c       |  2 +-
 src/backend/utils/adt/xml.c           | 12 ++++++++----
 src/include/utils/xml.h               |  2 +-
 4 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 0e1a74976f7..67f4e00eac4 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -4542,7 +4542,7 @@ ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op)
 
 				*op->resvalue = PointerGetDatum(xmlparse(data,
 														 xexpr->xmloption,
-														 preserve_whitespace));
+														 preserve_whitespace, NULL));
 				*op->resnull = false;
 			}
 			break;
diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c
index 3f40c9da1a0..5cb5c8c46f9 100644
--- a/src/backend/utils/adt/varchar.c
+++ b/src/backend/utils/adt/varchar.c
@@ -307,7 +307,7 @@ bpchar(PG_FUNCTION_ARGS)
 		{
 			for (i = maxmblen; i < len; i++)
 				if (s[i] != ' ')
-					ereport(ERROR,
+					ereturn(fcinfo->context, (Datum) 0,
 							(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
 							 errmsg("value too long for type character(%d)",
 									maxlen)));
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 182e8f75db7..1d423e0f9a3 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -660,7 +660,7 @@ texttoxml(PG_FUNCTION_ARGS)
 {
 	text	   *data = PG_GETARG_TEXT_PP(0);
 
-	PG_RETURN_XML_P(xmlparse(data, xmloption, true));
+	PG_RETURN_XML_P(xmlparse(data, xmloption, true, fcinfo->context));
 }
 
 
@@ -1029,14 +1029,18 @@ xmlelement(XmlExpr *xexpr,
 
 
 xmltype *
-xmlparse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace)
+xmlparse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace, Node *escontext)
 {
 #ifdef USE_LIBXML
 	xmlDocPtr	doc;
 
 	doc = xml_parse(data, xmloption_arg, preserve_whitespace,
-					GetDatabaseEncoding(), NULL, NULL, NULL);
-	xmlFreeDoc(doc);
+					GetDatabaseEncoding(), NULL, NULL, escontext);
+	if (doc)
+		xmlFreeDoc(doc);
+
+	if (SOFT_ERROR_OCCURRED(escontext))
+		return NULL;
 
 	return (xmltype *) data;
 #else
diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h
index 0d7a816b9f9..b3d3f11fac4 100644
--- a/src/include/utils/xml.h
+++ b/src/include/utils/xml.h
@@ -73,7 +73,7 @@ extern xmltype *xmlconcat(List *args);
 extern xmltype *xmlelement(XmlExpr *xexpr,
 						   Datum *named_argvalue, bool *named_argnull,
 						   Datum *argvalue, bool *argnull);
-extern xmltype *xmlparse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace);
+extern xmltype *xmlparse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace, Node *escontext);
 extern xmltype *xmlpi(const char *target, text *arg, bool arg_is_null, bool *result_is_null);
 extern xmltype *xmlroot(xmltype *data, text *version, int standalone);
 extern bool xml_is_document(xmltype *arg);
-- 
2.34.1



view thread (75+ messages)  latest in thread

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: [email protected]
  Cc: [email protected], [email protected], [email protected], [email protected], [email protected]
  Subject: Re: CAST(... ON DEFAULT) - WIP build on top of Error-Safe User Functions
  In-Reply-To: <CACJufxE053=bO3pDUpGba6Yz3VGpU_XCbg4HO6Rew5EJ7k7VnQ@mail.gmail.com>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox