From 5be82e6f95fdcf77a314e403ba398d3f78d22f08 Mon Sep 17 00:00:00 2001 From: Amul Sul Date: Tue, 31 Mar 2026 14:49:02 +0530 Subject: [PATCH] fixup! CAST(expr AS newtype DEFAULT expr ON CONVERSION ERROR) --- contrib/citext/expected/citext.out | 4 +- contrib/citext/expected/citext_1.out | 4 +- doc/src/sgml/syntax.sgml | 52 ++++++++++++++--------- src/backend/executor/execExpr.c | 4 +- src/backend/parser/parse_expr.c | 21 +++++---- src/test/regress/expected/cast.out | 8 ++-- src/test/regress/expected/create_cast.out | 4 +- 7 files changed, 53 insertions(+), 44 deletions(-) diff --git a/contrib/citext/expected/citext.out b/contrib/citext/expected/citext.out index 73a3f923b07..69649c2c5e2 100644 --- a/contrib/citext/expected/citext.out +++ b/contrib/citext/expected/citext.out @@ -11,10 +11,10 @@ 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 +ERROR: cannot cast type character to citext with DEFAULT expression in CAST ... ON CONVERSION ERROR LINE 1: SELECT CAST('abc'::bpchar AS citext DEFAULT NULL ON CONVERSI... ^ -DETAIL: Safe type cast for user-defined types are not yet supported. +DETAIL: Safe type casts for user-defined types are not yet supported. -- Test the operators and indexing functions -- Test = and <>. SELECT 'a'::citext = 'a'::citext AS t; diff --git a/contrib/citext/expected/citext_1.out b/contrib/citext/expected/citext_1.out index 28788adf26d..896939d35ce 100644 --- a/contrib/citext/expected/citext_1.out +++ b/contrib/citext/expected/citext_1.out @@ -11,10 +11,10 @@ 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 +ERROR: cannot cast type character to citext with DEFAULT expression in CAST ... ON CONVERSION ERROR LINE 1: SELECT CAST('abc'::bpchar AS citext DEFAULT NULL ON CONVERSI... ^ -DETAIL: Safe type cast for user-defined types are not yet supported. +DETAIL: Safe type casts for user-defined types are not yet supported. -- Test the operators and indexing functions -- Test = and <>. SELECT 'a'::citext = 'a'::citext AS t; diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml index aa0fc1ef81c..48239c45fe2 100644 --- a/doc/src/sgml/syntax.sgml +++ b/doc/src/sgml/syntax.sgml @@ -2071,10 +2071,6 @@ CAST ( expression AS type The CAST syntax conforms to SQL; the syntax with :: is historical PostgreSQL usage. - The default behavior when no ON CONVERSION ERROR clause is specified is equivalent to: - -CAST ( expression AS type ERROR ON CONVERSION ERROR ) - @@ -2130,33 +2126,47 @@ CAST ( expression AS type - - Safe Type Cast - - A type cast may occasionally fail. To guard against such failures, you can - provide an ON CONVERSION ERROR clause to handle potential errors. - The syntax for safe type cast is: + + Safe Type Casts + + + A type cast may occasionally fail at run time. To guard against such + failures, you can provide an ON CONVERSION ERROR + clause to control what happens when a cast fails. The available forms + are: +CAST ( expression AS type NULL ON CONVERSION ERROR ) CAST ( expression AS type DEFAULT expression ON CONVERSION ERROR ) +CAST ( expression AS type ERROR ON CONVERSION ERROR ) - If the type cast fails, an error is not raised. Instead, evaluation falls - back to the default expression specified in the - ON CONVERSION ERROR clause. - At present, this only support built-in type casts, see . - User-defined type casts created with CREATE CAST - are not supported. - + With NULL ON CONVERSION ERROR, a NULL + value is returned when the cast fails. With DEFAULT + expression ON CONVERSION ERROR, + the default expression is evaluated and + returned instead. The ERROR ON CONVERSION ERROR + form raises an error on failure, which is also the default behavior + when no ON CONVERSION ERROR clause is specified. + - Some examples: + At present, this only supports built-in type casts listed in + . User-defined type casts created + with CREATE CAST are not + supported. + + + + Some examples: +SELECT CAST('not-a-date' AS date NULL ON CONVERSION ERROR); +Result: NULL +SELECT CAST('not-a-date' AS date DEFAULT '1970-01-01' ON CONVERSION ERROR); +Result: 1970-01-01 SELECT CAST(TEXT 'error' AS integer DEFAULT 3 ON CONVERSION ERROR); Result: 3 -SELECT CAST(TEXT 'error' AS numeric DEFAULT 1.1 ON CONVERSION ERROR); -Result: 1.1 - + diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index 84a48e8e3f6..59482fab56c 100644 --- a/src/backend/executor/execExpr.c +++ b/src/backend/executor/execExpr.c @@ -4794,8 +4794,8 @@ ExecInitSafeTypeCastExpr(SafeTypeCastExpr *stcexpr, ExprState *state, ExprEvalStep *scratch) { /* - * Coercion to the target type failed. Falling back to the DEFAULT - * expression from the ON CONVERSION ERROR clause to finish. + * If there's no cast expression (castexpr == NULL), only the DEFAULT + * expression needs to be evaluated; set it up and return early. */ if (stcexpr->castexpr == NULL) { diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 83adc598565..a83d0dda76e 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -2785,11 +2785,7 @@ transformTypeCast(ParseState *pstate, TypeCast *tc) Oid targetTypecoll; int32 targetTypmod; int location; - ErrorSaveContext *escontext = makeNode(ErrorSaveContext); - - escontext->type = T_ErrorSaveContext; - escontext->error_occurred = false; - escontext->details_wanted = false; + ErrorSaveContext *escontext = NULL; /* Look up the type name first */ typenameTypeIdAndMod(pstate, tc->typeName, &targetType, &targetTypmod); @@ -2801,6 +2797,11 @@ transformTypeCast(ParseState *pstate, TypeCast *tc) { Oid defColl; + escontext = makeNode(ErrorSaveContext); + escontext->type = T_ErrorSaveContext; + escontext->error_occurred = false; + escontext->details_wanted = false; + defexpr = transformExpr(pstate, tc->defexpr, EXPR_KIND_CAST_DEFAULT); defexpr = coerce_to_target_type(pstate, defexpr, exprType(defexpr), @@ -2961,13 +2962,11 @@ CoercionErrorSafeCheck(ParseState *pstate, Node *castexpr, Node *source, if (!errorsafe_coercion) ereport(ERROR, errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot cast type %s to %s when %s expression is specified in %s", + errmsg("cannot cast type %s to %s with DEFAULT expression in CAST ... ON CONVERSION ERROR", format_type_be(inputType), - format_type_be(targetType), - "DEFAULT", - "CAST ... ON CONVERSION ERROR"), + format_type_be(targetType)), userdefined - ? errdetail("Safe type cast for user-defined types are not yet supported.") + ? errdetail("Safe type casts for user-defined types are not yet supported.") : errdetail("Explicit cast is defined but definition is not error safe."), parser_errposition(pstate, exprLocation(source))); } @@ -3048,7 +3047,7 @@ CoercionErrorSafe_Internal(Oid inputType, Oid targetType, } /* - * Casts source data type as MONEY are not error safe. + * Casts from MONEY source type are not error safe. */ if (inputType == MONEYOID) { diff --git a/src/test/regress/expected/cast.out b/src/test/regress/expected/cast.out index e5740d2f5bc..1a55fceeef3 100644 --- a/src/test/regress/expected/cast.out +++ b/src/test/regress/expected/cast.out @@ -186,7 +186,7 @@ SELECT CAST(a AS int2 DEFAULT NULL ON CONVERSION ERROR) FROM tcast0; (2 rows) SELECT CAST(ROW(1,2) AS tcast0 DEFAULT NULL ON CONVERSION ERROR) FROM tcast0 as t; -ERROR: cannot cast type record to tcast0 when DEFAULT expression is specified in CAST ... ON CONVERSION ERROR +ERROR: cannot cast type record to tcast0 with DEFAULT expression in CAST ... ON CONVERSION ERROR LINE 1: SELECT CAST(ROW(1,2) AS tcast0 DEFAULT NULL ON CONVERSION ER... ^ DETAIL: Explicit cast is defined but definition is not error safe. @@ -443,7 +443,7 @@ ERROR: value too long for type character(3) LINE 1: ...ST('(NULL,42)' AS comp_domain_with_typmod DEFAULT '(1234,2)'... ^ SELECT CAST(ROW(NULL,42) AS comp_domain_with_typmod DEFAULT NULL ON CONVERSION ERROR); -- error -ERROR: cannot cast type record to comp_domain_with_typmod when DEFAULT expression is specified in CAST ... ON CONVERSION ERROR +ERROR: cannot cast type record to comp_domain_with_typmod with DEFAULT expression in CAST ... ON CONVERSION ERROR LINE 1: SELECT CAST(ROW(NULL,42) AS comp_domain_with_typmod DEFAULT ... ^ DETAIL: Explicit cast is defined but definition is not error safe. @@ -641,12 +641,12 @@ SELECT CAST('-0'::int4 AS MONEY DEFAULT NULL ON CONVERSION ERROR); (1 row) SELECT CAST(NULL::money AS numeric DEFAULT NULL ON CONVERSION ERROR); -ERROR: cannot cast type money to numeric when DEFAULT expression is specified in CAST ... ON CONVERSION ERROR +ERROR: cannot cast type money to numeric with DEFAULT expression in CAST ... ON CONVERSION ERROR LINE 1: SELECT CAST(NULL::money AS numeric DEFAULT NULL ON CONVERSIO... ^ DETAIL: Explicit cast is defined but definition is not error safe. SELECT CAST(NULL::money[] AS numeric[] DEFAULT NULL ON CONVERSION ERROR); -ERROR: cannot cast type money[] to numeric[] when DEFAULT expression is specified in CAST ... ON CONVERSION ERROR +ERROR: cannot cast type money[] to numeric[] with DEFAULT expression in CAST ... ON CONVERSION ERROR LINE 1: SELECT CAST(NULL::money[] AS numeric[] DEFAULT NULL ON CONVE... ^ DETAIL: Explicit cast is defined but definition is not error safe. diff --git a/src/test/regress/expected/create_cast.out b/src/test/regress/expected/create_cast.out index 28c880b10e2..64ac60b157f 100644 --- a/src/test/regress/expected/create_cast.out +++ b/src/test/regress/expected/create_cast.out @@ -89,10 +89,10 @@ SELECT 1234::int4::casttesttype; -- Should work now (1 row) SELECT CAST(1234::int4 AS casttesttype DEFAULT NULL ON CONVERSION ERROR); -- error -ERROR: cannot cast type integer to casttesttype when DEFAULT expression is specified in CAST ... ON CONVERSION ERROR +ERROR: cannot cast type integer to casttesttype with DEFAULT expression in CAST ... ON CONVERSION ERROR LINE 1: SELECT CAST(1234::int4 AS casttesttype DEFAULT NULL ON CONVE... ^ -DETAIL: Safe type cast for user-defined types are not yet supported. +DETAIL: Safe type casts for user-defined types are not yet supported. -- check dependencies generated for that SELECT pg_describe_object(classid, objid, objsubid) as obj, pg_describe_object(refclassid, refobjid, refobjsubid) as objref, -- 2.47.1