public inbox for [email protected]
help / color / mirror / Atom feedFrom: 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: Fri, 10 Oct 2025 20:23:07 +0800
Message-ID: <CACJufxF--5d=fmoRBHfqJE9Vy38dCURNKYOKKpujRCnoTEQ7nQ@mail.gmail.com> (raw)
In-Reply-To: <CACJufxE053=bO3pDUpGba6Yz3VGpU_XCbg4HO6Rew5EJ7k7VnQ@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>
<CACJufxE053=bO3pDUpGba6Yz3VGpU_XCbg4HO6Rew5EJ7k7VnQ@mail.gmail.com>
hi.
First of all, rebase v6.
select distinct castsource::regtype, casttarget::regtype, pp.prosrc
from pg_cast pc join pg_proc pp on pp.oid = pc.castfunc
join pg_type pt on pt.oid = castsource
join pg_type pt1 on pt1.oid = casttarget
and pc.castfunc > 0 and pt.typarray <> 0 and pt.typnamespace =
'pg_catalog'::regnamespace
and pt1.typnamespace = 'pg_catalog'::regnamespace
order by castsource::regtype, casttarget::regtype;
The above query will list all cast functions in PG_CATALOG schema that
need to be
refactored to be error-safe. looking around, I found that the below source data
type, the cast function is already error safe:
boolean
name
"char" ("char" to integer was handled in integer cast part)
regprocedure
regoper
regoperator
regclass
regtype
regconfig
regdictionary
regproc
oid
regnamespace
regrole
regcollation
regdatabase
int4range
numrange
tsrange
tstzrange
daterange
int8range
xid8
time without time zone
time with time zone
After applying the attached v7 patch patchset,
the below are cast functions that I didn't refactor to error safe yet.
select pc.castsource::regtype,
pc.casttarget::regtype, castfunc::regproc, pp.prosrc
from pg_cast pc join pg_proc pp on pp.oid = pc.castfunc
join pg_type pt on pt.oid = castsource
join pg_type pt1 on pt1.oid = casttarget
and pc.castfunc > 0 and pt.typnamespace = 'pg_catalog'::regnamespace
and pt1.typnamespace = 'pg_catalog'::regnamespace and not pc.casterrorsafe;
castsource | casttarget | castfunc | prosrc
------------+------------+----------------------+--------------
bigint | money | pg_catalog.money | int8_cash
integer | money | pg_catalog.money | int4_cash
numeric | money | pg_catalog.money | numeric_cash
money | numeric | pg_catalog."numeric" | cash_numeric
(4 rows)
The reason I don't refactor these cast functions related to money data
type is because
1. I am not sure if the PGLC_localeconv() function is error safe or not.
2. refactoring such ``DirectFunctionCall1(numeric_int8, amount));``
requires more effort.
please check the attached v7 patch set:
01-error-safe-for-casting-bytea-to-other-types
02-error-safe-for-casting-bit-varbit-to-other-types
03-error-safe-for-casting-character-to-other-types
04-error-safe-for-casting-text-to-other-types
05-error-safe-for-casting-character-varying-to-other-types
06-error-safe-for-casting-inet-to-other-types
07-error-safe-for-casting-macaddr8-to-other-types
08-error-safe-for-casting-integer-to-other-types
09-error-safe-for-casting-bigint-to-other-types
10-error-safe-for-casting-numeric-to-other-types
11-error-safe-for-casting-float4-to-other-types
12-error-safe-for-casting-float8-to-other-types
13-error-safe-for-casting-date-to-other-types
14-error-safe-for-casting-interval-to-other-types
15-error-safe-for-casting-timestamptz-to-other-types
16-error-safe-for-casting-timestamp-to-other-types
17-error-safe-for-casting-jsonb-to-other-types
18-error-safe-for-casting-geometry-data-types
19-invent_some_error_safe_function.patch
20-CAST-expr-AS-newtype-DEFAULT-ON-ERROR.patch
Each patch includes a commit message explaining which function is being
refactored to be error-safe.
I also made some changes for
"20-CAST-expr-AS-newtype-DEFAULT-ON-ERROR.patch"
If the source expression is Const, it can be unknown type or other type.
* for UNKNOWN type, example:
SELECT CAST ('\x112233445566778899'::bytea AS int8 DEFAULT NULL ON
CONVERSION ERROR);
We use CoerceUnknownConstSafe to verify earlier that such Const can be
coerced to target data type or not.
* for other data type, example:
SELECT CAST ('\x112233445566778899'::bytea AS int8 DEFAULT NULL ON
CONVERSION ERROR);
we use evaluate_expr_safe to evaluate it error safe way earlier in
transformTypeSafeCast.
similar to transformPartitionBoundValue call evaluate_expr
The geometry cast functions ``(poly_circle(PG_FUNCTION_ARGS)`` have
problems making it error safe,
working on it.
Attachments:
[text/x-patch] v7-0019-invent-some-error-safe-functions.patch (9.0K, 2-v7-0019-invent-some-error-safe-functions.patch)
download | inline diff:
From a70d65ce8183eef768eba2e340b208743539ddd5 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Fri, 10 Oct 2025 18:57:21 +0800
Subject: [PATCH v7 19/20] invent some error safe functions
stringTypeDatumSafe: error safe version of stringTypeDatum
evaluate_expr_safe: error safe version of evaluate_expr
ExecInitExprSafe: soft error variant of ExecInitExpr
OidInputFunctionCallSafe: soft error variant of OidInputFunctionCall
discussion: https://postgr.es/m/CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@mail.gmail.com
---
src/backend/executor/execExpr.c | 41 +++++++++++++++++
src/backend/optimizer/util/clauses.c | 69 ++++++++++++++++++++++++++++
src/backend/parser/parse_type.c | 14 ++++++
src/backend/utils/fmgr/fmgr.c | 13 ++++++
src/include/executor/executor.h | 1 +
src/include/fmgr.h | 3 ++
src/include/optimizer/optimizer.h | 2 +
src/include/parser/parse_type.h | 2 +
8 files changed, 145 insertions(+)
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index f1569879b52..b302be36f73 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -170,6 +170,47 @@ ExecInitExpr(Expr *node, PlanState *parent)
return state;
}
+/*
+ * ExecInitExprSafe: soft error variant of ExecInitExpr.
+ *
+ * use it only for expression nodes support soft errors, not all expression
+ * nodes support it.
+*/
+ExprState *
+ExecInitExprSafe(Expr *node, PlanState *parent)
+{
+ ExprState *state;
+ ExprEvalStep scratch = {0};
+
+ /* Special case: NULL expression produces a NULL ExprState pointer */
+ if (node == NULL)
+ return NULL;
+
+ /* Initialize ExprState with empty step list */
+ state = makeNode(ExprState);
+ state->expr = node;
+ state->parent = parent;
+ state->ext_params = NULL;
+ state->escontext = makeNode(ErrorSaveContext);
+ state->escontext->type = T_ErrorSaveContext;
+ state->escontext->error_occurred = false;
+ state->escontext->details_wanted = false;
+
+ /* Insert setup steps as needed */
+ ExecCreateExprSetupSteps(state, (Node *) node);
+
+ /* Compile the expression proper */
+ ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
+
+ /* Finally, append a DONE step */
+ scratch.opcode = EEOP_DONE_RETURN;
+ ExprEvalPushStep(state, &scratch);
+
+ ExecReadyExpr(state);
+
+ return state;
+}
+
/*
* ExecInitExprWithParams: prepare a standalone expression tree for execution
*
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 81d768ff2a2..3f9ff455ba6 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -5147,6 +5147,75 @@ evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod,
resultTypByVal);
}
+/*
+ * evaluate_expr_safe: error safe version of evaluate_expr
+ *
+ * We use the executor's routine ExecEvalExpr() to avoid duplication of
+ * code and ensure we get the same result as the executor would get.
+ *
+ * return NULL when evaulation failed. Ensure the expression expr can be evaulated
+ * in a soft error way.
+ *
+ * See comments on evaluate_expr too.
+ */
+Expr *
+evaluate_expr_safe(Expr *expr, Oid result_type, int32 result_typmod,
+ Oid result_collation)
+{
+ EState *estate;
+ ExprState *exprstate;
+ MemoryContext oldcontext;
+ Datum const_val;
+ bool const_is_null;
+ int16 resultTypLen;
+ bool resultTypByVal;
+
+ estate = CreateExecutorState();
+
+ /* We can use the estate's working context to avoid memory leaks. */
+ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+ /* Make sure any opfuncids are filled in. */
+ fix_opfuncids((Node *) expr);
+
+ /*
+ * Prepare expr for execution. (Note: we can't use ExecPrepareExpr
+ * because it'd result in recursively invoking eval_const_expressions.)
+ */
+ exprstate = ExecInitExprSafe(expr, NULL);
+
+ const_val = ExecEvalExprSwitchContext(exprstate,
+ GetPerTupleExprContext(estate),
+ &const_is_null);
+
+ /* Get info needed about result datatype */
+ get_typlenbyval(result_type, &resultTypLen, &resultTypByVal);
+
+ /* Get back to outer memory context */
+ MemoryContextSwitchTo(oldcontext);
+
+ if (SOFT_ERROR_OCCURRED(exprstate->escontext))
+ {
+ FreeExecutorState(estate);
+ return NULL;
+ }
+
+ if (!const_is_null)
+ {
+ if (resultTypLen == -1)
+ const_val = PointerGetDatum(PG_DETOAST_DATUM_COPY(const_val));
+ else
+ const_val = datumCopy(const_val, resultTypByVal, resultTypLen);
+ }
+
+ FreeExecutorState(estate);
+
+ return (Expr *) makeConst(result_type, result_typmod, result_collation,
+ resultTypLen,
+ const_val, const_is_null,
+ resultTypByVal);
+}
+
/*
* inline_set_returning_function
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 7713bdc6af0..d260aeec5dc 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,19 @@ stringTypeDatum(Type tp, char *string, int32 atttypmod)
return OidInputFunctionCall(typinput, string, typioparam, atttypmod);
}
+/* error safe version of stringTypeDatum */
+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,
+ (Node *) &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/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 0fe63c6bb83..aaa4a42b1ea 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, Node *escontext,
+ Datum *result)
+{
+ FmgrInfo flinfo;
+
+ fmgr_info(functionId, &flinfo);
+
+ return InputFunctionCallSafe(&flinfo, str, typioparam, typmod,
+ escontext, result);
+}
+
char *
OidOutputFunctionCall(Oid functionId, Datum val)
{
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 3248e78cd28..ff9997e1cc1 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -321,6 +321,7 @@ ExecProcNode(PlanState *node)
* prototypes from functions in execExpr.c
*/
extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
+extern ExprState *ExecInitExprSafe(Expr *node, PlanState *parent);
extern ExprState *ExecInitExprWithParams(Expr *node, ParamListInfo ext_params);
extern ExprState *ExecInitQual(List *qual, PlanState *parent);
extern ExprState *ExecInitCheck(List *qual, PlanState *parent);
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 74fe3ea0575..991e14034d3 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, Node *escontext,
+ Datum *result);
extern char *OutputFunctionCall(FmgrInfo *flinfo, Datum val);
extern char *OidOutputFunctionCall(Oid functionId, Datum val);
extern Datum ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf,
diff --git a/src/include/optimizer/optimizer.h b/src/include/optimizer/optimizer.h
index a34113903c0..f1b6381d084 100644
--- a/src/include/optimizer/optimizer.h
+++ b/src/include/optimizer/optimizer.h
@@ -146,6 +146,8 @@ extern Node *estimate_expression_value(PlannerInfo *root, Node *node);
extern Expr *evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod,
Oid result_collation);
+extern Expr *evaluate_expr_safe(Expr *expr, Oid result_type, int32 result_typmod,
+ Oid result_collation);
extern bool var_is_nonnullable(PlannerInfo *root, Var *var, bool use_rel_info);
extern List *expand_function_arguments(List *args, bool include_out_arguments,
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);
--
2.34.1
[text/x-patch] v7-0020-CAST-expr-AS-newtype-DEFAULT-ON-ERROR.patch (140.2K, 3-v7-0020-CAST-expr-AS-newtype-DEFAULT-ON-ERROR.patch)
download | inline diff:
From 3e350ca387117f05388e340e048c14a95ce724e1 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Fri, 10 Oct 2025 19:06:14 +0800
Subject: [PATCH v7 20/20] 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.
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}
[0]: https://git.postgresql.org/cgit/postgresql.git/commit/?id=aaaf9449ec6be62cb0d30ed3588dc384f56274b
discussion: https://postgr.es/m/CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@mail.gmail.com
---
.../pg_stat_statements/expected/select.out | 23 +-
contrib/pg_stat_statements/sql/select.sql | 5 +
doc/src/sgml/catalogs.sgml | 12 +
doc/src/sgml/syntax.sgml | 16 +
src/backend/catalog/pg_cast.c | 1 +
src/backend/executor/execExpr.c | 76 +-
src/backend/executor/execExprInterp.c | 37 +
src/backend/jit/llvm/llvmjit_expr.c | 48 ++
src/backend/nodes/nodeFuncs.c | 67 ++
src/backend/optimizer/util/clauses.c | 24 +
src/backend/parser/gram.y | 24 +-
src/backend/parser/parse_agg.c | 9 +
src/backend/parser/parse_expr.c | 383 +++++++++-
src/backend/parser/parse_func.c | 3 +
src/backend/parser/parse_target.c | 14 +
src/backend/utils/adt/arrayfuncs.c | 13 +
src/backend/utils/adt/ruleutils.c | 16 +
src/include/catalog/pg_cast.dat | 330 ++++-----
src/include/catalog/pg_cast.h | 4 +
src/include/executor/execExpr.h | 8 +
src/include/nodes/execnodes.h | 30 +
src/include/nodes/parsenodes.h | 6 +
src/include/nodes/primnodes.h | 33 +
src/include/parser/parse_node.h | 2 +
src/test/regress/expected/cast.out | 681 ++++++++++++++++++
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 | 301 ++++++++
src/test/regress/sql/create_cast.sql | 1 +
src/tools/pgindent/typedefs.list | 3 +
31 files changed, 2011 insertions(+), 190 deletions(-)
create mode 100644 src/test/regress/expected/cast.out
create mode 100644 src/test/regress/sql/cast.sql
diff --git a/contrib/pg_stat_statements/expected/select.out b/contrib/pg_stat_statements/expected/select.out
index 75c896f3885..6e67997f0a2 100644
--- a/contrib/pg_stat_statements/expected/select.out
+++ b/contrib/pg_stat_statements/expected/select.out
@@ -73,6 +73,25 @@ SELECT 1 AS "int" OFFSET 2 FETCH FIRST 3 ROW ONLY;
-----
(0 rows)
+--error safe type cast
+SELECT CAST('a' AS int DEFAULT 2 ON CONVERSION ERROR);
+ int4
+------
+ 2
+(1 row)
+
+SELECT CAST('11' AS int DEFAULT 2 ON CONVERSION ERROR);
+ int4
+------
+ 11
+(1 row)
+
+SELECT CAST('12' AS numeric DEFAULT 2 ON CONVERSION ERROR);
+ numeric
+---------
+ 12
+(1 row)
+
-- DISTINCT and ORDER BY patterns
-- Try some query permutations which once produced identical query IDs
SELECT DISTINCT 1 AS "int";
@@ -222,6 +241,8 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
2 | 2 | SELECT $1 AS "int" ORDER BY 1
1 | 2 | SELECT $1 AS i UNION SELECT $2 ORDER BY i
1 | 1 | SELECT $1 || $2
+ 2 | 2 | SELECT CAST($1 AS int DEFAULT $2 ON CONVERSION ERROR)
+ 1 | 1 | SELECT CAST($1 AS numeric DEFAULT $2 ON CONVERSION ERROR)
2 | 2 | SELECT DISTINCT $1 AS "int"
0 | 0 | SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"
1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
@@ -230,7 +251,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | ) +
| | SELECT f FROM t ORDER BY f
1 | 1 | select $1::jsonb ? $2
-(17 rows)
+(19 rows)
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
diff --git a/contrib/pg_stat_statements/sql/select.sql b/contrib/pg_stat_statements/sql/select.sql
index 11662cde08c..7ee8160fd84 100644
--- a/contrib/pg_stat_statements/sql/select.sql
+++ b/contrib/pg_stat_statements/sql/select.sql
@@ -25,6 +25,11 @@ SELECT 1 AS "int" LIMIT 3 OFFSET 3;
SELECT 1 AS "int" OFFSET 1 FETCH FIRST 2 ROW ONLY;
SELECT 1 AS "int" OFFSET 2 FETCH FIRST 3 ROW ONLY;
+--error safe type cast
+SELECT CAST('a' AS int DEFAULT 2 ON CONVERSION ERROR);
+SELECT CAST('11' AS int DEFAULT 2 ON CONVERSION ERROR);
+SELECT CAST('12' AS numeric DEFAULT 2 ON CONVERSION ERROR);
+
-- DISTINCT and ORDER BY patterns
-- Try some query permutations which once produced identical query IDs
SELECT DISTINCT 1 AS "int";
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 9b3aae8603b..957041add7b 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1849,6 +1849,18 @@ 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 indicates whether the <structfield>castfunc</structfield> function is error safe.
+ If the <structfield>castfunc</structfield> function is error safe, it can be used in
+ For further details see <xref linkend="sql-syntax-type-casts"/>.
+ </para></entry>
+ </row>
+
</tbody>
</tgroup>
</table>
diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml
index 237d7306fe8..9acb7d69645 100644
--- a/doc/src/sgml/syntax.sgml
+++ b/doc/src/sgml/syntax.sgml
@@ -2106,6 +2106,22 @@ 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.
+ It can also be written as:
+<synopsis>
+CAST ( <replaceable>expression</replaceable> AS <replaceable>type</replaceable> ERROR ON CONVERSION ERROR )
+</synopsis>
+ </para>
+
+ <para>
+Type cast can also be error safe.
+<synopsis>
+CAST ( <replaceable>expression</replaceable> AS <replaceable>type</replaceable> DEFAULT <replaceable>defexpression</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 b302be36f73..88a647e44fa 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,
@@ -1742,6 +1745,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);
@@ -2218,6 +2222,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;
@@ -2784,7 +2796,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;
@@ -4782,6 +4794,68 @@ ExecBuildParamSetEqual(TupleDesc desc,
return state;
}
+/*
+ * Push steps to evaluate a SafeTypeCastExpr and its various subsidiary
+ * expressions.
+ */
+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
+ {
+ ExprEvalStep *as;
+ SafeTypeCastState *stcstate;
+ ErrorSaveContext *saved_escontext;
+ int jumps_to_end;
+
+ stcstate = palloc0(sizeof(SafeTypeCastState));
+ stcstate->stcexpr = stcexpr;
+ stcstate->escontext.type = T_ErrorSaveContext;
+ state->escontext = &stcstate->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 = 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;
+
+ as = &state->steps[jumps_to_end];
+ 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 67f4e00eac4..49da2905810 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,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
+ EEO_CASE(EEOP_SAFETYPE_CAST)
+ {
+ /* too complex for an inline implementation */
+ EEO_JUMP(ExecEvalSafeTypeCast(state, op));
+ }
+
EEO_CASE(EEOP_JSONEXPR_PATH)
{
/* too complex for an inline implementation */
@@ -3644,6 +3651,12 @@ 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;
+ }
}
/*
@@ -5183,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 712b35df7e5..ebea7c4445f 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -2256,6 +2256,54 @@ 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 ede838cd40c..533e21120a6 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;
@@ -1550,6 +1561,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;
@@ -2321,6 +2341,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:
@@ -3330,6 +3362,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;
@@ -4464,6 +4509,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->def_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/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 3f9ff455ba6..14e0df3bee2 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -2945,6 +2945,30 @@ 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);
+
+ /*
+ * We must not reduce any recognizably constant subexpressions
+ * in cast_expr here, since we don’t want it to fail
+ * prematurely.
+ */
+ 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 21caf2d43bf..c62e9ac8edf 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -649,6 +649,7 @@ 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_opt
%type <node> json_format_clause
json_format_clause_opt
@@ -15982,8 +15983,20 @@ 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_opt ')'
+ {
+ TypeCast *cast = (TypeCast *) makeTypeCast($3, $5, @1);
+ if ($6 == NULL)
+ $$ = (Node *) cast;
+ else
+ {
+ SafeTypeCast *safecast = makeNode(SafeTypeCast);
+ safecast->cast = (Node *) cast;
+ safecast->def_expr = $6;
+
+ $$ = (Node *) safecast;
+ }
+ }
| EXTRACT '(' extract_list ')'
{
$$ = (Node *) makeFuncCall(SystemFuncName("extract"),
@@ -16370,6 +16383,13 @@ func_expr_common_subexpr:
;
+cast_on_error_clause_opt:
+ DEFAULT a_expr ON CONVERSION_P ERROR_P { $$ = $2; }
+ | ERROR_P ON CONVERSION_P ERROR_P { $$ = NULL; }
+ | NULL_P ON CONVERSION_P ERROR_P { $$ = makeNullAConst(-1); }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
/*
* SQL/XML support
*/
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 3254c83cc6c..6573e2a93ea 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -486,6 +486,12 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
err = _("grouping operations are not allowed in check constraints");
break;
+ case EXPR_KIND_CAST_DEFAULT:
+ if (isAgg)
+ err = _("aggregate functions are not allowed in CAST DEFAULT expressions");
+ else
+ err = _("grouping operations are not allowed in CAST DEFAULT expressions");
+ break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
@@ -956,6 +962,9 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
case EXPR_KIND_DOMAIN_CHECK:
err = _("window functions are not allowed in check constraints");
break;
+ case EXPR_KIND_CAST_DEFAULT:
+ err = _("window functions are not allowed in CAST DEFAULT expressions");
+ break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
err = _("window functions are not allowed in DEFAULT expressions");
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 12119f147fc..ac60b74dceb 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -17,6 +17,8 @@
#include "access/htup_details.h"
#include "catalog/pg_aggregate.h"
+#include "catalog/pg_cast.h"
+#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "nodes/makefuncs.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,8 @@ static Node *transformMultiAssignRef(ParseState *pstate, MultiAssignRef *maref);
static Node *transformCaseExpr(ParseState *pstate, CaseExpr *c);
static Node *transformSubLink(ParseState *pstate, SubLink *sublink);
static Node *transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
- Oid array_type, Oid element_type, int32 typmod);
+ Oid array_type, Oid element_type, int32 typmod,
+ bool *can_coerce);
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 +80,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);
@@ -107,6 +112,8 @@ static Expr *make_distinct_op(ParseState *pstate, List *opname,
Node *ltree, Node *rtree, int location);
static Node *make_nulltest_from_distinct(ParseState *pstate,
A_Expr *distincta, Node *arg);
+static bool CoerceUnknownConstSafe(ParseState *pstate, Node *node,
+ Oid targetType, int32 targetTypeMod);
/*
@@ -164,13 +171,17 @@ transformExprRecurse(ParseState *pstate, Node *expr)
case T_A_ArrayExpr:
result = transformArrayExpr(pstate, (A_ArrayExpr *) expr,
- InvalidOid, InvalidOid, -1);
+ InvalidOid, InvalidOid, -1, NULL);
break;
case T_TypeCast:
result = transformTypeCast(pstate, (TypeCast *) expr);
break;
+ case T_SafeTypeCast:
+ result = transformTypeSafeCast(pstate, (SafeTypeCast *) expr);
+ break;
+
case T_CollateClause:
result = transformCollateClause(pstate, (CollateClause *) expr);
break;
@@ -576,6 +587,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
case EXPR_KIND_COPY_WHERE:
case EXPR_KIND_GENERATED_COLUMN:
case EXPR_KIND_CYCLE_MARK:
+ case EXPR_KIND_CAST_DEFAULT:
/* okay */
break;
@@ -1824,6 +1836,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
case EXPR_KIND_DOMAIN_CHECK:
err = _("cannot use subquery in check constraint");
break;
+ case EXPR_KIND_CAST_DEFAULT:
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
err = _("cannot use subquery in DEFAULT expression");
@@ -2005,16 +2018,73 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
return result;
}
+/*
+ * Return true if successfuly coerced a Unknown Const to targetType
+*/
+static bool
+CoerceUnknownConstSafe(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;
+}
+
+
/*
* 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().
+ *
+ * Most of the time, the caller will pass can_coerce as NULL.
+ * can_coerce is not NULL only when performing parse analysis for expressions
+ * like CAST(DEFAULT ... ON CONVERSION ERROR).
+ * When can_coerce is not NULL, it should be assumed to default to true. If
+ * coerce array elements to the target type fails, can_coerce will be set to
+ * 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;
@@ -2022,6 +2092,7 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
ListCell *element;
Oid coerce_type;
bool coerce_hard;
+ bool expr_is_const = false;
/*
* Transform the element expressions
@@ -2045,9 +2116,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
@@ -2088,6 +2160,9 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
}
else
{
+ /* We obviously know the target type when performing type-safe cast */
+ Assert(can_coerce == NULL);
+
/* Can't handle an empty array without a target type */
if (newelems == NIL)
ereport(ERROR,
@@ -2140,7 +2215,36 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
Node *e = (Node *) lfirst(element);
Node *newe;
- if (coerce_hard)
+ /*
+ * For error-safe type casting, we need to safely check whether the
+ * UNKNOWN constant can be coerced to the target type. If it cannot, set
+ * can_coerce to false and coerce source element to TEXT data type.
+ * see coerce_type function also.
+ */
+ if (can_coerce != NULL && IsA(e, Const))
+ {
+ if (exprType(e) == UNKNOWNOID)
+ {
+ if (!CoerceUnknownConstSafe(pstate, e, coerce_type, typmod))
+ {
+ *can_coerce = false;
+
+ /* see resolveTargetListUnknowns also */
+ e = coerce_to_target_type(pstate, e,
+ UNKNOWNOID,
+ TEXTOID,
+ -1,
+ COERCION_IMPLICIT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ }
+ }
+ expr_is_const = true;
+ }
+
+ if (can_coerce != NULL && (!*can_coerce))
+ newe = e;
+ else if (coerce_hard)
{
newe = coerce_to_target_type(pstate, e,
exprType(e),
@@ -2149,13 +2253,51 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
COERCION_EXPLICIT,
COERCE_EXPLICIT_CAST,
-1);
- if (newe == NULL)
+ if (newe == NULL && can_coerce == NULL)
ereport(ERROR,
(errcode(ERRCODE_CANNOT_COERCE),
errmsg("cannot cast type %s to %s",
format_type_be(exprType(e)),
format_type_be(coerce_type)),
parser_errposition(pstate, exprLocation(e))));
+
+ if (can_coerce != NULL)
+ {
+ if (newe == NULL)
+ {
+ newe = e;
+ *can_coerce = false;
+ }
+ else if (expr_is_const && !IsA(newe, Const))
+ {
+ /*
+ * Use evaluate_expr_safe to pre-evaluate simple constant
+ * cast expressions early, in a way that tolter errors .
+ *
+ * Rationale:
+ * 1. When deparsing safe cast expressions (or in other
+ * cases), eval_const_expressions might be invoked, but
+ * it cannot handle errors gracefully.
+ * 2. If the cast expression involves only simple constants,
+ * we can safely evaluate it ahead of time. If the
+ * evaluation fails, it indicates that such a cast is not
+ * possible, and we can then fall back to the CAST
+ * DEFAULT expression.
+ */
+ Expr *newe_copy = (Expr *) copyObject(newe);
+
+ newe = (Node *) evaluate_expr_safe(newe_copy,
+ coerce_type,
+ typmod,
+ exprCollation(newe));
+ if (newe == NULL)
+ {
+ newe = e;
+ *can_coerce = false;
+ }
+ }
+ expr_is_const = false;
+ }
}
else
newe = coerce_to_common_type(pstate, e,
@@ -2743,7 +2885,8 @@ transformTypeCast(ParseState *pstate, TypeCast *tc)
(A_ArrayExpr *) arg,
targetBaseType,
elementType,
- targetBaseTypmod);
+ targetBaseTypmod,
+ NULL);
}
else
expr = transformExprRecurse(pstate, arg);
@@ -2780,6 +2923,228 @@ 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 *def_expr = NULL;
+ Node *cast_expr = NULL;
+ Node *source_expr = NULL;
+ Oid inputType = InvalidOid;
+ Oid targetType;
+ Oid targetBaseType;
+ Oid typeColl;
+ int32 targetTypmod;
+ int32 targetBaseTypmod;
+ bool can_coerce = true;
+ bool expr_is_const = false;
+ int location;
+
+ /* Look up the type name first */
+ typenameTypeIdAndMod(pstate, tcast->typeName, &targetType, &targetTypmod);
+ targetBaseTypmod = targetTypmod;
+
+ typeColl = get_typcollation(targetType);
+ targetBaseType = getBaseTypeAndTypmod(targetType, &targetBaseTypmod);
+
+ /* now looking at cast default expression */
+ def_expr = transformExpr(pstate, tc->def_expr, EXPR_KIND_CAST_DEFAULT);
+
+ def_expr = coerce_to_target_type(pstate, def_expr, exprType(def_expr),
+ targetType, targetTypmod,
+ COERCION_EXPLICIT,
+ COERCE_EXPLICIT_CAST,
+ exprLocation(def_expr));
+
+ assign_expr_collations(pstate, def_expr);
+
+ if (def_expr == NULL)
+ ereport(ERROR,
+ errcode(ERRCODE_CANNOT_COERCE),
+ errmsg("cannot coerce %s expression to type %s",
+ "CAST DEFAULT",
+ format_type_be(targetType)),
+ parser_coercion_errposition(pstate, exprLocation(tc->def_expr), def_expr));
+
+ /*
+ * The collation of DEFAULT expression must match the collation of the
+ * target type.
+ */
+ if (OidIsValid(typeColl))
+ {
+ Oid defColl;
+
+ defColl = exprCollation(def_expr);
+
+ if (OidIsValid(defColl) && typeColl != defColl)
+ ereport(ERROR,
+ errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("the collation of CAST DEFAULT expression conflicts with target type collation"),
+ errdetail("\"%s\" versus \"%s\"",
+ get_collation_name(typeColl),
+ get_collation_name(defColl)),
+ parser_errposition(pstate, exprLocation(def_expr)));
+ }
+
+ /*
+ * If the type cast 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(tcast->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))
+ {
+ source_expr = transformArrayExpr(pstate,
+ (A_ArrayExpr *) tcast->arg,
+ targetBaseType,
+ elementType,
+ targetBaseTypmod,
+ &can_coerce);
+ }
+ else
+ source_expr = transformExprRecurse(pstate, tcast->arg);
+ }
+ else
+ source_expr = transformExprRecurse(pstate, tcast->arg);
+
+ inputType = exprType(source_expr);
+ if (inputType == InvalidOid)
+ return (Node *) NULL; /* return NULL if NULL input */
+
+ if (can_coerce && IsA(source_expr, Const))
+ {
+ if (exprType(source_expr) == UNKNOWNOID)
+ can_coerce = CoerceUnknownConstSafe(pstate,
+ source_expr,
+ targetType,
+ targetTypmod);
+ expr_is_const = true;
+ }
+
+ /*
+ * Location of the coercion is preferentially the location of 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 != NULL && 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 expression is specified in %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);
+
+ if (expr_is_const)
+ {
+ /*
+ * Use evaluate_expr_safe to pre-evaluate simple constant cast
+ * expressions early, in a way that tolter errors .
+ *
+ * Rationale:
+ * 1. When deparsing safe cast expressions (or in other cases),
+ * eval_const_expressions might be invoked, but it cannot
+ * handle errors gracefully.
+ * 2. If the cast expression involves only simple constants, we
+ * can safely evaluate it ahead of time. If the evaluation
+ * fails, it indicates that such a cast is not possible, and
+ * we can then fall back to the CAST DEFAULT expression.
+ */
+ FuncExpr *newexpr = (FuncExpr *) copyObject(cast_expr);
+
+ assign_expr_collations(pstate, (Node *) newexpr);
+
+ cast_expr = (Node *) evaluate_expr_safe((Expr *) newexpr,
+ targetType,
+ targetTypmod,
+ typeColl);
+ }
+ }
+
+ /*
+ * set cast_expr back as is, cast_expr can be NULL after
+ * evaluate_expr_safe
+ */
+ if (cast_expr != NULL)
+ cast_expr = origexpr;
+ }
+
+ Assert(can_coerce || cast_expr == NULL);
+
+ result = makeNode(SafeTypeCastExpr);
+ result->source_expr = source_expr;
+ result->cast_expr = cast_expr;
+ result->default_expr = def_expr;
+ result->resulttype = targetType;
+ result->resulttypmod = targetTypmod;
+ result->resultcollid = typeColl;
+
+ return (Node *) result;
+}
+
/*
* Handle an explicit COLLATE clause.
*
@@ -3193,6 +3558,8 @@ ParseExprKindName(ParseExprKind exprKind)
case EXPR_KIND_CHECK_CONSTRAINT:
case EXPR_KIND_DOMAIN_CHECK:
return "CHECK";
+ case EXPR_KIND_CAST_DEFAULT:
+ return "CAST DEFAULT";
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
return "DEFAULT";
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 778d69c6f3c..a90705b9847 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -2743,6 +2743,9 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location)
case EXPR_KIND_DOMAIN_CHECK:
err = _("set-returning functions are not allowed in check constraints");
break;
+ case EXPR_KIND_CAST_DEFAULT:
+ err = _("set-returning functions are not allowed in CAST DEFAULT expressions");
+ break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
err = _("set-returning functions are not allowed in DEFAULT expressions");
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 905c975d83b..d67e5f8a5b7 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -1822,6 +1822,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/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index a8951f55b93..31cb36a56e6 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -3289,6 +3289,19 @@ 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 (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 050eef97a4c..4278e44b4a9 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -10558,6 +10558,22 @@ 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/include/catalog/pg_cast.dat b/src/include/catalog/pg_cast.dat
index fbfd669587f..1ef59a982af 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', casterrorsafe => 't' },
# 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..34a884e3196 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_SAFETYPE_CAST */
+ 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);
+extern 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/nodes/execnodes.h b/src/include/nodes/execnodes.h
index a36653c37f9..015cd9f47e7 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1053,6 +1053,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 dc09d1a3f03..83549151bfb 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -400,6 +400,12 @@ typedef struct TypeCast
ParseLoc location; /* token location, or -1 if unknown */
} TypeCast;
+typedef struct SafeTypeCast
+{
+ NodeTag type;
+ Node *cast;
+ Node *def_expr; /* default expr */
+} SafeTypeCast;
/*
* CollateClause - a COLLATE expression
*/
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 1b4436f2ff6..70b72ed2f40 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -769,6 +769,39 @@ 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
+{
+ Expr xpr;
+
+ /* transformed expression being casted */
+ Node *source_expr;
+
+ /*
+ * The transformed cast expression; It will NULL if can not cast source type
+ * to target type
+ */
+ Node *cast_expr pg_node_attr(query_jumble_ignore);
+
+ /* Fall back to the default expression if cast evaluation fails */
+ Node *default_expr;
+
+ /* cast result data type */
+ Oid resulttype;
+
+ /* cast result data type typmod (usually -1) */
+ int32 resulttypmod;
+
+ /* cast result data type collation */
+ Oid resultcollid;
+
+} SafeTypeCastExpr;
+
/*
* FuncExpr - expression node for a function call
*
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index f7d07c84542..9f5b32e0360 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -67,6 +67,8 @@ typedef enum ParseExprKind
EXPR_KIND_VALUES_SINGLE, /* single-row VALUES (in INSERT only) */
EXPR_KIND_CHECK_CONSTRAINT, /* CHECK constraint for a table */
EXPR_KIND_DOMAIN_CHECK, /* CHECK constraint for a domain */
+ EXPR_KIND_CAST_DEFAULT, /* default expression in
+ CAST DEFAULT ON CONVERSION ERROR */
EXPR_KIND_COLUMN_DEFAULT, /* default value for a table column */
EXPR_KIND_FUNCTION_DEFAULT, /* default parameter value for function */
EXPR_KIND_INDEX_EXPRESSION, /* index expression */
diff --git a/src/test/regress/expected/cast.out b/src/test/regress/expected/cast.out
new file mode 100644
index 00000000000..6614ba3370c
--- /dev/null
+++ b/src/test/regress/expected/cast.out
@@ -0,0 +1,681 @@
+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 expression is specified in 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 coerce CAST DEFAULT expression to type date
+LINE 1: SELECT CAST('a' as date DEFAULT ret_int8() ON CONVERSION ERR...
+ ^
+-- test array coerce
+SELECT CAST(array[11.12] AS int[] DEFAULT NULL ON CONVERSION ERROR);
+ array
+-------
+ {11}
+(1 row)
+
+SELECT CAST(array[11.12] AS date[] DEFAULT NULL ON CONVERSION ERROR);
+ array
+-------
+
+(1 row)
+
+SELECT CAST(array[11111111111111111] AS int[] DEFAULT NULL ON CONVERSION ERROR);
+ array
+-------
+
+(1 row)
+
+SELECT CAST(array[['abc'],[456]] AS int[] DEFAULT NULL ON CONVERSION ERROR);
+ array
+-------
+
+(1 row)
+
+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: set-returning functions are not allowed in CAST DEFAULT expressions
+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: aggregate functions are not allowed in CAST DEFAULT expressions
+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: window functions are not allowed in CAST DEFAULT expressions
+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 DOMAIN d_varchar as varchar(3) NOT NULL;
+CREATE TYPE comp_domain_with_typmod AS (a d_char3_not_null, b int);
+CREATE TYPE comp2 AS (a d_varchar);
+SELECT CAST('(NULL)' AS comp2 DEFAULT '(1232)' ON CONVERSION ERROR); --error
+ERROR: value too long for type character varying(3)
+LINE 1: SELECT CAST('(NULL)' AS comp2 DEFAULT '(1232)' ON CONVERSION...
+ ^
+SELECT CAST('(NULL)' AS comp2 DEFAULT '(123)' ON CONVERSION ERROR); --ok
+ comp2
+-------
+ (123)
+(1 row)
+
+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 with geometry data type
+SELECT CAST('(1,2)'::point AS box DEFAULT NULL ON CONVERSION ERROR);
+ box
+-------------
+ (1,2),(1,2)
+(1 row)
+
+SELECT CAST('[(NaN,1),(NaN,infinity)]'::lseg AS point DEFAULT NULL ON CONVERSION ERROR);
+ point
+----------------
+ (NaN,Infinity)
+(1 row)
+
+SELECT CAST('[(1e+300,Infinity),(1e+300,Infinity)]'::lseg AS point DEFAULT NULL ON CONVERSION ERROR);
+ point
+-------------------
+ (1e+300,Infinity)
+(1 row)
+
+SELECT CAST('[(1,2),(3,4)]'::path as polygon DEFAULT NULL ON CONVERSION ERROR);
+ polygon
+---------
+
+(1 row)
+
+SELECT CAST('(NaN,1.0,NaN,infinity)'::box AS point DEFAULT NULL ON CONVERSION ERROR);
+ point
+----------------
+ (NaN,Infinity)
+(1 row)
+
+SELECT CAST('(2.0,2.0,0.0,0.0)'::box AS lseg DEFAULT NULL ON CONVERSION ERROR);
+ lseg
+---------------
+ [(2,2),(0,0)]
+(1 row)
+
+SELECT CAST('(2.0,2.0,0.0,0.0)'::box AS polygon DEFAULT NULL ON CONVERSION ERROR);
+ polygon
+---------------------------
+ ((0,0),(0,2),(2,2),(2,0))
+(1 row)
+
+SELECT CAST('(2.0,2.0,0.0,0.0)'::box AS path DEFAULT NULL ON CONVERSION ERROR);
+ path
+------
+
+(1 row)
+
+SELECT CAST('(2.0,infinity,NaN,infinity)'::box AS circle DEFAULT NULL ON CONVERSION ERROR);
+ circle
+----------------------
+ <(NaN,Infinity),NaN>
+(1 row)
+
+SELECT CAST('(NaN,0.0),(2.0,4.0),(0.0,infinity)'::polygon AS point DEFAULT NULL ON CONVERSION ERROR);
+ point
+----------------
+ (NaN,Infinity)
+(1 row)
+
+SELECT CAST('(2.0,0.0),(2.0,4.0),(0.0,0.0)'::polygon AS path DEFAULT NULL ON CONVERSION ERROR);
+ path
+---------------------
+ ((2,0),(2,4),(0,0))
+(1 row)
+
+SELECT CAST('(2.0,0.0),(2.0,4.0),(0.0,0.0)'::polygon AS box DEFAULT NULL ON CONVERSION ERROR);
+ box
+-------------
+ (2,4),(0,0)
+(1 row)
+
+SELECT CAST('(NaN,infinity),(2.0,4.0),(0.0,infinity)'::polygon AS circle DEFAULT NULL ON CONVERSION ERROR);
+ circle
+----------------------
+ <(NaN,Infinity),NaN>
+(1 row)
+
+SELECT CAST('<(5,1),3>'::circle AS point DEFAULT NULL ON CONVERSION ERROR);
+ point
+-------
+ (5,1)
+(1 row)
+
+SELECT CAST('<(3,5),0>'::circle as box DEFAULT NULL ON CONVERSION ERROR);
+ box
+-------------
+ (3,5),(3,5)
+(1 row)
+
+SELECT CAST('<(3,5),0>'::circle as polygon DEFAULT NULL ON CONVERSION ERROR);
+ERROR: cannot convert circle with radius zero to polygon
+CONTEXT: SQL function "polygon" statement 1
+-----safe cast from/to money type is not supported, all the following would result error
+SELECT CAST(NULL::int8 AS money DEFAULT NULL ON CONVERSION ERROR);
+ERROR: cannot cast type bigint to money when DEFAULT expression is specified in 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: cannot cast type integer to money when DEFAULT expression is specified in 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: cannot cast type numeric to money when DEFAULT expression is specified in 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: cannot cast type money to numeric when DEFAULT expression is specified in 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
+/*
+the following are safe cast test with type
+{
+ bytea, bit character, text, character-varying,
+ inet, macaddr8
+ int2, integer, bigint, numeric, float4, float8,
+ date, interval, timestamptz, timestamp
+ jsonb
+}
+*/
+-----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)
+
+SELECT CAST('111111111100001'::bit(100) AS INT DEFAULT NULL ON CONVERSION ERROR);
+ int4
+------
+
+(1 row)
+
+SELECT CAST ('111111111100001'::bit(100) AS INT8 DEFAULT NULL ON CONVERSION ERROR);
+ int8
+------
+
+(1 row)
+
+select CAST('a.b.c.d'::text as regclass default NULL on conversion error);
+ regclass
+----------
+
+(1 row)
+
+CREATE TABLE test_safecast(col0 text);
+INSERT INTO test_safecast(col0) VALUES ('<value>one</value');
+SELECT col0 as text,
+ CAST(col0 AS regclass DEFAULT NULL ON CONVERSION ERROR) as to_regclass,
+ CAST(col0 AS regclass DEFAULT NULL ON CONVERSION ERROR) IS NULL as expect_true,
+ 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 test_safecast;
+ text | to_regclass | expect_true | to_char | to_name | to_xml
+-------------------+-------------+-------------+---------+-------------------+--------
+ <value>one</value | | t | < | <value>one</value |
+(1 row)
+
+-----safe cast from other type to inet/cidr
+SELECT CAST('192.168.1.x' as inet DEFAULT NULL ON CONVERSION ERROR);
+ inet
+------
+
+(1 row)
+
+SELECT CAST('192.168.1.2/30' as cidr DEFAULT NULL ON CONVERSION ERROR);
+ cidr
+------
+
+(1 row)
+
+SELECT CAST('22:00:5c:08:55:08:01:02'::macaddr8 as macaddr DEFAULT NULL ON CONVERSION ERROR);
+ macaddr
+---------
+
+(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)
+
+--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 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)
+
+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 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)
+
+--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 TEMP TABLE test_safecast1(col0 jsonb);
+INSERT INTO test_safecast1(col0) VALUES ('"test"');
+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 test_safecast1;
+ jsonb | to_integer | to_numeric | to_int8 | to_float4 | to_float8 | to_bool | to_smallint
+--------+------------+------------+---------+-----------+-----------+---------+-------------
+ "test" | | | | | | |
+(1 row)
+
+-- test deparse
+CREATE DOMAIN d_int_arr as int[];
+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'::text], ARRAY['a'::text]] 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 VIEW safecastview1 AS
+SELECT CAST(ARRAY[['1'], ['three'],['a']] AS d_int_arr DEFAULT '{1,2}' ON CONVERSION ERROR) as cast1,
+ CAST(ARRAY[['1', '2'], ['three', 'a']] AS d_int_arr DEFAULT '{21,22}' ON CONVERSION ERROR) as cast2;
+\sv safecastview1
+CREATE OR REPLACE VIEW public.safecastview1 AS
+ SELECT CAST(ARRAY[ARRAY[1], ARRAY['three'::text], ARRAY['a'::text]] AS d_int_arr DEFAULT '{1,2}'::integer[]::d_int_arr ON CONVERSION ERROR) AS cast1,
+ CAST(ARRAY[ARRAY[1, 2], ARRAY['three'::text, 'a'::text]] AS d_int_arr DEFAULT '{21,22}'::integer[]::d_int_arr ON CONVERSION ERROR) AS cast2
+SELECT * FROM safecastview1;
+ cast1 | cast2
+-------+---------
+ {1,2} | {21,22}
+(1 row)
+
+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 0e69644bca2..38eab453344 100644
--- a/src/test/regress/expected/create_cast.out
+++ b/src/test/regress/expected/create_cast.out
@@ -88,6 +88,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 expression is specified in 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 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/parallel_schedule b/src/test/regress/parallel_schedule
index f9450cdc477..573a6699a63 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..eb68ec21859
--- /dev/null
+++ b/src/test/regress/sql/cast.sql
@@ -0,0 +1,301 @@
+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(array[11.12] AS int[] DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST(array[11.12] AS date[] DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST(array[11111111111111111] AS int[] DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST(array[['abc'],[456]] AS int[] DEFAULT NULL ON CONVERSION ERROR);
+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 DOMAIN d_varchar as varchar(3) NOT NULL;
+CREATE TYPE comp_domain_with_typmod AS (a d_char3_not_null, b int);
+CREATE TYPE comp2 AS (a d_varchar);
+SELECT CAST('(NULL)' AS comp2 DEFAULT '(1232)' ON CONVERSION ERROR); --error
+SELECT CAST('(NULL)' AS comp2 DEFAULT '(123)' ON CONVERSION ERROR); --ok
+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 with geometry data type
+SELECT CAST('(1,2)'::point AS box DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('[(NaN,1),(NaN,infinity)]'::lseg AS point DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('[(1e+300,Infinity),(1e+300,Infinity)]'::lseg AS point DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('[(1,2),(3,4)]'::path as polygon DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('(NaN,1.0,NaN,infinity)'::box AS point DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('(2.0,2.0,0.0,0.0)'::box AS lseg DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('(2.0,2.0,0.0,0.0)'::box AS polygon DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('(2.0,2.0,0.0,0.0)'::box AS path DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('(2.0,infinity,NaN,infinity)'::box AS circle DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('(NaN,0.0),(2.0,4.0),(0.0,infinity)'::polygon AS point DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('(2.0,0.0),(2.0,4.0),(0.0,0.0)'::polygon AS path DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('(2.0,0.0),(2.0,4.0),(0.0,0.0)'::polygon AS box DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('(NaN,infinity),(2.0,4.0),(0.0,infinity)'::polygon AS circle DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('<(5,1),3>'::circle AS point DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('<(3,5),0>'::circle as box DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('<(3,5),0>'::circle as polygon DEFAULT NULL ON CONVERSION ERROR);
+
+-----safe cast from/to money type is not supported, all the following would result error
+SELECT CAST(NULL::int8 AS money DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST(NULL::int4 AS money DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST(NULL::numeric AS money DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST(NULL::money AS numeric DEFAULT NULL ON CONVERSION ERROR);
+
+/*
+the following are safe cast test with type
+{
+ bytea, bit character, text, character-varying,
+ inet, macaddr8
+ int2, integer, bigint, numeric, float4, float8,
+ date, interval, timestamptz, timestamp
+ jsonb
+}
+*/
+-----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);
+
+SELECT CAST('111111111100001'::bit(100) AS INT DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST ('111111111100001'::bit(100) AS INT8 DEFAULT NULL ON CONVERSION ERROR);
+
+select CAST('a.b.c.d'::text as regclass default NULL on conversion error);
+
+CREATE TABLE test_safecast(col0 text);
+INSERT INTO test_safecast(col0) VALUES ('<value>one</value');
+
+SELECT col0 as text,
+ CAST(col0 AS regclass DEFAULT NULL ON CONVERSION ERROR) as to_regclass,
+ CAST(col0 AS regclass DEFAULT NULL ON CONVERSION ERROR) IS NULL as expect_true,
+ 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 test_safecast;
+
+-----safe cast from other type to inet/cidr
+SELECT CAST('192.168.1.x' as inet DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('192.168.1.2/30' as cidr DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('22:00:5c:08:55:08:01:02'::macaddr8 as macaddr 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);
+
+--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 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;
+
+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 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;
+
+--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 TEMP TABLE test_safecast1(col0 jsonb);
+INSERT INTO test_safecast1(col0) VALUES ('"test"');
+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 test_safecast1;
+
+-- test deparse
+CREATE DOMAIN d_int_arr as int[];
+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 VIEW safecastview1 AS
+SELECT CAST(ARRAY[['1'], ['three'],['a']] AS d_int_arr DEFAULT '{1,2}' ON CONVERSION ERROR) as cast1,
+ CAST(ARRAY[['1', '2'], ['three', 'a']] AS d_int_arr DEFAULT '{21,22}' ON CONVERSION ERROR) as cast2;
+\sv safecastview1
+SELECT * FROM safecastview1;
+
+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 5290b91e83e..7996854b3a5 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2660,6 +2660,9 @@ STRLEN
SV
SYNCHRONIZATION_BARRIER
SYSTEM_INFO
+SafeTypeCast
+SafeTypeCastExpr
+SafeTypeCastState
SampleScan
SampleScanGetSampleSize_function
SampleScanState
--
2.34.1
[text/x-patch] v7-0016-error-safe-for-casting-timestamp-to-other-types-per-pg_cast.patch (3.4K, 4-v7-0016-error-safe-for-casting-timestamp-to-other-types-per-pg_cast.patch)
download | inline diff:
From 2f2168688b59e2d3d0568636f32190afe67e969e Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Thu, 9 Oct 2025 18:44:35 +0800
Subject: [PATCH v7 16/20] 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)
discussion: https://postgr.es/m/CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@mail.gmail.com
---
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 116e3ef28fc..7a57ac3eaf6 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);
}
@@ -6430,7 +6431,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] v7-0004-error-safe-for-casting-text-to-other-types-per-pg_cast.patch (10.7K, 5-v7-0004-error-safe-for-casting-text-to-other-types-per-pg_cast.patch)
download | inline diff:
From a402051d73e3b3ed86f2972639bd3910ee7b7cff Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Thu, 9 Oct 2025 15:49:46 +0800
Subject: [PATCH v7 04/20] 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.
discussion: https://postgr.es/m/CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@mail.gmail.com
---
src/backend/catalog/namespace.c | 167 ++++++++++++++++++++++++++++++++
src/backend/utils/adt/regproc.c | 33 ++++++-
src/backend/utils/adt/varlena.c | 42 ++++++++
src/include/catalog/namespace.h | 6 ++
src/include/utils/varlena.h | 1 +
5 files changed, 246 insertions(+), 3 deletions(-)
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index d23474da4fb..a04d179c7fb 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -640,6 +640,137 @@ RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode,
return relId;
}
+/* safe version of RangeVarGetRelidExtended */
+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)
+ ereturn(escontext, InvalidOid,
+ errcode(ERRCODE_UNDEFINED_TABLE),
+ errmsg("relation \"%s.%s\" does not exist",
+ relation->schemaname, relation->relname));
+ else
+ ereturn(escontext, InvalidOid,
+ errcode(ERRCODE_UNDEFINED_TABLE),
+ errmsg("relation \"%s\" does not exist",
+ relation->relname));
+ }
+ return relId;
+}
+
/*
* RangeVarGetCreationNamespace
* Given a RangeVar describing a to-be-created relation,
@@ -3650,6 +3781,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 e5c2246f2c9..059d0154335 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -1902,10 +1902,37 @@ 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 = makeRangeVarFromNameListSafe(rvnames, fcinfo->context);
+ if (SOFT_ERROR_OCCURRED(fcinfo->context))
+ PG_RETURN_NULL();
+
+ 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 2c398cd9e5c..1117939430a 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -2717,6 +2717,48 @@ textToQualifiedNameList(text *textval)
return result;
}
+/* error safe version of textToQualifiedNameList */
+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 f1423f28c32..ab61af55ddc 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -103,6 +103,11 @@ 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,
@@ -168,6 +173,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] v7-0017-error-safe-for-casting-jsonb-to-other-types-per-pg_cast.patch (6.5K, 6-v7-0017-error-safe-for-casting-jsonb-to-other-types-per-pg_cast.patch)
download | inline diff:
From 8f462532bbcc356181f212eeb9f8f1a070b88efe Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Thu, 9 Oct 2025 18:58:40 +0800
Subject: [PATCH v7 17/20] 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)
discussion: https://postgr.es/m/CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@mail.gmail.com
---
src/backend/utils/adt/jsonb.c | 91 +++++++++++++++++++++++++++++------
1 file changed, 75 insertions(+), 16 deletions(-)
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index da94d424d61..1ff00f72f78 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -2005,7 +2005,7 @@ JsonbExtractScalar(JsonbContainer *jbc, JsonbValue *res)
* Emit correct, translatable cast error message
*/
static void
-cannotCastJsonbValue(enum jbvType type, const char *sqltype)
+cannotCastJsonbValue(enum jbvType type, const char *sqltype, Node *escontext)
{
static const struct
{
@@ -2026,9 +2026,12 @@ cannotCastJsonbValue(enum jbvType type, const char *sqltype)
for (i = 0; i < lengthof(messages); i++)
if (messages[i].type == type)
- ereport(ERROR,
+ {
+ errsave(escontext,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg(messages[i].msg, sqltype)));
+ return;
+ }
/* should be unreachable */
elog(ERROR, "unknown jsonb type: %d", (int) type);
@@ -2041,7 +2044,11 @@ jsonb_bool(PG_FUNCTION_ARGS)
JsonbValue v;
if (!JsonbExtractScalar(&in->root, &v))
- cannotCastJsonbValue(v.type, "boolean");
+ {
+ cannotCastJsonbValue(v.type, "boolean", fcinfo->context);
+
+ PG_RETURN_NULL();
+ }
if (v.type == jbvNull)
{
@@ -2050,7 +2057,11 @@ jsonb_bool(PG_FUNCTION_ARGS)
}
if (v.type != jbvBool)
- cannotCastJsonbValue(v.type, "boolean");
+ {
+ cannotCastJsonbValue(v.type, "boolean", fcinfo->context);
+
+ PG_RETURN_NULL();
+ }
PG_FREE_IF_COPY(in, 0);
@@ -2065,7 +2076,11 @@ jsonb_numeric(PG_FUNCTION_ARGS)
Numeric retValue;
if (!JsonbExtractScalar(&in->root, &v))
- cannotCastJsonbValue(v.type, "numeric");
+ {
+ cannotCastJsonbValue(v.type, "numeric", fcinfo->context);
+
+ PG_RETURN_NULL();
+ }
if (v.type == jbvNull)
{
@@ -2074,7 +2089,11 @@ jsonb_numeric(PG_FUNCTION_ARGS)
}
if (v.type != jbvNumeric)
- cannotCastJsonbValue(v.type, "numeric");
+ {
+ cannotCastJsonbValue(v.type, "numeric", fcinfo->context);
+
+ PG_RETURN_NULL();
+ }
/*
* v.val.numeric points into jsonb body, so we need to make a copy to
@@ -2095,7 +2114,11 @@ jsonb_int2(PG_FUNCTION_ARGS)
Datum retValue;
if (!JsonbExtractScalar(&in->root, &v))
- cannotCastJsonbValue(v.type, "smallint");
+ {
+ cannotCastJsonbValue(v.type, "smallint", fcinfo->context);
+
+ PG_RETURN_NULL();
+ }
if (v.type == jbvNull)
{
@@ -2104,7 +2127,11 @@ jsonb_int2(PG_FUNCTION_ARGS)
}
if (v.type != jbvNumeric)
- cannotCastJsonbValue(v.type, "smallint");
+ {
+ cannotCastJsonbValue(v.type, "smallint", fcinfo->context);
+
+ PG_RETURN_NULL();
+ }
retValue = DirectFunctionCall1(numeric_int2,
NumericGetDatum(v.val.numeric));
@@ -2122,7 +2149,11 @@ jsonb_int4(PG_FUNCTION_ARGS)
Datum retValue;
if (!JsonbExtractScalar(&in->root, &v))
- cannotCastJsonbValue(v.type, "integer");
+ {
+ cannotCastJsonbValue(v.type, "integer", fcinfo->context);
+
+ PG_RETURN_NULL();
+ }
if (v.type == jbvNull)
{
@@ -2131,7 +2162,11 @@ jsonb_int4(PG_FUNCTION_ARGS)
}
if (v.type != jbvNumeric)
- cannotCastJsonbValue(v.type, "integer");
+ {
+ cannotCastJsonbValue(v.type, "integer", fcinfo->context);
+
+ PG_RETURN_NULL();
+ }
retValue = DirectFunctionCall1(numeric_int4,
NumericGetDatum(v.val.numeric));
@@ -2149,7 +2184,11 @@ jsonb_int8(PG_FUNCTION_ARGS)
Datum retValue;
if (!JsonbExtractScalar(&in->root, &v))
- cannotCastJsonbValue(v.type, "bigint");
+ {
+ cannotCastJsonbValue(v.type, "bigint", fcinfo->context);
+
+ PG_RETURN_NULL();
+ }
if (v.type == jbvNull)
{
@@ -2158,7 +2197,11 @@ jsonb_int8(PG_FUNCTION_ARGS)
}
if (v.type != jbvNumeric)
- cannotCastJsonbValue(v.type, "bigint");
+ {
+ cannotCastJsonbValue(v.type, "bigint", fcinfo->context);
+
+ PG_RETURN_NULL();
+ }
retValue = DirectFunctionCall1(numeric_int8,
NumericGetDatum(v.val.numeric));
@@ -2176,7 +2219,11 @@ jsonb_float4(PG_FUNCTION_ARGS)
Datum retValue;
if (!JsonbExtractScalar(&in->root, &v))
- cannotCastJsonbValue(v.type, "real");
+ {
+ cannotCastJsonbValue(v.type, "real", fcinfo->context);
+
+ PG_RETURN_NULL();
+ }
if (v.type == jbvNull)
{
@@ -2185,7 +2232,11 @@ jsonb_float4(PG_FUNCTION_ARGS)
}
if (v.type != jbvNumeric)
- cannotCastJsonbValue(v.type, "real");
+ {
+ cannotCastJsonbValue(v.type, "real", fcinfo->context);
+
+ PG_RETURN_NULL();
+ }
retValue = DirectFunctionCall1(numeric_float4,
NumericGetDatum(v.val.numeric));
@@ -2203,7 +2254,11 @@ jsonb_float8(PG_FUNCTION_ARGS)
Datum retValue;
if (!JsonbExtractScalar(&in->root, &v))
- cannotCastJsonbValue(v.type, "double precision");
+ {
+ cannotCastJsonbValue(v.type, "double precision", fcinfo->context);
+
+ PG_RETURN_NULL();
+ }
if (v.type == jbvNull)
{
@@ -2212,7 +2267,11 @@ jsonb_float8(PG_FUNCTION_ARGS)
}
if (v.type != jbvNumeric)
- cannotCastJsonbValue(v.type, "double precision");
+ {
+ cannotCastJsonbValue(v.type, "double precision", fcinfo->context);
+
+ PG_RETURN_NULL();
+ }
retValue = DirectFunctionCall1(numeric_float8,
NumericGetDatum(v.val.numeric));
--
2.34.1
[text/x-patch] v7-0013-error-safe-for-casting-date-to-other-types-per-pg_cast.patch (2.1K, 7-v7-0013-error-safe-for-casting-date-to-other-types-per-pg_cast.patch)
download | inline diff:
From d5c577a6ce3e69417940dbba049e2abde9bd934e Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Thu, 9 Oct 2025 18:42:50 +0800
Subject: [PATCH v7 13/20] 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)
discussion: https://postgr.es/m/CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@mail.gmail.com
---
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] v7-0014-error-safe-for-casting-interval-to-other-types-per-pg_cast.patch (2.1K, 8-v7-0014-error-safe-for-casting-interval-to-other-types-per-pg_cast.patch)
download | inline diff:
From 14337c6ba6e656d85bcf95aa4c18c078fbbfb740 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Thu, 9 Oct 2025 18:43:29 +0800
Subject: [PATCH v7 14/20] 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)
discussion: https://postgr.es/m/CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@mail.gmail.com
---
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 156a4830ffd..7b565cc6d66 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] v7-0015-error-safe-for-casting-timestamptz-to-other-types-per-pg_cast.patch (3.8K, 9-v7-0015-error-safe-for-casting-timestamptz-to-other-types-per-pg_cast.patch)
download | inline diff:
From e56515b37e3a5601d7bd315ba8816a7d569c7824 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Mon, 6 Oct 2025 12:39:22 +0800
Subject: [PATCH v7 15/20] 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)
discussion: https://postgr.es/m/CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@mail.gmail.com
---
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 7b565cc6d66..116e3ef28fc 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);
}
@@ -6507,7 +6508,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] v7-0012-error-safe-for-casting-float8-to-other-types-per-pg_cast.patch (3.9K, 10-v7-0012-error-safe-for-casting-float8-to-other-types-per-pg_cast.patch)
download | inline diff:
From 5558c75ac6876a83e739a4a219f4f166f3fa8a06 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Thu, 9 Oct 2025 18:41:55 +0800
Subject: [PATCH v7 12/20] 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)
discussion: https://postgr.es/m/CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@mail.gmail.com
---
src/backend/utils/adt/float.c | 29 +++++++++++++++++++++++++----
src/backend/utils/adt/int8.c | 6 +++++-
src/backend/utils/adt/numeric.c | 3 ++-
3 files changed, 32 insertions(+), 6 deletions(-)
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index dd8a2ff378b..6747544b679 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -1199,9 +1199,22 @@ dtof(PG_FUNCTION_ARGS)
result = (float4) num;
if (unlikely(isinf(result)) && !isinf(num))
- float_overflow_error();
+ {
+ errsave(fcinfo->context,
+ errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: overflow"));
+
+ PG_RETURN_NULL();
+ }
+
if (unlikely(result == 0.0f) && num != 0.0)
- float_underflow_error();
+ {
+ errsave(fcinfo->context,
+ errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: underflow"));
+
+ PG_RETURN_NULL();
+ }
PG_RETURN_FLOAT4(result);
}
@@ -1224,10 +1237,14 @@ dtoi4(PG_FUNCTION_ARGS)
/* Range check */
if (unlikely(isnan(num) || !FLOAT8_FITS_IN_INT32(num)))
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
+ PG_RETURN_NULL();
+ }
+
PG_RETURN_INT32((int32) num);
}
@@ -1249,10 +1266,14 @@ dtoi2(PG_FUNCTION_ARGS)
/* Range check */
if (unlikely(isnan(num) || !FLOAT8_FITS_IN_INT16(num)))
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range")));
+ PG_RETURN_NULL();
+ }
+
PG_RETURN_INT16((int16) num);
}
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index bcdb020a91c..437dbbccd4a 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -1268,10 +1268,14 @@ dtoi8(PG_FUNCTION_ARGS)
/* Range check */
if (unlikely(isnan(num) || !FLOAT8_FITS_IN_INT64(num)))
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("bigint out of range")));
+ PG_RETURN_NULL();
+ }
+
PG_RETURN_INT64((int64) num);
}
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 8839b095f60..76cd9800c2a 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -4570,7 +4570,8 @@ float8_numeric(PG_FUNCTION_ARGS)
init_var(&result);
/* Assume we need not worry about leading/trailing spaces */
- (void) set_var_from_str(buf, buf, &result, &endptr, NULL);
+ if (!set_var_from_str(buf, buf, &result, &endptr, fcinfo->context))
+ PG_RETURN_NULL();
res = make_result(&result);
--
2.34.1
[text/x-patch] v7-0011-error-safe-for-casting-float4-to-other-types-per-pg_cast.patch (3.3K, 11-v7-0011-error-safe-for-casting-float4-to-other-types-per-pg_cast.patch)
download | inline diff:
From ab4382ec5ad71348279f6fd1be3d04d66bb3399e Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Thu, 9 Oct 2025 18:33:57 +0800
Subject: [PATCH v7 11/20] 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)
discussion: https://postgr.es/m/CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@mail.gmail.com
---
src/backend/utils/adt/float.c | 12 ++++++++++--
src/backend/utils/adt/int8.c | 6 +++++-
src/backend/utils/adt/numeric.c | 3 ++-
3 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 7b97d2be6ca..dd8a2ff378b 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -1298,10 +1298,14 @@ ftoi4(PG_FUNCTION_ARGS)
/* Range check */
if (unlikely(isnan(num) || !FLOAT4_FITS_IN_INT32(num)))
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
+ PG_RETURN_NULL();
+ }
+
PG_RETURN_INT32((int32) num);
}
@@ -1323,10 +1327,14 @@ ftoi2(PG_FUNCTION_ARGS)
/* Range check */
if (unlikely(isnan(num) || !FLOAT4_FITS_IN_INT16(num)))
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range")));
+ PG_RETURN_NULL();
+ }
+
PG_RETURN_INT16((int16) num);
}
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index 3b2d100be92..bcdb020a91c 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -1303,10 +1303,14 @@ ftoi8(PG_FUNCTION_ARGS)
/* Range check */
if (unlikely(isnan(num) || !FLOAT4_FITS_IN_INT64(num)))
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("bigint out of range")));
+ PG_RETURN_NULL();
+ }
+
PG_RETURN_INT64((int64) num);
}
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index ce5f71109e7..8839b095f60 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -4668,7 +4668,8 @@ float4_numeric(PG_FUNCTION_ARGS)
init_var(&result);
/* Assume we need not worry about leading/trailing spaces */
- (void) set_var_from_str(buf, buf, &result, &endptr, NULL);
+ if (!set_var_from_str(buf, buf, &result, &endptr, fcinfo->context))
+ PG_RETURN_NULL();
res = make_result(&result);
--
2.34.1
[text/x-patch] v7-0010-error-safe-for-casting-numeric-to-other-types-per-pg_cast.patch (5.3K, 12-v7-0010-error-safe-for-casting-numeric-to-other-types-per-pg_cast.patch)
download | inline diff:
From f9af62970b23dfee506ee8fd46fdbee80170e6fd Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Thu, 9 Oct 2025 21:57:31 +0800
Subject: [PATCH v7 10/20] 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)
discussion: https://postgr.es/m/CACJufxHCMzrHOW=wRe8L30rMhB3sjwAv1LE928Fa7sxMu1Tx-g@mail.gmail.com
---
src/backend/utils/adt/numeric.c | 68 +++++++++++++++++++++++++--------
1 file changed, 53 insertions(+), 15 deletions(-)
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 2501007d981..ce5f71109e7 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -1244,7 +1244,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));
}
@@ -1295,8 +1296,9 @@ numeric (PG_FUNCTION_ARGS)
init_var(&var);
set_var_from_num(num, &var);
- (void) apply_typmod(&var, typmod, NULL);
- new = make_result(&var);
+ if (!apply_typmod(&var, typmod, fcinfo->context))
+ PG_RETURN_NULL();
+ new = make_result_safe(&var, fcinfo->context);
free_var(&var);
@@ -3019,7 +3021,10 @@ numeric_mul(PG_FUNCTION_ARGS)
Numeric num2 = PG_GETARG_NUMERIC(1);
Numeric res;
- res = numeric_mul_safe(num1, num2, NULL);
+ res = numeric_mul_safe(num1, num2, fcinfo->context);
+
+ if (unlikely(SOFT_ERROR_OCCURRED(fcinfo->context)))
+ PG_RETURN_NULL();
PG_RETURN_NUMERIC(res);
}
@@ -4393,9 +4398,15 @@ numeric_int4_safe(Numeric num, Node *escontext)
Datum
numeric_int4(PG_FUNCTION_ARGS)
{
+ int32 result;
Numeric num = PG_GETARG_NUMERIC(0);
- PG_RETURN_INT32(numeric_int4_safe(num, NULL));
+ result = numeric_int4_safe(num, fcinfo->context);
+
+ if (unlikely(SOFT_ERROR_OCCURRED(fcinfo->context)))
+ PG_RETURN_NULL();
+
+ PG_RETURN_INT32(result);
}
/*
@@ -4463,9 +4474,15 @@ numeric_int8_safe(Numeric num, Node *escontext)
Datum
numeric_int8(PG_FUNCTION_ARGS)
{
+ int64 result;
Numeric num = PG_GETARG_NUMERIC(0);
- PG_RETURN_INT64(numeric_int8_safe(num, NULL));
+ result = numeric_int8_safe(num, fcinfo->context);
+
+ if (unlikely(SOFT_ERROR_OCCURRED(fcinfo->context)))
+ PG_RETURN_NULL();
+
+ PG_RETURN_INT64(result);
}
@@ -4489,28 +4506,38 @@ numeric_int2(PG_FUNCTION_ARGS)
if (NUMERIC_IS_SPECIAL(num))
{
if (NUMERIC_IS_NAN(num))
- ereport(ERROR,
+ errsave(fcinfo->context,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot convert NaN to %s", "smallint")));
else
- ereport(ERROR,
+ errsave(fcinfo->context,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot convert infinity to %s", "smallint")));
+
+ PG_RETURN_NULL();
}
/* Convert to variable format and thence to int8 */
init_var_from_num(num, &x);
if (!numericvar_to_int64(&x, &val))
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range")));
+ PG_RETURN_NULL();
+ }
+
if (unlikely(val < PG_INT16_MIN) || unlikely(val > PG_INT16_MAX))
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range")));
+ PG_RETURN_NULL();
+ }
+
/* Down-convert to int2 */
result = (int16) val;
@@ -4572,10 +4599,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);
}
@@ -4667,7 +4698,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] v7-0008-error-safe-for-casting-integer-to-other-types-per-pg_cast.patch (3.0K, 13-v7-0008-error-safe-for-casting-integer-to-other-types-per-pg_cast.patch)
download | inline diff:
From afa97fc4300711baf573799a7f363644fee03b4c Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Thu, 9 Oct 2025 16:04:07 +0800
Subject: [PATCH v7 08/20] 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.
discussion: https://postgr.es/m/CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@mail.gmail.com
---
src/backend/utils/adt/char.c | 6 +++++-
src/backend/utils/adt/int.c | 5 ++++-
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/src/backend/utils/adt/char.c b/src/backend/utils/adt/char.c
index 22dbfc950b1..7cf09d954e7 100644
--- a/src/backend/utils/adt/char.c
+++ b/src/backend/utils/adt/char.c
@@ -192,10 +192,14 @@ i4tochar(PG_FUNCTION_ARGS)
int32 arg1 = PG_GETARG_INT32(0);
if (arg1 < SCHAR_MIN || arg1 > SCHAR_MAX)
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("\"char\" out of range")));
+ PG_RETURN_NULL();
+ }
+
PG_RETURN_CHAR((int8) arg1);
}
diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c
index b5781989a64..4d3ce23a4af 100644
--- a/src/backend/utils/adt/int.c
+++ b/src/backend/utils/adt/int.c
@@ -350,10 +350,13 @@ i4toi2(PG_FUNCTION_ARGS)
int32 arg1 = PG_GETARG_INT32(0);
if (unlikely(arg1 < SHRT_MIN) || unlikely(arg1 > SHRT_MAX))
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range")));
+ PG_RETURN_NULL();
+ }
PG_RETURN_INT16((int16) arg1);
}
--
2.34.1
[text/x-patch] v7-0009-error-safe-for-casting-bigint-to-other-types-per-pg_cast.patch (4.0K, 14-v7-0009-error-safe-for-casting-bigint-to-other-types-per-pg_cast.patch)
download | inline diff:
From ecce9850b5793174fb67699b95106c363820888c Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Thu, 9 Oct 2025 16:38:37 +0800
Subject: [PATCH v7 09/20] 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
discussion: https://postgr.es/m/CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@mail.gmail.com
---
src/backend/utils/adt/int8.c | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index bdea490202a..3b2d100be92 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -1204,10 +1204,14 @@ int84(PG_FUNCTION_ARGS)
int64 arg = PG_GETARG_INT64(0);
if (unlikely(arg < PG_INT32_MIN) || unlikely(arg > PG_INT32_MAX))
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
+ PG_RETURN_NULL();
+ }
+
PG_RETURN_INT32((int32) arg);
}
@@ -1225,10 +1229,14 @@ int82(PG_FUNCTION_ARGS)
int64 arg = PG_GETARG_INT64(0);
if (unlikely(arg < PG_INT16_MIN) || unlikely(arg > PG_INT16_MAX))
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range")));
+ PG_RETURN_NULL();
+ }
+
PG_RETURN_INT16((int16) arg);
}
@@ -1308,10 +1316,14 @@ i8tooid(PG_FUNCTION_ARGS)
int64 arg = PG_GETARG_INT64(0);
if (unlikely(arg < 0) || unlikely(arg > PG_UINT32_MAX))
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("OID out of range")));
+ PG_RETURN_NULL();
+ }
+
PG_RETURN_OID((Oid) arg);
}
--
2.34.1
[text/x-patch] v7-0006-error-safe-for-casting-inet-to-other-types-per-pg_cast.patch (1.9K, 15-v7-0006-error-safe-for-casting-inet-to-other-types-per-pg_cast.patch)
download | inline diff:
From 101651e9a37fa394113006f3f5ba30218d1fb60a Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Thu, 9 Oct 2025 15:55:55 +0800
Subject: [PATCH v7 06/20] 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)
inet_to_cidr is already error safe.
discussion: https://postgr.es/m/CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@mail.gmail.com
---
src/backend/utils/adt/network.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/backend/utils/adt/network.c b/src/backend/utils/adt/network.c
index 3cb0ab6829a..f34228763da 100644
--- a/src/backend/utils/adt/network.c
+++ b/src/backend/utils/adt/network.c
@@ -1137,10 +1137,14 @@ 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,
+ {
+ errsave(fcinfo->context,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("could not format inet value: %m")));
+ PG_RETURN_NULL();
+ }
+
/* Add /n if not present (which it won't be) */
if (strchr(tmp, '/') == NULL)
{
--
2.34.1
[text/x-patch] v7-0007-error-safe-for-casting-macaddr8-to-other-types-per-pg_cast.patch (1.8K, 16-v7-0007-error-safe-for-casting-macaddr8-to-other-types-per-pg_cast.patch)
download | inline diff:
From 7d5152e88cb8506811f61f83ff2bb9a7f3cfacbe Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Thu, 9 Oct 2025 19:12:12 +0800
Subject: [PATCH v7 07/20] 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)
discussion: https://postgr.es/m/CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@mail.gmail.com
---
src/backend/utils/adt/mac8.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/backend/utils/adt/mac8.c b/src/backend/utils/adt/mac8.c
index 08e41ba4eea..e2a7a90e42c 100644
--- a/src/backend/utils/adt/mac8.c
+++ b/src/backend/utils/adt/mac8.c
@@ -550,7 +550,8 @@ macaddr8tomacaddr(PG_FUNCTION_ARGS)
result = (macaddr *) palloc0(sizeof(macaddr));
if ((addr->d != 0xFF) || (addr->e != 0xFE))
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
(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 "
@@ -558,6 +559,9 @@ macaddr8tomacaddr(PG_FUNCTION_ARGS)
"xx:xx:xx:ff:fe:xx:xx:xx, are eligible to be converted "
"from macaddr8 to macaddr.")));
+ PG_RETURN_NULL();
+ }
+
result->a = addr->a;
result->b = addr->b;
result->c = addr->c;
--
2.34.1
[text/x-patch] v7-0003-error-safe-for-casting-character-to-other-types-per-pg_cast.patch (4.4K, 17-v7-0003-error-safe-for-casting-character-to-other-types-per-pg_cast.patch)
download | inline diff:
From a6c47c4c351f2d69e5a6be2e77c5701a73e6a24e Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Thu, 9 Oct 2025 15:46:16 +0800
Subject: [PATCH v7 03/20] 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 functions already error safe.
discussion: https://postgr.es/m/CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@mail.gmail.com
---
src/backend/executor/execExprInterp.c | 2 +-
src/backend/utils/adt/varchar.c | 6 +++++-
src/backend/utils/adt/xml.c | 12 ++++++++----
src/include/utils/xml.h | 2 +-
4 files changed, 15 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..a95e4bd094e 100644
--- a/src/backend/utils/adt/varchar.c
+++ b/src/backend/utils/adt/varchar.c
@@ -307,10 +307,14 @@ bpchar(PG_FUNCTION_ARGS)
{
for (i = maxmblen; i < len; i++)
if (s[i] != ' ')
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
errmsg("value too long for type character(%d)",
maxlen)));
+
+ PG_RETURN_NULL();
+ }
}
len = maxmblen;
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 66b44183695..e6a5be8d9cc 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -659,7 +659,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));
}
@@ -1028,14 +1028,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
[text/x-patch] v7-0005-error-safe-for-casting-character-varying-to-other-types-per-pg_ca.patch (2.2K, 18-v7-0005-error-safe-for-casting-character-varying-to-other-types-per-pg_ca.patch)
download | inline diff:
From 0849ffe6c62fccf18ce03cbd8ef593a601daf50f Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Thu, 9 Oct 2025 19:17:00 +0800
Subject: [PATCH v7 05/20] 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.
so here we only need handle function "varchar".
discussion: https://postgr.es/m/CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@mail.gmail.com
---
src/backend/utils/adt/varchar.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c
index a95e4bd094e..fdbef272c39 100644
--- a/src/backend/utils/adt/varchar.c
+++ b/src/backend/utils/adt/varchar.c
@@ -638,10 +638,14 @@ 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)));
+
+ PG_RETURN_NULL();
+ }
}
PG_RETURN_VARCHAR_P((VarChar *) cstring_to_text_with_len(s_data,
--
2.34.1
[text/x-patch] v7-0002-error-safe-for-casting-bit-varbit-to-other-types-per-pg_cast.patch (3.0K, 19-v7-0002-error-safe-for-casting-bit-varbit-to-other-types-per-pg_cast.patch)
download | inline diff:
From fed4f75d0a2a77c99eebcd6351ff099d385587bf Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Thu, 9 Oct 2025 19:09:53 +0800
Subject: [PATCH v7 02/20] error safe for casting bit/varbit 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
where pc.castfunc > 0 and (castsource::regtype ='bit'::regtype or
castsource::regtype ='varbit'::regtype)
order by castsource::regtype;
castsource | casttarget | castfunc | castcontext | castmethod | prosrc | proname
-------------+-------------+----------+-------------+------------+-----------+---------
bit | bigint | 2076 | e | f | bittoint8 | int8
bit | integer | 1684 | e | f | bittoint4 | int4
bit | bit | 1685 | i | f | bit | bit
bit varying | bit varying | 1687 | i | f | varbit | varbit
(4 rows)
discussion: https://postgr.es/m/CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@mail.gmail.com
---
src/backend/utils/adt/varbit.c | 24 ++++++++++++++++++++----
1 file changed, 20 insertions(+), 4 deletions(-)
diff --git a/src/backend/utils/adt/varbit.c b/src/backend/utils/adt/varbit.c
index 205a67dafc5..bfcea18f4b2 100644
--- a/src/backend/utils/adt/varbit.c
+++ b/src/backend/utils/adt/varbit.c
@@ -401,11 +401,15 @@ bit(PG_FUNCTION_ARGS)
PG_RETURN_VARBIT_P(arg);
if (!isExplicit)
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
(errcode(ERRCODE_STRING_DATA_LENGTH_MISMATCH),
errmsg("bit string length %d does not match type bit(%d)",
VARBITLEN(arg), len)));
+ PG_RETURN_NULL();
+ }
+
rlen = VARBITTOTALLEN(len);
/* set to 0 so that string is zero-padded */
result = (VarBit *) palloc0(rlen);
@@ -752,11 +756,15 @@ varbit(PG_FUNCTION_ARGS)
PG_RETURN_VARBIT_P(arg);
if (!isExplicit)
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
errmsg("bit string too long for type bit varying(%d)",
len)));
+ PG_RETURN_NULL();
+ }
+
rlen = VARBITTOTALLEN(len);
result = (VarBit *) palloc(rlen);
SET_VARSIZE(result, rlen);
@@ -1591,10 +1599,14 @@ bittoint4(PG_FUNCTION_ARGS)
/* Check that the bit string is not too long */
if (VARBITLEN(arg) > sizeof(result) * BITS_PER_BYTE)
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
+ PG_RETURN_NULL();
+ }
+
result = 0;
for (r = VARBITS(arg); r < VARBITEND(arg); r++)
{
@@ -1671,10 +1683,14 @@ bittoint8(PG_FUNCTION_ARGS)
/* Check that the bit string is not too long */
if (VARBITLEN(arg) > sizeof(result) * BITS_PER_BYTE)
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("bigint out of range")));
+ PG_RETURN_NULL();
+ }
+
result = 0;
for (r = VARBITS(arg); r < VARBITEND(arg); r++)
{
--
2.34.1
[text/x-patch] v7-0018-error-safe-for-casting-geometry-data-type.patch (21.2K, 20-v7-0018-error-safe-for-casting-geometry-data-type.patch)
download | inline diff:
From cedd16bb3bc7df4870f6689cb90e137e83dcc66a Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Thu, 9 Oct 2025 19:19:04 +0800
Subject: [PATCH v7 18/20] error safe for casting geometry data type
select castsource::regtype, casttarget::regtype, pp.prosrc
from pg_cast pc join pg_proc pp on pp.oid = pc.castfunc
join pg_type pt on pt.oid = castsource
join pg_type pt1 on pt1.oid = casttarget
and pc.castfunc > 0 and pt.typarray <> 0
and pt.typnamespace = 'pg_catalog'::regnamespace
and pt1.typnamespace = 'pg_catalog'::regnamespace
and (pt.typcategory = 'G' or pt1.typcategory = 'G')
order by castsource::regtype, casttarget::regtype;
castsource | casttarget | prosrc
------------+------------+---------------
point | box | point_box
lseg | point | lseg_center
path | polygon | path_poly
box | point | box_center
box | lseg | box_diagonal
box | polygon | box_poly
box | circle | box_circle
polygon | point | poly_center
polygon | path | poly_path
polygon | box | poly_box
polygon | circle | poly_circle
circle | point | circle_center
circle | box | circle_box
circle | polygon |
(14 rows)
already error safe: point_box, box_diagonal, box_poly, poly_path, poly_box, circle_center
almost error safe: path_poly
discussion: https://postgr.es/m/CACJufxHCMzrHOW=wRe8L30rMhB3sjwAv1LE928Fa7sxMu1Tx-g@mail.gmail.com
---
src/backend/access/spgist/spgproc.c | 2 +-
src/backend/utils/adt/float.c | 8 +-
src/backend/utils/adt/geo_ops.c | 282 ++++++++++++++++++++++++----
src/backend/utils/adt/geo_spgist.c | 2 +-
src/include/utils/float.h | 107 +++++++++++
src/include/utils/geo_decls.h | 4 +-
6 files changed, 358 insertions(+), 47 deletions(-)
diff --git a/src/backend/access/spgist/spgproc.c b/src/backend/access/spgist/spgproc.c
index 660009291da..b3e0fbc59ba 100644
--- a/src/backend/access/spgist/spgproc.c
+++ b/src/backend/access/spgist/spgproc.c
@@ -51,7 +51,7 @@ point_box_distance(Point *point, BOX *box)
else
dy = 0.0;
- return HYPOT(dx, dy);
+ return HYPOT(dx, dy, NULL);
}
/*
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 6747544b679..06b2b6ae295 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -772,7 +772,7 @@ float8pl(PG_FUNCTION_ARGS)
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_FLOAT8(float8_pl(arg1, arg2));
+ PG_RETURN_FLOAT8(float8_pl_safe(arg1, arg2, fcinfo->context));
}
Datum
@@ -781,7 +781,7 @@ float8mi(PG_FUNCTION_ARGS)
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_FLOAT8(float8_mi(arg1, arg2));
+ PG_RETURN_FLOAT8(float8_mi_safe(arg1, arg2, fcinfo->context));
}
Datum
@@ -790,7 +790,7 @@ float8mul(PG_FUNCTION_ARGS)
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_FLOAT8(float8_mul(arg1, arg2));
+ PG_RETURN_FLOAT8(float8_mul_safe(arg1, arg2, fcinfo->context));
}
Datum
@@ -799,7 +799,7 @@ float8div(PG_FUNCTION_ARGS)
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 arg2 = PG_GETARG_FLOAT8(1);
- PG_RETURN_FLOAT8(float8_div(arg1, arg2));
+ PG_RETURN_FLOAT8(float8_div_safe(arg1, arg2, fcinfo->context));
}
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c
index 377a1b3f3ad..a5f2537da25 100644
--- a/src/backend/utils/adt/geo_ops.c
+++ b/src/backend/utils/adt/geo_ops.c
@@ -78,11 +78,14 @@ enum path_delim
/* Routines for points */
static inline void point_construct(Point *result, float8 x, float8 y);
static inline void point_add_point(Point *result, Point *pt1, Point *pt2);
+static inline bool point_add_point_safe(Point *result, Point *pt1, Point *pt2,
+ Node *escontext);
static inline void point_sub_point(Point *result, Point *pt1, Point *pt2);
static inline void point_mul_point(Point *result, Point *pt1, Point *pt2);
static inline void point_div_point(Point *result, Point *pt1, Point *pt2);
static inline bool point_eq_point(Point *pt1, Point *pt2);
static inline float8 point_dt(Point *pt1, Point *pt2);
+static inline bool point_dt_safe(Point *pt1, Point *pt2, Node *escontext, float8 *result);
static inline float8 point_sl(Point *pt1, Point *pt2);
static int point_inside(Point *p, int npts, Point *plist);
@@ -109,6 +112,7 @@ static float8 lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg);
/* Routines for boxes */
static inline void box_construct(BOX *result, Point *pt1, Point *pt2);
static void box_cn(Point *center, BOX *box);
+static bool box_cn_safe(Point *center, BOX *box, Node* escontext);
static bool box_ov(BOX *box1, BOX *box2);
static float8 box_ar(BOX *box);
static float8 box_ht(BOX *box);
@@ -125,7 +129,7 @@ static float8 circle_ar(CIRCLE *circle);
/* Routines for polygons */
static void make_bound_box(POLYGON *poly);
-static void poly_to_circle(CIRCLE *result, POLYGON *poly);
+static bool poly_to_circle_safe(CIRCLE *result, POLYGON *poly, Node *escontext);
static bool lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start);
static bool poly_contain_poly(POLYGON *contains_poly, POLYGON *contained_poly);
static bool plist_same(int npts, Point *p1, Point *p2);
@@ -851,7 +855,8 @@ box_center(PG_FUNCTION_ARGS)
BOX *box = PG_GETARG_BOX_P(0);
Point *result = (Point *) palloc(sizeof(Point));
- box_cn(result, box);
+ if (!box_cn_safe(result, box, fcinfo->context))
+ PG_RETURN_NULL();
PG_RETURN_POINT_P(result);
}
@@ -875,6 +880,31 @@ box_cn(Point *center, BOX *box)
center->y = float8_div(float8_pl(box->high.y, box->low.y), 2.0);
}
+/* safe version of box_cn */
+static bool
+box_cn_safe(Point *center, BOX *box, Node* escontext)
+{
+ float8 x;
+ float8 y;
+
+ x = float8_pl_safe(box->high.x, box->low.x, escontext);
+ if (SOFT_ERROR_OCCURRED(escontext))
+ return false;
+
+ center->x = float8_div_safe(x, 2.0, escontext);
+ if (SOFT_ERROR_OCCURRED(escontext))
+ return false;
+
+ y = float8_pl_safe(box->high.y, box->low.y, escontext);
+ if (SOFT_ERROR_OCCURRED(escontext))
+ return false;
+
+ center->y = float8_div_safe(y, 2.0, escontext);
+ if (SOFT_ERROR_OCCURRED(escontext))
+ return false;
+
+ return true;
+}
/* box_wd - returns the width (length) of the box
* (horizontal magnitude).
@@ -1276,7 +1306,7 @@ line_distance(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(float8_div(fabs(float8_mi(l1->C,
float8_mul(ratio, l2->C))),
- HYPOT(l1->A, l1->B)));
+ HYPOT(l1->A, l1->B, NULL)));
}
/* line_interpt()
@@ -2001,9 +2031,34 @@ point_distance(PG_FUNCTION_ARGS)
static inline float8
point_dt(Point *pt1, Point *pt2)
{
- return HYPOT(float8_mi(pt1->x, pt2->x), float8_mi(pt1->y, pt2->y));
+ return HYPOT(float8_mi(pt1->x, pt2->x), float8_mi(pt1->y, pt2->y), NULL);
}
+/* errror safe version of point_dt */
+static inline bool
+point_dt_safe(Point *pt1, Point *pt2, Node *escontext, float8 *result)
+{
+ float8 x;
+ float8 y;
+
+ Assert(result != NULL);
+
+ x = float8_mi_safe(pt1->x, pt2->x, escontext);
+ if (SOFT_ERROR_OCCURRED(escontext))
+ return false;
+
+ y = float8_mi_safe(pt1->y, pt2->y, escontext);
+ if (SOFT_ERROR_OCCURRED(escontext))
+ return false;
+
+ *result = HYPOT(x, y, escontext);
+ if (SOFT_ERROR_OCCURRED(escontext))
+ return false;
+
+ return true;
+}
+
+
Datum
point_slope(PG_FUNCTION_ARGS)
{
@@ -2317,13 +2372,31 @@ lseg_center(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0);
Point *result;
+ float8 x;
+ float8 y;
result = (Point *) palloc(sizeof(Point));
- result->x = float8_div(float8_pl(lseg->p[0].x, lseg->p[1].x), 2.0);
- result->y = float8_div(float8_pl(lseg->p[0].y, lseg->p[1].y), 2.0);
+ x = float8_pl_safe(lseg->p[0].x, lseg->p[1].x, fcinfo->context);
+ if (SOFT_ERROR_OCCURRED(fcinfo->context))
+ goto fail;
+
+ result->x = float8_div_safe(x, 2.0, fcinfo->context);
+ if (SOFT_ERROR_OCCURRED(fcinfo->context))
+ goto fail;
+
+ y = float8_pl_safe(lseg->p[0].y, lseg->p[1].y, fcinfo->context);
+ if (SOFT_ERROR_OCCURRED(fcinfo->context))
+ goto fail;
+
+ result->y = float8_div_safe(y, 2.0, fcinfo->context);
+ if (SOFT_ERROR_OCCURRED(fcinfo->context))
+ goto fail;
PG_RETURN_POINT_P(result);
+
+fail:
+ PG_RETURN_NULL();
}
@@ -4115,6 +4188,27 @@ point_add_point(Point *result, Point *pt1, Point *pt2)
float8_pl(pt1->y, pt2->y));
}
+/* error safe version of point_add_point */
+static inline bool
+point_add_point_safe(Point *result, Point *pt1, Point *pt2,
+ Node *escontext)
+{
+ float8 x;
+ float8 y;
+
+ x = float8_pl_safe(pt1->x, pt2->x, escontext);
+ if (SOFT_ERROR_OCCURRED(escontext))
+ return false;
+
+ y = float8_pl_safe(pt1->y, pt2->y, escontext);
+ if (SOFT_ERROR_OCCURRED(escontext))
+ return false;
+
+ point_construct(result, x, y);
+
+ return true;
+}
+
Datum
point_add(PG_FUNCTION_ARGS)
{
@@ -4458,10 +4552,14 @@ path_poly(PG_FUNCTION_ARGS)
/* This is not very consistent --- other similar cases return NULL ... */
if (!path->closed)
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("open path cannot be converted to polygon")));
+ PG_RETURN_NULL();
+ }
+
/*
* Never overflows: the old size fit in MaxAllocSize, and the new size is
* just a small constant larger.
@@ -4508,7 +4606,9 @@ poly_center(PG_FUNCTION_ARGS)
result = (Point *) palloc(sizeof(Point));
- poly_to_circle(&circle, poly);
+ if (!poly_to_circle_safe(&circle, poly, fcinfo->context))
+ PG_RETURN_NULL();
+
*result = circle.center;
PG_RETURN_POINT_P(result);
@@ -5005,7 +5105,7 @@ circle_mul_pt(PG_FUNCTION_ARGS)
result = (CIRCLE *) palloc(sizeof(CIRCLE));
point_mul_point(&result->center, &circle->center, point);
- result->radius = float8_mul(circle->radius, HYPOT(point->x, point->y));
+ result->radius = float8_mul(circle->radius, HYPOT(point->x, point->y, NULL));
PG_RETURN_CIRCLE_P(result);
}
@@ -5020,7 +5120,7 @@ circle_div_pt(PG_FUNCTION_ARGS)
result = (CIRCLE *) palloc(sizeof(CIRCLE));
point_div_point(&result->center, &circle->center, point);
- result->radius = float8_div(circle->radius, HYPOT(point->x, point->y));
+ result->radius = float8_div(circle->radius, HYPOT(point->x, point->y, NULL));
PG_RETURN_CIRCLE_P(result);
}
@@ -5191,14 +5291,30 @@ circle_box(PG_FUNCTION_ARGS)
box = (BOX *) palloc(sizeof(BOX));
- delta = float8_div(circle->radius, sqrt(2.0));
+ delta = float8_div_safe(circle->radius, sqrt(2.0), fcinfo->context);
+ if (SOFT_ERROR_OCCURRED(fcinfo->context))
+ goto fail;
- box->high.x = float8_pl(circle->center.x, delta);
- box->low.x = float8_mi(circle->center.x, delta);
- box->high.y = float8_pl(circle->center.y, delta);
- box->low.y = float8_mi(circle->center.y, delta);
+ box->high.x = float8_pl_safe(circle->center.x, delta, fcinfo->context);
+ if (SOFT_ERROR_OCCURRED(fcinfo->context))
+ goto fail;
+
+ box->low.x = float8_mi_safe(circle->center.x, delta, fcinfo->context);
+ if (SOFT_ERROR_OCCURRED(fcinfo->context))
+ goto fail;
+
+ box->high.y = float8_pl_safe(circle->center.y, delta, fcinfo->context);
+ if (SOFT_ERROR_OCCURRED(fcinfo->context))
+ goto fail;
+
+ box->low.y = float8_mi_safe(circle->center.y, delta, fcinfo->context);
+ if (SOFT_ERROR_OCCURRED(fcinfo->context))
+ goto fail;
PG_RETURN_BOX_P(box);
+
+fail:
+ PG_RETURN_NULL();
}
/* box_circle()
@@ -5209,15 +5325,35 @@ box_circle(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0);
CIRCLE *circle;
+ float8 x;
+ float8 y;
circle = (CIRCLE *) palloc(sizeof(CIRCLE));
- circle->center.x = float8_div(float8_pl(box->high.x, box->low.x), 2.0);
- circle->center.y = float8_div(float8_pl(box->high.y, box->low.y), 2.0);
+ x = float8_pl_safe(box->high.x, box->low.x, fcinfo->context);
+ if (SOFT_ERROR_OCCURRED(fcinfo->context))
+ goto fail;
- circle->radius = point_dt(&circle->center, &box->high);
+ circle->center.x = float8_div_safe(x, 2.0, fcinfo->context);
+ if (SOFT_ERROR_OCCURRED(fcinfo->context))
+ goto fail;
+
+ y = float8_pl_safe(box->high.y, box->low.y, fcinfo->context);
+ if (SOFT_ERROR_OCCURRED(fcinfo->context))
+ goto fail;
+
+ circle->center.y = float8_div_safe(y, 2.0, fcinfo->context);
+ if (SOFT_ERROR_OCCURRED(fcinfo->context))
+ goto fail;
+
+ if (!point_dt_safe(&circle->center, &box->high, fcinfo->context,
+ &circle->radius))
+ goto fail;
PG_RETURN_CIRCLE_P(circle);
+
+fail:
+ PG_RETURN_NULL();
}
@@ -5230,28 +5366,38 @@ circle_poly(PG_FUNCTION_ARGS)
int base_size,
size;
int i;
+ float8 x;
+ float8 y;
float8 angle;
float8 anglestep;
if (FPzero(circle->radius))
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot convert circle with radius zero to polygon")));
+ goto fail;
+ }
if (npts < 2)
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("must request at least 2 points")));
+ goto fail;
+ }
base_size = sizeof(poly->p[0]) * npts;
size = offsetof(POLYGON, p) + base_size;
/* Check for integer overflow */
if (base_size / npts != sizeof(poly->p[0]) || size <= base_size)
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("too many points requested")));
-
+ goto fail;
+ }
poly = (POLYGON *) palloc0(size); /* zero any holes */
SET_VARSIZE(poly, size);
poly->npts = npts;
@@ -5260,17 +5406,33 @@ circle_poly(PG_FUNCTION_ARGS)
for (i = 0; i < npts; i++)
{
- angle = float8_mul(anglestep, i);
+ angle = float8_mul_safe(anglestep, i, fcinfo->context);
+ if (SOFT_ERROR_OCCURRED(fcinfo->context))
+ goto fail;
- poly->p[i].x = float8_mi(circle->center.x,
- float8_mul(circle->radius, cos(angle)));
- poly->p[i].y = float8_pl(circle->center.y,
- float8_mul(circle->radius, sin(angle)));
+ x = float8_mul_safe(circle->radius, cos(angle), fcinfo->context);
+ if (SOFT_ERROR_OCCURRED(fcinfo->context))
+ goto fail;
+
+ poly->p[i].x = float8_mi_safe(circle->center.x, x, fcinfo->context);
+ if (SOFT_ERROR_OCCURRED(fcinfo->context))
+ goto fail;
+
+ y = float8_mul_safe(circle->radius, sin(angle),fcinfo->context);
+ if (SOFT_ERROR_OCCURRED(fcinfo->context))
+ goto fail;
+
+ poly->p[i].y = float8_pl_safe(circle->center.y, y, fcinfo->context);
+ if (SOFT_ERROR_OCCURRED(fcinfo->context))
+ goto fail;
}
make_bound_box(poly);
PG_RETURN_POLYGON_P(poly);
+
+fail:
+ PG_RETURN_NULL();
}
/*
@@ -5281,10 +5443,11 @@ circle_poly(PG_FUNCTION_ARGS)
* XXX This algorithm should use weighted means of line segments
* rather than straight average values of points - tgl 97/01/21.
*/
-static void
-poly_to_circle(CIRCLE *result, POLYGON *poly)
+static bool
+poly_to_circle_safe(CIRCLE *result, POLYGON *poly, Node *escontext)
{
int i;
+ float8 x;
Assert(poly->npts > 0);
@@ -5293,14 +5456,41 @@ poly_to_circle(CIRCLE *result, POLYGON *poly)
result->radius = 0;
for (i = 0; i < poly->npts; i++)
- point_add_point(&result->center, &result->center, &poly->p[i]);
- result->center.x = float8_div(result->center.x, poly->npts);
- result->center.y = float8_div(result->center.y, poly->npts);
+ {
+ if (!point_add_point_safe(&result->center,
+ &result->center,
+ &poly->p[i],
+ escontext))
+ return false;
+ }
+
+ result->center.x = float8_div_safe(result->center.x,
+ poly->npts,
+ escontext);
+ if (SOFT_ERROR_OCCURRED(escontext))
+ return false;
+
+ result->center.y = float8_div_safe(result->center.y,
+ poly->npts,
+ escontext);
+ if (SOFT_ERROR_OCCURRED(escontext))
+ return false;
for (i = 0; i < poly->npts; i++)
- result->radius = float8_pl(result->radius,
- point_dt(&poly->p[i], &result->center));
- result->radius = float8_div(result->radius, poly->npts);
+ {
+ if (!point_dt_safe(&poly->p[i], &result->center, escontext, &x))
+ return false;
+
+ result->radius = float8_pl_safe(result->radius, x, escontext);
+ if (SOFT_ERROR_OCCURRED(escontext))
+ return false;
+ }
+
+ result->radius = float8_div_safe(result->radius, poly->npts, escontext);
+ if (SOFT_ERROR_OCCURRED(escontext))
+ return false;
+
+ return true;
}
Datum
@@ -5311,7 +5501,8 @@ poly_circle(PG_FUNCTION_ARGS)
result = (CIRCLE *) palloc(sizeof(CIRCLE));
- poly_to_circle(result, poly);
+ if (!poly_to_circle_safe(result, poly, fcinfo->context))
+ PG_RETURN_NULL();
PG_RETURN_CIRCLE_P(result);
}
@@ -5516,7 +5707,7 @@ plist_same(int npts, Point *p1, Point *p2)
*-----------------------------------------------------------------------
*/
float8
-pg_hypot(float8 x, float8 y)
+pg_hypot(float8 x, float8 y, Node *escontext)
{
float8 yx,
result;
@@ -5554,9 +5745,22 @@ pg_hypot(float8 x, float8 y)
result = x * sqrt(1.0 + (yx * yx));
if (unlikely(isinf(result)))
- float_overflow_error();
+ {
+ errsave(escontext,
+ errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: overflow"));
+
+ result = 0.0;
+ }
+
if (unlikely(result == 0.0))
- float_underflow_error();
+ {
+ errsave(escontext,
+ errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: underflow"));
+
+ result = 0.0;
+ }
return result;
}
diff --git a/src/backend/utils/adt/geo_spgist.c b/src/backend/utils/adt/geo_spgist.c
index fec33e95372..ffffd3cd2de 100644
--- a/src/backend/utils/adt/geo_spgist.c
+++ b/src/backend/utils/adt/geo_spgist.c
@@ -390,7 +390,7 @@ pointToRectBoxDistance(Point *point, RectBox *rect_box)
else
dy = 0;
- return HYPOT(dx, dy);
+ return HYPOT(dx, dy, NULL);
}
diff --git a/src/include/utils/float.h b/src/include/utils/float.h
index fc2a9cf6475..5a7033f2a60 100644
--- a/src/include/utils/float.h
+++ b/src/include/utils/float.h
@@ -109,6 +109,24 @@ float4_pl(const float4 val1, const float4 val2)
return result;
}
+/* error safe version of float8_pl */
+static inline float8
+float8_pl_safe(const float8 val1, const float8 val2, struct Node *escontext)
+{
+ float8 result;
+
+ result = val1 + val2;
+ if (unlikely(isinf(result)) && !isinf(val1) && !isinf(val2))
+ {
+ errsave(escontext,
+ errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: overflow"));
+ result = 0.0f;
+ }
+
+ return result;
+}
+
static inline float8
float8_pl(const float8 val1, const float8 val2)
{
@@ -133,6 +151,24 @@ float4_mi(const float4 val1, const float4 val2)
return result;
}
+/* error safe version of float8_mi */
+static inline float8
+float8_mi_safe(const float8 val1, const float8 val2, struct Node *escontext)
+{
+ float8 result;
+
+ result = val1 - val2;
+ if (unlikely(isinf(result)) && !isinf(val1) && !isinf(val2))
+ {
+ errsave(escontext,
+ errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: overflow"));
+ result = 0.0f;
+ }
+
+ return result;
+}
+
static inline float8
float8_mi(const float8 val1, const float8 val2)
{
@@ -159,6 +195,36 @@ float4_mul(const float4 val1, const float4 val2)
return result;
}
+/* error safe version of float8_mul */
+static inline float8
+float8_mul_safe(const float8 val1, const float8 val2, struct Node *escontext)
+{
+ float8 result;
+
+ result = val1 * val2;
+ if (unlikely(isinf(result)) && !isinf(val1) && !isinf(val2))
+ {
+ errsave(escontext,
+ errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: overflow"));
+
+ result = 0.0;
+ return result;
+ }
+
+ if (unlikely(result == 0.0) && val1 != 0.0 && val2 != 0.0)
+ {
+ errsave(escontext,
+ errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: underflow"));
+
+ result = 0.0;
+ return result;
+ }
+
+ return result;
+}
+
static inline float8
float8_mul(const float8 val1, const float8 val2)
{
@@ -189,6 +255,47 @@ float4_div(const float4 val1, const float4 val2)
return result;
}
+/* error safe version of float8_div */
+static inline float8
+float8_div_safe(const float8 val1, const float8 val2, struct Node *escontext)
+{
+ float8 result;
+
+ if (unlikely(val2 == 0.0) && !isnan(val1))
+ {
+ errsave(escontext,
+ errcode(ERRCODE_DIVISION_BY_ZERO),
+ errmsg("division by zero"));
+
+ result = 0.0;
+ return result;
+ }
+
+ result = val1 / val2;
+
+ if (unlikely(isinf(result)) && !isinf(val1))
+ {
+ errsave(escontext,
+ errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: overflow"));
+
+ result = 0.0;
+ return result;
+ }
+
+ if (unlikely(result == 0.0) && val1 != 0.0 && !isinf(val2))
+ {
+ errsave(escontext,
+ errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: underflow"));
+
+ result = 0.0;
+ return result;
+ }
+
+ return result;
+}
+
static inline float8
float8_div(const float8 val1, const float8 val2)
{
diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h
index 8a9df75c93c..0056a26639f 100644
--- a/src/include/utils/geo_decls.h
+++ b/src/include/utils/geo_decls.h
@@ -88,7 +88,7 @@ FPge(double A, double B)
#define FPge(A,B) ((A) >= (B))
#endif
-#define HYPOT(A, B) pg_hypot(A, B)
+#define HYPOT(A, B, escontext) pg_hypot(A, B, escontext)
/*---------------------------------------------------------------------
* Point - (x,y)
@@ -280,6 +280,6 @@ CirclePGetDatum(const CIRCLE *X)
* in geo_ops.c
*/
-extern float8 pg_hypot(float8 x, float8 y);
+extern float8 pg_hypot(float8 x, float8 y, Node *escontext);
#endif /* GEO_DECLS_H */
--
2.34.1
[text/x-patch] v7-0001-error-safe-for-casting-bytea-to-other-types-per-pg_cast.patch (2.5K, 21-v7-0001-error-safe-for-casting-bytea-to-other-types-per-pg_cast.patch)
download | inline diff:
From 1338cc5c2f867ffad457fb5624eb9d0e0627e67c Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Thu, 9 Oct 2025 15:36:35 +0800
Subject: [PATCH v7 01/20] 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)
discussion: https://postgr.es/m/CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@mail.gmail.com
---
src/backend/utils/adt/bytea.c | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/src/backend/utils/adt/bytea.c b/src/backend/utils/adt/bytea.c
index 6e7b914c563..4a8adcb8204 100644
--- a/src/backend/utils/adt/bytea.c
+++ b/src/backend/utils/adt/bytea.c
@@ -1027,10 +1027,14 @@ bytea_int2(PG_FUNCTION_ARGS)
/* Check that the byte array is not too long */
if (len > sizeof(result))
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range"));
+ PG_RETURN_NULL();
+ }
+
/* Convert it to an integer; most significant bytes come first */
result = 0;
for (int i = 0; i < len; i++)
@@ -1052,10 +1056,14 @@ bytea_int4(PG_FUNCTION_ARGS)
/* Check that the byte array is not too long */
if (len > sizeof(result))
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range"));
+ PG_RETURN_NULL();
+ }
+
/* Convert it to an integer; most significant bytes come first */
result = 0;
for (int i = 0; i < len; i++)
@@ -1077,10 +1085,14 @@ bytea_int8(PG_FUNCTION_ARGS)
/* Check that the byte array is not too long */
if (len > sizeof(result))
- ereport(ERROR,
+ {
+ errsave(fcinfo->context,
errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("bigint out of range"));
+ PG_RETURN_NULL();
+ }
+
/* Convert it to an integer; most significant bytes come first */
result = 0;
for (int i = 0; i < len; i++)
--
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: <CACJufxF--5d=fmoRBHfqJE9Vy38dCURNKYOKKpujRCnoTEQ7nQ@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