v15-0022-error-safe-for-user-defined-CREATE-CAST.patch
text/x-patch
Filename: v15-0022-error-safe-for-user-defined-CREATE-CAST.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v15-0022
Subject: error safe for user defined CREATE CAST
| File | + | − |
|---|---|---|
| contrib/citext/citext--1.4.sql | 3 | 3 |
| contrib/citext/expected/citext_1.out | 19 | 5 |
| contrib/citext/expected/citext.out | 19 | 5 |
| contrib/citext/sql/citext.sql | 4 | 1 |
| contrib/hstore/expected/hstore.out | 37 | 0 |
| contrib/hstore/hstore--1.2--1.3.sql | 1 | 1 |
| contrib/hstore/hstore--1.4.sql | 3 | 3 |
| contrib/hstore/hstore_io.c | 4 | 4 |
| contrib/hstore/sql/hstore.sql | 11 | 0 |
| doc/src/sgml/catalogs.sgml | 15 | 0 |
| doc/src/sgml/ref/create_cast.sgml | 13 | 1 |
| doc/src/sgml/syntax.sgml | 1 | 2 |
| src/backend/catalog/pg_cast.c | 3 | 1 |
| src/backend/commands/functioncmds.c | 8 | 1 |
| src/backend/commands/typecmds.c | 1 | 0 |
| src/backend/parser/gram.y | 13 | 5 |
| src/backend/parser/parse_expr.c | 2 | 8 |
| src/include/catalog/pg_cast.dat | 165 | 165 |
| src/include/catalog/pg_cast.h | 5 | 0 |
| src/include/nodes/parsenodes.h | 1 | 0 |
| src/include/parser/kwlist.h | 1 | 0 |
| src/test/regress/expected/create_cast.out | 7 | 1 |
| src/test/regress/expected/opr_sanity.out | 12 | 12 |
| src/test/regress/sql/create_cast.sql | 5 | 0 |
From 99d299237edcb65151a94b7ab17497dad6d7b778 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Wed, 10 Dec 2025 16:15:37 +0800
Subject: [PATCH v15 22/22] error safe for user defined CREATE CAST
pg_cast.casterrorsafe column to indicate castfunc is error safe or not.
change src/include/catalog/pg_cast.dat to indicate that most of the system cast
function support soft error evaluation.
The SAFE keyword is introduced for allow user-defined CREATE CAST can also be
evaluated in soft-error. now the synopsis of CREATE CAST is:
CREATE CAST (source_type AS target_type)
WITH [SAFE] FUNCTION function_name [ (argument_type [, ...]) ]
[ AS ASSIGNMENT | AS IMPLICIT ]
The following cast in citext, hstore module refactored to error safe:
CAST (bpchar AS citext)
CAST (boolean AS citext)
CAST (inet AS citext)
CAST (text[] AS hstore)
CAST (hstore AS json)
CAST (hstore AS jsonb)
discussion: https://postgr.es/m/CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@mail.gmail.com
---
contrib/citext/citext--1.4.sql | 6 +-
contrib/citext/expected/citext.out | 24 +-
contrib/citext/expected/citext_1.out | 24 +-
contrib/citext/sql/citext.sql | 5 +-
contrib/hstore/expected/hstore.out | 37 +++
contrib/hstore/hstore--1.2--1.3.sql | 2 +-
contrib/hstore/hstore--1.4.sql | 6 +-
contrib/hstore/hstore_io.c | 8 +-
contrib/hstore/sql/hstore.sql | 11 +
doc/src/sgml/catalogs.sgml | 15 +
doc/src/sgml/ref/create_cast.sgml | 14 +-
doc/src/sgml/syntax.sgml | 3 +-
src/backend/catalog/pg_cast.c | 4 +-
src/backend/commands/functioncmds.c | 9 +-
src/backend/commands/typecmds.c | 1 +
src/backend/parser/gram.y | 18 +-
src/backend/parser/parse_expr.c | 10 +-
src/include/catalog/pg_cast.dat | 330 +++++++++++-----------
src/include/catalog/pg_cast.h | 5 +
src/include/nodes/parsenodes.h | 1 +
src/include/parser/kwlist.h | 1 +
src/test/regress/expected/create_cast.out | 8 +-
src/test/regress/expected/opr_sanity.out | 24 +-
src/test/regress/sql/create_cast.sql | 5 +
24 files changed, 353 insertions(+), 218 deletions(-)
diff --git a/contrib/citext/citext--1.4.sql b/contrib/citext/citext--1.4.sql
index 7b061989352..5c87820388f 100644
--- a/contrib/citext/citext--1.4.sql
+++ b/contrib/citext/citext--1.4.sql
@@ -85,9 +85,9 @@ CREATE CAST (citext AS varchar) WITHOUT FUNCTION AS IMPLICIT;
CREATE CAST (citext AS bpchar) WITHOUT FUNCTION AS ASSIGNMENT;
CREATE CAST (text AS citext) WITHOUT FUNCTION AS ASSIGNMENT;
CREATE CAST (varchar AS citext) WITHOUT FUNCTION AS ASSIGNMENT;
-CREATE CAST (bpchar AS citext) WITH FUNCTION citext(bpchar) AS ASSIGNMENT;
-CREATE CAST (boolean AS citext) WITH FUNCTION citext(boolean) AS ASSIGNMENT;
-CREATE CAST (inet AS citext) WITH FUNCTION citext(inet) AS ASSIGNMENT;
+CREATE CAST (bpchar AS citext) WITH SAFE FUNCTION citext(bpchar) AS ASSIGNMENT;
+CREATE CAST (boolean AS citext) WITH SAFE FUNCTION citext(boolean) AS ASSIGNMENT;
+CREATE CAST (inet AS citext) WITH SAFE FUNCTION citext(inet) AS ASSIGNMENT;
--
-- Operator Functions.
diff --git a/contrib/citext/expected/citext.out b/contrib/citext/expected/citext.out
index 33da19d8df4..be328715492 100644
--- a/contrib/citext/expected/citext.out
+++ b/contrib/citext/expected/citext.out
@@ -10,11 +10,12 @@ WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid);
--------+---------
(0 rows)
-SELECT CAST('abc'::bpchar AS citext DEFAULT NULL ON CONVERSION ERROR); --error
-ERROR: cannot cast type character to citext when DEFAULT expression is specified in CAST ... ON CONVERSION ERROR
-LINE 1: SELECT CAST('abc'::bpchar AS citext DEFAULT NULL ON CONVERSI...
- ^
-HINT: Safe type cast for user-defined types are not yet supported
+SELECT CAST('abc'::bpchar AS citext DEFAULT NULL ON CONVERSION ERROR);
+ citext
+--------
+ abc
+(1 row)
+
-- Test the operators and indexing functions
-- Test = and <>.
SELECT 'a'::citext = 'a'::citext AS t;
@@ -523,6 +524,12 @@ SELECT true::citext = 'true' AS t;
t
(1 row)
+SELECT CAST(true AS citext DEFAULT NULL ON CONVERSION ERROR);
+ citext
+--------
+ true
+(1 row)
+
SELECT 'true'::citext::boolean = true AS t;
t
---
@@ -787,6 +794,13 @@ SELECT '192.168.100.128'::citext::inet = '192.168.100.128'::inet AS t;
t
(1 row)
+SELECT CAST(inet '192.168.100.128' AS citext
+ DEFAULT NULL ON CONVERSION ERROR) = '192.168.100.128/32' AS t;
+ t
+---
+ t
+(1 row)
+
SELECT '08:00:2b:01:02:03'::macaddr::citext = '08:00:2b:01:02:03' AS t;
t
---
diff --git a/contrib/citext/expected/citext_1.out b/contrib/citext/expected/citext_1.out
index 647eea19142..e9f8454c662 100644
--- a/contrib/citext/expected/citext_1.out
+++ b/contrib/citext/expected/citext_1.out
@@ -10,11 +10,12 @@ WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid);
--------+---------
(0 rows)
-SELECT CAST('abc'::bpchar AS citext DEFAULT NULL ON CONVERSION ERROR); --error
-ERROR: cannot cast type character to citext when DEFAULT expression is specified in CAST ... ON CONVERSION ERROR
-LINE 1: SELECT CAST('abc'::bpchar AS citext DEFAULT NULL ON CONVERSI...
- ^
-HINT: Safe type cast for user-defined types are not yet supported
+SELECT CAST('abc'::bpchar AS citext DEFAULT NULL ON CONVERSION ERROR);
+ citext
+--------
+ abc
+(1 row)
+
-- Test the operators and indexing functions
-- Test = and <>.
SELECT 'a'::citext = 'a'::citext AS t;
@@ -523,6 +524,12 @@ SELECT true::citext = 'true' AS t;
t
(1 row)
+SELECT CAST(true AS citext DEFAULT NULL ON CONVERSION ERROR);
+ citext
+--------
+ true
+(1 row)
+
SELECT 'true'::citext::boolean = true AS t;
t
---
@@ -787,6 +794,13 @@ SELECT '192.168.100.128'::citext::inet = '192.168.100.128'::inet AS t;
t
(1 row)
+SELECT CAST(inet '192.168.100.128' AS citext
+ DEFAULT NULL ON CONVERSION ERROR) = '192.168.100.128/32' AS t;
+ t
+---
+ t
+(1 row)
+
SELECT '08:00:2b:01:02:03'::macaddr::citext = '08:00:2b:01:02:03' AS t;
t
---
diff --git a/contrib/citext/sql/citext.sql b/contrib/citext/sql/citext.sql
index 99794497d47..62f83d749f9 100644
--- a/contrib/citext/sql/citext.sql
+++ b/contrib/citext/sql/citext.sql
@@ -9,7 +9,7 @@ SELECT amname, opcname
FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod
WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid);
-SELECT CAST('abc'::bpchar AS citext DEFAULT NULL ON CONVERSION ERROR); --error
+SELECT CAST('abc'::bpchar AS citext DEFAULT NULL ON CONVERSION ERROR);
-- Test the operators and indexing functions
@@ -165,6 +165,7 @@ SELECT name FROM srt WHERE name SIMILAR TO '%A.*';
-- Explicit casts.
SELECT true::citext = 'true' AS t;
+SELECT CAST(true AS citext DEFAULT NULL ON CONVERSION ERROR);
SELECT 'true'::citext::boolean = true AS t;
SELECT 4::citext = '4' AS t;
@@ -224,6 +225,8 @@ SELECT '192.168.100.128/25'::citext::cidr = '192.168.100.128/25'::cidr AS t;
SELECT '192.168.100.128'::inet::citext = '192.168.100.128/32' AS t;
SELECT '192.168.100.128'::citext::inet = '192.168.100.128'::inet AS t;
+SELECT CAST(inet '192.168.100.128' AS citext
+ DEFAULT NULL ON CONVERSION ERROR) = '192.168.100.128/32' AS t;
SELECT '08:00:2b:01:02:03'::macaddr::citext = '08:00:2b:01:02:03' AS t;
SELECT '08:00:2b:01:02:03'::citext::macaddr = '08:00:2b:01:02:03'::macaddr AS t;
diff --git a/contrib/hstore/expected/hstore.out b/contrib/hstore/expected/hstore.out
index 1836c9acf39..2622137cbf9 100644
--- a/contrib/hstore/expected/hstore.out
+++ b/contrib/hstore/expected/hstore.out
@@ -841,12 +841,28 @@ select '{}'::text[]::hstore;
select ARRAY['a','g','b','h','asd']::hstore;
ERROR: array must have even number of elements
+select CAST(ARRAY['a','g','b','h','asd'] AS hstore
+ DEFAULT NULL ON CONVERSION ERROR);
+ array
+-------
+
+(1 row)
+
select ARRAY['a','g','b','h','asd','i']::hstore;
array
--------------------------------
"a"=>"g", "b"=>"h", "asd"=>"i"
(1 row)
+select ARRAY['a','g','b','h',null,'i']::hstore;
+ERROR: null value not allowed for hstore key
+select CAST(ARRAY['a','g','b','h',null,'i'] AS hstore
+ DEFAULT NULL ON CONVERSION ERROR);
+ array
+-------
+
+(1 row)
+
select ARRAY[['a','g'],['b','h'],['asd','i']]::hstore;
array
--------------------------------
@@ -855,6 +871,13 @@ select ARRAY[['a','g'],['b','h'],['asd','i']]::hstore;
select ARRAY[['a','g','b'],['h','asd','i']]::hstore;
ERROR: array must have two columns
+select CAST(ARRAY[['a','g','b'],['h','asd','i']] AS hstore
+ DEFAULT NULL ON CONVERSION ERROR);
+ array
+-------
+
+(1 row)
+
select ARRAY[[['a','g'],['b','h'],['asd','i']]]::hstore;
ERROR: wrong number of array subscripts
select hstore('{}'::text[]);
@@ -1553,6 +1576,13 @@ select cast( hstore '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=
{"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}
(1 row)
+select cast( hstore '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as json
+ default null on conversion error);
+ json
+-------------------------------------------------------------------------------------------------
+ {"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}
+(1 row)
+
select hstore_to_json_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4, h=> "2016-01-01"');
hstore_to_json_loose
-------------------------------------------------------------------------------------------------------------
@@ -1571,6 +1601,13 @@ select cast( hstore '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=
{"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}
(1 row)
+select cast( hstore '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as jsonb
+ default null on conversion error);
+ jsonb
+-------------------------------------------------------------------------------------------------
+ {"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}
+(1 row)
+
select hstore_to_jsonb_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4, h=> "2016-01-01"');
hstore_to_jsonb_loose
----------------------------------------------------------------------------------------------------------
diff --git a/contrib/hstore/hstore--1.2--1.3.sql b/contrib/hstore/hstore--1.2--1.3.sql
index 0a7056015b7..cbb1a139e69 100644
--- a/contrib/hstore/hstore--1.2--1.3.sql
+++ b/contrib/hstore/hstore--1.2--1.3.sql
@@ -9,7 +9,7 @@ AS 'MODULE_PATHNAME', 'hstore_to_jsonb'
LANGUAGE C IMMUTABLE STRICT;
CREATE CAST (hstore AS jsonb)
- WITH FUNCTION hstore_to_jsonb(hstore);
+ WITH SAFE FUNCTION hstore_to_jsonb(hstore);
CREATE FUNCTION hstore_to_jsonb_loose(hstore)
RETURNS jsonb
diff --git a/contrib/hstore/hstore--1.4.sql b/contrib/hstore/hstore--1.4.sql
index 4294d14ceb5..451c2ed8187 100644
--- a/contrib/hstore/hstore--1.4.sql
+++ b/contrib/hstore/hstore--1.4.sql
@@ -232,7 +232,7 @@ AS 'MODULE_PATHNAME', 'hstore_from_array'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE CAST (text[] AS hstore)
- WITH FUNCTION hstore(text[]);
+ WITH SAFE FUNCTION hstore(text[]);
CREATE FUNCTION hstore_to_json(hstore)
RETURNS json
@@ -240,7 +240,7 @@ AS 'MODULE_PATHNAME', 'hstore_to_json'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE CAST (hstore AS json)
- WITH FUNCTION hstore_to_json(hstore);
+ WITH SAFE FUNCTION hstore_to_json(hstore);
CREATE FUNCTION hstore_to_json_loose(hstore)
RETURNS json
@@ -253,7 +253,7 @@ AS 'MODULE_PATHNAME', 'hstore_to_jsonb'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE CAST (hstore AS jsonb)
- WITH FUNCTION hstore_to_jsonb(hstore);
+ WITH SAFE FUNCTION hstore_to_jsonb(hstore);
CREATE FUNCTION hstore_to_jsonb_loose(hstore)
RETURNS jsonb
diff --git a/contrib/hstore/hstore_io.c b/contrib/hstore/hstore_io.c
index 34e3918811c..f5166679783 100644
--- a/contrib/hstore/hstore_io.c
+++ b/contrib/hstore/hstore_io.c
@@ -738,20 +738,20 @@ hstore_from_array(PG_FUNCTION_ARGS)
case 1:
if ((ARR_DIMS(in_array)[0]) % 2)
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("array must have even number of elements")));
break;
case 2:
if ((ARR_DIMS(in_array)[1]) != 2)
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("array must have two columns")));
break;
default:
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("wrong number of array subscripts")));
}
@@ -772,7 +772,7 @@ hstore_from_array(PG_FUNCTION_ARGS)
for (i = 0; i < count; ++i)
{
if (in_nulls[i * 2])
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("null value not allowed for hstore key")));
diff --git a/contrib/hstore/sql/hstore.sql b/contrib/hstore/sql/hstore.sql
index efef91292a3..8fa46630d6d 100644
--- a/contrib/hstore/sql/hstore.sql
+++ b/contrib/hstore/sql/hstore.sql
@@ -192,9 +192,16 @@ select pg_column_size(slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b','aa']))
-- array input
select '{}'::text[]::hstore;
select ARRAY['a','g','b','h','asd']::hstore;
+select CAST(ARRAY['a','g','b','h','asd'] AS hstore
+ DEFAULT NULL ON CONVERSION ERROR);
select ARRAY['a','g','b','h','asd','i']::hstore;
+select ARRAY['a','g','b','h',null,'i']::hstore;
+select CAST(ARRAY['a','g','b','h',null,'i'] AS hstore
+ DEFAULT NULL ON CONVERSION ERROR);
select ARRAY[['a','g'],['b','h'],['asd','i']]::hstore;
select ARRAY[['a','g','b'],['h','asd','i']]::hstore;
+select CAST(ARRAY[['a','g','b'],['h','asd','i']] AS hstore
+ DEFAULT NULL ON CONVERSION ERROR);
select ARRAY[[['a','g'],['b','h'],['asd','i']]]::hstore;
select hstore('{}'::text[]);
select hstore(ARRAY['a','g','b','h','asd']);
@@ -363,10 +370,14 @@ select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexe
-- json and jsonb
select hstore_to_json('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
select cast( hstore '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as json);
+select cast( hstore '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as json
+ default null on conversion error);
select hstore_to_json_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4, h=> "2016-01-01"');
select hstore_to_jsonb('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
select cast( hstore '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as jsonb);
+select cast( hstore '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as jsonb
+ default null on conversion error);
select hstore_to_jsonb_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4, h=> "2016-01-01"');
create table test_json_agg (f1 text, f2 hstore);
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 2fc63442980..8fca3534f32 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1849,6 +1849,21 @@ SCRAM-SHA-256$<replaceable><iteration count></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 flag indicates whether the <structfield>castfunc</structfield> function
+ is error-safe. It is meaningful only when <structfield>castfunc</structfield>
+ is not zero. User-defined casts can set it
+ to <literal>true</literal> via <link linkend="sql-createcast">CREATE CAST</link>.
+ For error-safe type cast, see <xref linkend="sql-syntax-type-casts-safe"/>.
+ </para>
+ </entry>
+ </row>
+
</tbody>
</tgroup>
</table>
diff --git a/doc/src/sgml/ref/create_cast.sgml b/doc/src/sgml/ref/create_cast.sgml
index bad75bc1dce..888d7142e42 100644
--- a/doc/src/sgml/ref/create_cast.sgml
+++ b/doc/src/sgml/ref/create_cast.sgml
@@ -22,7 +22,7 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
CREATE CAST (<replaceable>source_type</replaceable> AS <replaceable>target_type</replaceable>)
- WITH FUNCTION <replaceable>function_name</replaceable> [ (<replaceable>argument_type</replaceable> [, ...]) ]
+ WITH [SAFE] FUNCTION <replaceable>function_name</replaceable> [ (<replaceable>argument_type</replaceable> [, ...]) ]
[ AS ASSIGNMENT | AS IMPLICIT ]
CREATE CAST (<replaceable>source_type</replaceable> AS <replaceable>target_type</replaceable>)
@@ -194,6 +194,18 @@ SELECT CAST ( 2 AS numeric ) + 4.0;
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>SAFE</literal></term>
+ <listitem>
+ <para>
+ The function used to perform the cast support soft-error evaluation,
+ Currently, only functions written in C or the internal language are supported.
+ An alternate expression can be specified to be evaluated if the cast
+ error occurs. See <link linkend="sql-syntax-type-casts-safe">safe type cast</link>.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal><replaceable>function_name</replaceable>[(<replaceable>argument_type</replaceable> [, ...])]</literal></term>
diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml
index 32af9ea061c..24d1dc6de0b 100644
--- a/doc/src/sgml/syntax.sgml
+++ b/doc/src/sgml/syntax.sgml
@@ -2176,8 +2176,7 @@ CAST ( <replaceable>expression</replaceable> AS <replaceable>type</replaceable>
</synopsis>
If the type cast fails, instead of error out, evaluation falls back to the
default <replaceable>expression</replaceable> specified in the <literal>ON ERROR</literal> clause.
- At present, this only support built-in type casts; see <xref linkend="catalog-pg-cast"/>.
- User-defined type casts created with <link linkend="sql-createcast">CREATE CAST</link> are not supported.
+ User-defined type casts created with <link linkend="sql-createcast">CREATE CAST</link> are supported too.
</para>
<para>
diff --git a/src/backend/catalog/pg_cast.c b/src/backend/catalog/pg_cast.c
index 1773c9c5491..4116b1708b0 100644
--- a/src/backend/catalog/pg_cast.c
+++ b/src/backend/catalog/pg_cast.c
@@ -48,7 +48,8 @@
ObjectAddress
CastCreate(Oid sourcetypeid, Oid targettypeid,
Oid funcid, Oid incastid, Oid outcastid,
- char castcontext, char castmethod, DependencyType behavior)
+ char castcontext, char castmethod, bool casterrorsafe,
+ DependencyType behavior)
{
Relation relation;
HeapTuple tuple;
@@ -84,6 +85,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(casterrorsafe);
tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 59d00638ee6..2f7bdf26d37 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -1667,6 +1667,13 @@ CreateCast(CreateCastStmt *stmt)
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("cast function must not return a set")));
+ if (stmt->safe &&
+ procstruct->prolang != INTERNALlanguageId &&
+ procstruct->prolang != ClanguageId)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("Safe type cast functions are only supported for C and internal languages"));
+
ReleaseSysCache(tuple);
}
else
@@ -1795,7 +1802,7 @@ CreateCast(CreateCastStmt *stmt)
}
myself = CastCreate(sourcetypeid, targettypeid, funcid, incastid, outcastid,
- castcontext, castmethod, DEPENDENCY_NORMAL);
+ castcontext, castmethod, stmt->safe, DEPENDENCY_NORMAL);
return myself;
}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 47d5047fe8b..6fce17ce02b 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1754,6 +1754,7 @@ DefineRange(ParseState *pstate, CreateRangeStmt *stmt)
/* Create cast from the range type to its multirange type */
CastCreate(typoid, multirangeOid, castFuncOid, InvalidOid, InvalidOid,
COERCION_CODE_EXPLICIT, COERCION_METHOD_FUNCTION,
+ false,
DEPENDENCY_INTERNAL);
pfree(multirangeArrayName);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8a27e045bc0..e7ff7e5c275 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -353,7 +353,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <defelt> drop_option
%type <boolean> opt_or_replace opt_no
opt_grant_grant_option
- opt_nowait opt_if_exists opt_with_data
+ opt_nowait opt_safe opt_if_exists opt_with_data
opt_transaction_chain
%type <list> grant_role_opt_list
%type <defelt> grant_role_opt
@@ -774,7 +774,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
RESET RESPECT_P RESTART RESTRICT RETURN RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP
ROUTINE ROUTINES ROW ROWS RULE
- SAVEPOINT SCALAR SCHEMA SCHEMAS SCROLL SEARCH SECOND_P SECURITY SELECT
+ SAFE SAVEPOINT SCALAR SCHEMA SCHEMAS SCROLL SEARCH SECOND_P SECURITY SELECT
SEQUENCE SEQUENCES
SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHOW
SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SOURCE SQL_P STABLE STANDALONE_P
@@ -9291,14 +9291,15 @@ dostmt_opt_item:
*****************************************************************************/
CreateCastStmt: CREATE CAST '(' Typename AS Typename ')'
- WITH FUNCTION function_with_argtypes cast_context
+ WITH opt_safe FUNCTION function_with_argtypes cast_context
{
CreateCastStmt *n = makeNode(CreateCastStmt);
n->sourcetype = $4;
n->targettype = $6;
- n->func = $10;
- n->context = (CoercionContext) $11;
+ n->safe = $9;
+ n->func = $11;
+ n->context = (CoercionContext) $12;
n->inout = false;
$$ = (Node *) n;
}
@@ -9309,6 +9310,7 @@ CreateCastStmt: CREATE CAST '(' Typename AS Typename ')'
n->sourcetype = $4;
n->targettype = $6;
+ n->safe = false;
n->func = NULL;
n->context = (CoercionContext) $10;
n->inout = false;
@@ -9321,6 +9323,7 @@ CreateCastStmt: CREATE CAST '(' Typename AS Typename ')'
n->sourcetype = $4;
n->targettype = $6;
+ n->safe = false;
n->func = NULL;
n->context = (CoercionContext) $10;
n->inout = true;
@@ -9333,6 +9336,9 @@ cast_context: AS IMPLICIT_P { $$ = COERCION_IMPLICIT; }
| /*EMPTY*/ { $$ = COERCION_EXPLICIT; }
;
+opt_safe: SAFE { $$ = true; }
+ | /*EMPTY*/ { $$ = false; }
+ ;
DropCastStmt: DROP CAST opt_if_exists '(' Typename AS Typename ')' opt_drop_behavior
{
@@ -18108,6 +18114,7 @@ unreserved_keyword:
| ROUTINES
| ROWS
| RULE
+ | SAFE
| SAVEPOINT
| SCALAR
| SCHEMA
@@ -18744,6 +18751,7 @@ bare_label_keyword:
| ROW
| ROWS
| RULE
+ | SAFE
| SAVEPOINT
| SCALAR
| SCHEMA
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 7d3ed0eb890..a350c7edd11 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -3051,7 +3051,6 @@ CoercionErrorSafeCheck(ParseState *pstate, Node *castexpr, Node *sourceexpr, Oid
{
HeapTuple tuple;
bool errorsafe = true;
- bool userdefined = false;
Oid inputBaseType;
Oid targetBaseType;
Oid inputElementType;
@@ -3123,11 +3122,8 @@ CoercionErrorSafeCheck(ParseState *pstate, Node *castexpr, Node *sourceexpr, Oid
{
Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple);
- if (castForm->oid > FirstUnpinnedObjectId)
- {
+ if (OidIsValid(castForm->castfunc) && !castForm->casterrorsafe)
errorsafe = false;
- userdefined = true;
- }
ReleaseSysCache(tuple);
}
@@ -3141,9 +3137,7 @@ CoercionErrorSafeCheck(ParseState *pstate, Node *castexpr, Node *sourceexpr, Oid
format_type_be(targetType),
"DEFAULT",
"CAST ... ON CONVERSION ERROR"),
- userdefined
- ? errhint("Safe type cast for user-defined types are not yet supported")
- : errhint("Explicit cast is defined but definition is not error safe"),
+ errhint("Explicit cast is defined but definition is not error safe"),
parser_errposition(pstate, exprLocation(sourceexpr)));
}
diff --git a/src/include/catalog/pg_cast.dat b/src/include/catalog/pg_cast.dat
index fbfd669587f..ca52cfcd086 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,73 +378,73 @@
# 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)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'lseg', casttarget => 'point', castfunc => 'point(lseg)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'path', casttarget => 'polygon', castfunc => 'polygon(path)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'box', casttarget => 'point', castfunc => 'point(box)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'box', casttarget => 'lseg', castfunc => 'lseg(box)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'box', casttarget => 'polygon', castfunc => 'polygon(box)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'box', casttarget => 'circle', castfunc => 'circle(box)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'polygon', casttarget => 'point', castfunc => 'point(polygon)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'polygon', casttarget => 'path', castfunc => 'path',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'polygon', casttarget => 'box', castfunc => 'box(polygon)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'polygon', casttarget => 'circle',
- castfunc => 'circle(polygon)', castcontext => 'e', castmethod => 'f' },
+ castfunc => 'circle(polygon)', castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'circle', casttarget => 'point', castfunc => 'point(circle)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'circle', casttarget => 'box', castfunc => 'box(circle)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'circle', casttarget => 'polygon',
- castfunc => 'polygon(circle)', castcontext => 'e', castmethod => 'f' },
+ castfunc => 'polygon(circle)', castcontext => 'e', castmethod => 'f'},
# 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..bf47544f675 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;
/* ----------------
@@ -101,6 +105,7 @@ extern ObjectAddress CastCreate(Oid sourcetypeid,
Oid outcastid,
char castcontext,
char castmethod,
+ bool casterrorsafe,
DependencyType behavior);
#endif /* PG_CAST_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 82b0fb83b4b..e99499196cb 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -4155,6 +4155,7 @@ typedef struct CreateCastStmt
ObjectWithArgs *func;
CoercionContext context;
bool inout;
+ bool safe;
} CreateCastStmt;
/* ----------------------
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 5d4fe27ef96..f2ff6091fd2 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -396,6 +396,7 @@ PG_KEYWORD("routines", ROUTINES, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("row", ROW, COL_NAME_KEYWORD, BARE_LABEL)
PG_KEYWORD("rows", ROWS, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("rule", RULE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("safe", SAFE, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("savepoint", SAVEPOINT, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("scalar", SCALAR, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("schema", SCHEMA, UNRESERVED_KEYWORD, BARE_LABEL)
diff --git a/src/test/regress/expected/create_cast.out b/src/test/regress/expected/create_cast.out
index 0054ed0ef67..1e32c041b9f 100644
--- a/src/test/regress/expected/create_cast.out
+++ b/src/test/regress/expected/create_cast.out
@@ -81,6 +81,12 @@ NOTICE: drop cascades to cast from integer to casttesttype
-- Try it with a function that requires an implicit cast
CREATE FUNCTION bar_int4_text(int4) RETURNS text LANGUAGE SQL AS
$$ SELECT ('bar'::text || $1::text); $$;
+CREATE FUNCTION bar_int4_text_plpg(int4) RETURNS text LANGUAGE plpgsql AS
+$$ BEGIN RETURN ('bar'::text || $1::text); END $$;
+CREATE CAST (int4 AS casttesttype) WITH SAFE FUNCTION bar_int4_text(int4) AS IMPLICIT; -- error
+ERROR: Safe type cast functions are only supported for C and internal languages
+CREATE CAST (int4 AS casttesttype) WITH SAFE FUNCTION bar_int4_text_plpg(int4) AS IMPLICIT; -- error
+ERROR: Safe type cast functions are only supported for C and internal languages
CREATE CAST (int4 AS casttesttype) WITH FUNCTION bar_int4_text(int4) AS IMPLICIT;
SELECT 1234::int4::casttesttype; -- Should work now
casttesttype
@@ -92,7 +98,7 @@ SELECT CAST(1234::int4 AS casttesttype DEFAULT NULL ON CONVERSION ERROR); -- err
ERROR: cannot cast type integer to casttesttype when DEFAULT expression is specified in CAST ... ON CONVERSION ERROR
LINE 1: SELECT CAST(1234::int4 AS casttesttype DEFAULT NULL ON CONVE...
^
-HINT: Safe type cast for user-defined types are not yet supported
+HINT: Explicit cast is defined but definition is not error safe
-- 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 a357e1d0c0e..81ea244859f 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/sql/create_cast.sql b/src/test/regress/sql/create_cast.sql
index 0a15a795d87..30a0ff077c9 100644
--- a/src/test/regress/sql/create_cast.sql
+++ b/src/test/regress/sql/create_cast.sql
@@ -60,6 +60,11 @@ DROP FUNCTION int4_casttesttype(int4) CASCADE;
CREATE FUNCTION bar_int4_text(int4) RETURNS text LANGUAGE SQL AS
$$ SELECT ('bar'::text || $1::text); $$;
+CREATE FUNCTION bar_int4_text_plpg(int4) RETURNS text LANGUAGE plpgsql AS
+$$ BEGIN RETURN ('bar'::text || $1::text); END $$;
+
+CREATE CAST (int4 AS casttesttype) WITH SAFE FUNCTION bar_int4_text(int4) AS IMPLICIT; -- error
+CREATE CAST (int4 AS casttesttype) WITH SAFE FUNCTION bar_int4_text_plpg(int4) AS IMPLICIT; -- error
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
--
2.34.1