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: Tue, 25 Nov 2025 20:27:52 +0800
Message-ID: <CACJufxH5OSeY0-qirksn8S2FUycxON-O=iwc0-Nne1MTAguGhQ@mail.gmail.com> (raw)
In-Reply-To: <CADkLM=ecTybe9Z9TSRD-NKZ=-V4DuGVRtXZGO6+F7=m3Gg9GGQ@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>
<CACJufxF--5d=fmoRBHfqJE9Vy38dCURNKYOKKpujRCnoTEQ7nQ@mail.gmail.com>
<CACJufxHpMJn22Nu_wmG6eV_S8SAM6KM+GhMO7GuzVb=d9q5C4A@mail.gmail.com>
<CACJufxHM2e3DQmbRdDZvWyG3ZCLyOg6XFifvOz_TGy1tGw7NHw@mail.gmail.com>
<CADkLM=daTLuRcwzc6Egtwvh4XYgtABWuMBVnEznd-dXqmXfzUw@mail.gmail.com>
<CACJufxEcrrcaeFW+zYsjgb6r+ijzwszyxeHk3wxGY+3idiA2ZA@mail.gmail.com>
<CADkLM=ehavqENDBCcYQufPFKboV90+o_uFdhcrh=Ymq_TNqo=A@mail.gmail.com>
<CADkLM=ecTybe9Z9TSRD-NKZ=-V4DuGVRtXZGO6+F7=m3Gg9GGQ@mail.gmail.com>
On Fri, Nov 21, 2025 at 3:32 PM Corey Huinker <[email protected]> wrote:
>
>>>
>>> Also, are we settled on this new pg_cast column name (pg_cast.casterrorsafe)?
>>> Overall, I think it's better not to touch pg_cast.dat in each of these
>>> refactoring patches.
>>
>>
>> I'm fine with it. I can see having 'f' and 's' both mean cast functions, but 's' means safe, but the extra boolean works too and we'll be fine with either method.
>
>
> I can work on this part if you don't have time.
Do you mean change pg_cast.casterrorsafe from boolean to char?
Currently pg_cast.casterrorsafe works just fine.
if the cast function is not applicable, the castfunc would be InvalidOid.
also the cast function is either error safe or not, I don't see a
usage case for the third value.
attached v12-0004 fixes the xmlparse error, see
https://cirrus-ci.com/task/6181359384788992?logs=build#L1217.
all other patches remain the same as v11.
--
jian
https://www.enterprisedb.com/
Attachments:
[text/x-patch] v12-0019-invent-some-error-safe-functions.patch (12.2K, 2-v12-0019-invent-some-error-safe-functions.patch)
download | inline diff:
From 6803dd3fcf06de55544d6e62baa48ad2c7c18d05 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Mon, 24 Nov 2025 17:03:45 +0800
Subject: [PATCH v12 19/20] invent some error safe functions
stringTypeDatumSafe: error safe version of stringTypeDatum
evaluate_expr_extended: If you wish to evaluate a constant expression in a
manner that is safe from potential errors, set the error_safe parameter to true
ExecInitExprSafe: soft error variant of ExecInitExpr
OidInputFunctionCallSafe: soft error variant of OidInputFunctionCall
CoerceUnknownConstSafe: attempts to coerce an UNKNOWN Const to the target type.
Returns NULL if the coercion is not possible.
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 | 21 ++++++-
src/backend/parser/parse_coerce.c | 92 ++++++++++++++++++++++++++++
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 | 3 +
src/include/parser/parse_coerce.h | 4 ++
src/include/parser/parse_type.h | 2 +
10 files changed, 193 insertions(+), 1 deletion(-)
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 202ba8ed4bb..32af0df6c70 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -5081,6 +5081,16 @@ sql_inline_error_callback(void *arg)
Expr *
evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod,
Oid result_collation)
+{
+ return evaluate_expr_extended(expr,
+ result_type,
+ result_typmod,
+ result_collation,
+ false);
+}
+Expr *
+evaluate_expr_extended(Expr *expr, Oid result_type, int32 result_typmod,
+ Oid result_collation, bool error_safe)
{
EState *estate;
ExprState *exprstate;
@@ -5105,7 +5115,10 @@ evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod,
* Prepare expr for execution. (Note: we can't use ExecPrepareExpr
* because it'd result in recursively invoking eval_const_expressions.)
*/
- exprstate = ExecInitExpr(expr, NULL);
+ if (error_safe)
+ exprstate = ExecInitExprSafe(expr, NULL);
+ else
+ exprstate = ExecInitExpr(expr, NULL);
/*
* And evaluate it.
@@ -5125,6 +5138,12 @@ evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod,
/* Get back to outer memory context */
MemoryContextSwitchTo(oldcontext);
+ if (error_safe && SOFT_ERROR_OCCURRED(exprstate->escontext))
+ {
+ FreeExecutorState(estate);
+ return NULL;
+ }
+
/*
* Must copy result out of sub-context used by expression eval.
*
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 78b1e366ad7..cdcdd44a799 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -657,6 +657,98 @@ can_coerce_type(int nargs, const Oid *input_typeids, const Oid *target_typeids,
return true;
}
+/*
+ * Create an expression tree to represent coercion a UNKNOWN Const node.
+ *
+ * 'node': the input expression
+ * 'baseTypeId': base type of domain
+ * 'baseTypeMod': base type typmod of domain
+ * 'targetType': target type to coerce to
+ * 'targetTypeMod': target type typmod
+ * 'ccontext': context indicator to control coercions
+ * 'cformat': coercion display format
+ * 'location': coercion request location
+ * 'hideInputCoercion': if true, hide the input coercion under this one.
+ */
+Node *
+CoerceUnknownConstSafe(ParseState *pstate, Node *node, Oid targetType, int32 targetTypeMod,
+ CoercionContext ccontext, CoercionForm cformat, int location,
+ bool hideInputCoercion)
+{
+ Oid baseTypeId;
+ int32 baseTypeMod;
+ int32 inputTypeMod;
+ Type baseType;
+ char *string;
+ Datum datum;
+ Const *newcon;
+ Node *result = NULL;
+ Const *con = (Const *) node;
+
+ Assert(IsA(node, Const));
+ Assert(exprType(node) == UNKNOWNOID);
+
+ 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;
+
+ if (!stringTypeDatumSafe(baseType,
+ string,
+ inputTypeMod,
+ &datum))
+ {
+ ReleaseSysCache(baseType);
+ return NULL;
+ }
+
+ newcon = makeNode(Const);
+ newcon->consttype = baseTypeId;
+ newcon->consttypmod = inputTypeMod;
+ newcon->constcollid = typeTypeCollation(baseType);
+ newcon->constlen = typeLen(baseType);
+ newcon->constbyval = typeByVal(baseType);
+ newcon->constisnull = con->constisnull;
+ newcon->constvalue = datum;
+
+ /*
+ * If it's a varlena value, force it to be in non-expanded
+ * (non-toasted) format; this avoids any possible dependency on
+ * external values and improves consistency of representation.
+ */
+ if (!con->constisnull && newcon->constlen == -1)
+ newcon->constvalue =
+ PointerGetDatum(PG_DETOAST_DATUM(newcon->constvalue));
+
+ result = (Node *) newcon;
+
+ /* If target is a domain, apply constraints. */
+ if (baseTypeId != targetType)
+ result = coerce_to_domain(result,
+ baseTypeId, baseTypeMod,
+ targetType,
+ ccontext, cformat, location,
+ false);
+
+ ReleaseSysCache(baseType);
+
+ return result;
+}
+
/*
* Create an expression tree to represent coercion to a domain type.
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 fa2b657fb2f..f99fc26eb1f 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -324,6 +324,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 d0aa8ab0c1c..97a0337452c 100644
--- a/src/include/optimizer/optimizer.h
+++ b/src/include/optimizer/optimizer.h
@@ -144,6 +144,9 @@ 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_extended(Expr *expr, Oid result_type,
+ int32 result_typmod, Oid result_collation,
+ bool error_safe);
extern bool var_is_nonnullable(PlannerInfo *root, Var *var, bool use_rel_info);
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index 8d775c72c59..ad16cdd7022 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -48,6 +48,10 @@ extern bool can_coerce_type(int nargs, const Oid *input_typeids, const Oid *targ
extern Node *coerce_type(ParseState *pstate, Node *node,
Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod,
CoercionContext ccontext, CoercionForm cformat, int location);
+extern Node *CoerceUnknownConstSafe(ParseState *pstate, Node *node,
+ Oid targetType, int32 targetTypeMod,
+ CoercionContext ccontext, CoercionForm cformat,
+ int location, bool hideInputCoercion);
extern Node *coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod,
Oid typeId,
CoercionContext ccontext, CoercionForm cformat, int location,
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] v12-0018-error-safe-for-casting-geometry-data-type.patch (17.6K, 3-v12-0018-error-safe-for-casting-geometry-data-type.patch)
download | inline diff:
From e7032cad38de580b0f6487d1c3be2e6fe817268c Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Mon, 24 Nov 2025 16:38:23 +0800
Subject: [PATCH v12 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
can not error safe: cast circle to polygon, because it's a SQL function
discussion: https://postgr.es/m/CACJufxHCMzrHOW=wRe8L30rMhB3sjwAv1LE928Fa7sxMu1Tx-g@mail.gmail.com
---
src/backend/access/spgist/spgproc.c | 2 +-
src/backend/utils/adt/geo_ops.c | 225 +++++++++++++++++++++++-----
src/backend/utils/adt/geo_spgist.c | 2 +-
src/include/utils/float.h | 77 ++++++++++
src/include/utils/geo_decls.h | 4 +-
5 files changed, 271 insertions(+), 39 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/geo_ops.c b/src/backend/utils/adt/geo_ops.c
index 377a1b3f3ad..bae8730c347 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 float8 point_dt_safe(Point *pt1, Point *pt2, Node *escontext);
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);
}
@@ -871,10 +876,33 @@ box_ar(BOX *box)
static void
box_cn(Point *center, BOX *box)
{
- center->x = float8_div(float8_pl(box->high.x, box->low.x), 2.0);
- center->y = float8_div(float8_pl(box->high.y, box->low.y), 2.0);
+ (void) box_cn_safe(center, box, NULL);
}
+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 +1304,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 +2029,27 @@ 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 point_dt_safe(pt1, pt2, NULL);
}
+static inline float8
+point_dt_safe(Point *pt1, Point *pt2, Node *escontext)
+{
+ float8 x;
+ float8 y;
+
+ x = float8_mi_safe(pt1->x, pt2->x, escontext);
+ if (SOFT_ERROR_OCCURRED(escontext))
+ return 0.0;
+
+ y = float8_mi_safe(pt1->y, pt2->y, escontext);
+ if (SOFT_ERROR_OCCURRED(escontext))
+ return 0.0;
+
+ return HYPOT(x, y, escontext);
+}
+
+
Datum
point_slope(PG_FUNCTION_ARGS)
{
@@ -2317,13 +2363,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();
}
@@ -4110,9 +4174,27 @@ construct_point(PG_FUNCTION_ARGS)
static inline void
point_add_point(Point *result, Point *pt1, Point *pt2)
{
- point_construct(result,
- float8_pl(pt1->x, pt2->x),
- float8_pl(pt1->y, pt2->y));
+ (void) point_add_point_safe(result, pt1, pt2, NULL);
+}
+
+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
@@ -4458,7 +4540,7 @@ path_poly(PG_FUNCTION_ARGS)
/* This is not very consistent --- other similar cases return NULL ... */
if (!path->closed)
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("open path cannot be converted to polygon")));
@@ -4508,7 +4590,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 +5089,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 +5104,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 +5275,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 +5309,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;
+
+ circle->radius = point_dt_safe(&circle->center, &box->high, fcinfo->context);
+ if (SOFT_ERROR_OCCURRED(fcinfo->context))
+ goto fail;
PG_RETURN_CIRCLE_P(circle);
+
+fail:
+ PG_RETURN_NULL();
}
@@ -5281,10 +5401,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 +5414,42 @@ 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);
+ {
+ x = point_dt_safe(&poly->p[i], &result->center, escontext);
+ if (SOFT_ERROR_OCCURRED(escontext))
+ 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 +5460,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 +5666,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 +5704,14 @@ pg_hypot(float8 x, float8 y)
result = x * sqrt(1.0 + (yx * yx));
if (unlikely(isinf(result)))
- float_overflow_error();
+ ereturn(escontext, 0.0,
+ errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: overflow"));
+
if (unlikely(result == 0.0))
- float_underflow_error();
+ ereturn(escontext, 0.0,
+ errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: underflow"));
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..aec376ffc56 100644
--- a/src/include/utils/float.h
+++ b/src/include/utils/float.h
@@ -121,6 +121,21 @@ float8_pl(const float8 val1, const float8 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))
+ ereturn(escontext, 0,
+ errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: overflow"));
+
+ return result;
+}
+
static inline float4
float4_mi(const float4 val1, const float4 val2)
{
@@ -145,6 +160,21 @@ float8_mi(const float8 val1, const float8 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))
+ ereturn(escontext, 0,
+ errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: overflow"));
+
+ return result;
+}
+
static inline float4
float4_mul(const float4 val1, const float4 val2)
{
@@ -173,6 +203,27 @@ float8_mul(const float8 val1, const float8 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))
+ ereturn(escontext, 0,
+ errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: overflow"));
+
+ if (unlikely(result == 0.0) && val1 != 0.0 && val2 != 0.0)
+ ereturn(escontext, 0,
+ errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: underflow"));
+
+ return result;
+}
+
static inline float4
float4_div(const float4 val1, const float4 val2)
{
@@ -205,6 +256,32 @@ float8_div(const float8 val1, const float8 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))
+ ereturn(escontext, 0,
+ errcode(ERRCODE_DIVISION_BY_ZERO),
+ errmsg("division by zero"));
+
+ result = val1 / val2;
+
+ if (unlikely(isinf(result)) && !isinf(val1))
+ ereturn(escontext, 0,
+ errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: overflow"));
+
+ if (unlikely(result == 0.0) && val1 != 0.0 && !isinf(val2))
+ ereturn(escontext, 0,
+ errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: underflow"));
+
+ return result;
+}
+
/*
* Routines for NaN-aware comparisons
*
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] v12-0017-error-safe-for-casting-jsonb-to-other-types-per-pg_cast.patch (6.3K, 4-v12-0017-error-safe-for-casting-jsonb-to-other-types-per-pg_cast.patch)
download | inline diff:
From bafd37051cf8a39e2fa5f5c4bc81ae94d5f951e0 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Mon, 24 Nov 2025 14:26:53 +0800
Subject: [PATCH v12 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 | 74 +++++++++++++++++++++++++++--------
1 file changed, 58 insertions(+), 16 deletions(-)
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index 9399cdb491a..ab24d6dc813 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -2006,7 +2006,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
{
@@ -2027,7 +2027,7 @@ cannotCastJsonbValue(enum jbvType type, const char *sqltype)
for (i = 0; i < lengthof(messages); i++)
if (messages[i].type == type)
- ereport(ERROR,
+ ereturn(escontext,,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg(messages[i].msg, sqltype)));
@@ -2042,7 +2042,10 @@ 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)
{
@@ -2051,7 +2054,10 @@ 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);
@@ -2066,7 +2072,10 @@ 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)
{
@@ -2075,7 +2084,10 @@ 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
@@ -2096,7 +2108,10 @@ 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)
{
@@ -2105,7 +2120,10 @@ 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));
@@ -2123,7 +2141,10 @@ 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)
{
@@ -2132,7 +2153,10 @@ 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));
@@ -2150,7 +2174,10 @@ 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)
{
@@ -2159,7 +2186,10 @@ 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));
@@ -2177,7 +2207,10 @@ 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)
{
@@ -2186,7 +2219,10 @@ 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));
@@ -2204,7 +2240,10 @@ 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)
{
@@ -2213,7 +2252,10 @@ 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] v12-0015-error-safe-for-casting-timestamptz-to-other-types-per-pg_cast.patch (3.8K, 5-v12-0015-error-safe-for-casting-timestamptz-to-other-types-per-pg_cast.patch)
download | inline diff:
From 30fb4299bcc66aa90755fbac42e43d6d8a66b642 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Mon, 6 Oct 2025 12:39:22 +0800
Subject: [PATCH v12 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] v12-0016-error-safe-for-casting-timestamp-to-other-types-per-pg_cast.patch (3.4K, 6-v12-0016-error-safe-for-casting-timestamp-to-other-types-per-pg_cast.patch)
download | inline diff:
From 675190ed0721396503fa3a79d562c72066c9464d Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Thu, 9 Oct 2025 18:44:35 +0800
Subject: [PATCH v12 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] v12-0014-error-safe-for-casting-interval-to-other-types-per-pg_cast.patch (2.1K, 7-v12-0014-error-safe-for-casting-interval-to-other-types-per-pg_cast.patch)
download | inline diff:
From 61ef3a3c9bd59a83c893fcdb51d0865e43c1fe3a Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Thu, 9 Oct 2025 18:43:29 +0800
Subject: [PATCH v12 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] v12-0012-error-safe-for-casting-float8-to-other-types-per-pg_cast.patch (3.6K, 8-v12-0012-error-safe-for-casting-float8-to-other-types-per-pg_cast.patch)
download | inline diff:
From 35664fc4f1aafd5f0058bc796cb83ce9d37c2881 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Sat, 22 Nov 2025 00:31:53 +0800
Subject: [PATCH v12 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 | 13 +++++++++----
src/backend/utils/adt/int8.c | 2 +-
src/backend/utils/adt/numeric.c | 3 ++-
3 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index ab5c38d723d..d746f961fd3 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -1199,9 +1199,14 @@ dtof(PG_FUNCTION_ARGS)
result = (float4) num;
if (unlikely(isinf(result)) && !isinf(num))
- float_overflow_error();
+ ereturn(fcinfo->context, (Datum) 0,
+ errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: overflow"));
+
if (unlikely(result == 0.0f) && num != 0.0)
- float_underflow_error();
+ ereturn(fcinfo->context, (Datum) 0,
+ errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: underflow"));
PG_RETURN_FLOAT4(result);
}
@@ -1224,7 +1229,7 @@ dtoi4(PG_FUNCTION_ARGS)
/* Range check */
if (unlikely(isnan(num) || !FLOAT8_FITS_IN_INT32(num)))
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
@@ -1249,7 +1254,7 @@ dtoi2(PG_FUNCTION_ARGS)
/* Range check */
if (unlikely(isnan(num) || !FLOAT8_FITS_IN_INT16(num)))
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range")));
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index 8f7c117557a..70e6d89813c 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -1260,7 +1260,7 @@ dtoi8(PG_FUNCTION_ARGS)
/* Range check */
if (unlikely(isnan(num) || !FLOAT8_FITS_IN_INT64(num)))
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("bigint out of range")));
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index b89c35eee65..507c607a799 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -4560,7 +4560,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] v12-0013-error-safe-for-casting-date-to-other-types-per-pg_cast.patch (2.1K, 9-v12-0013-error-safe-for-casting-date-to-other-types-per-pg_cast.patch)
download | inline diff:
From 819f444d9cf10248c4a29fc89aab9aa62913ebbb Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Thu, 9 Oct 2025 18:42:50 +0800
Subject: [PATCH v12 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] v12-0011-error-safe-for-casting-float4-to-other-types-per-pg_cast.patch (3.1K, 10-v12-0011-error-safe-for-casting-float4-to-other-types-per-pg_cast.patch)
download | inline diff:
From 2d5488319fb708212e8d44a4226c013ad8bf2401 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Sat, 22 Nov 2025 00:28:20 +0800
Subject: [PATCH v12 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 | 4 ++--
src/backend/utils/adt/int8.c | 2 +-
src/backend/utils/adt/numeric.c | 3 ++-
3 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 7b97d2be6ca..ab5c38d723d 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -1298,7 +1298,7 @@ ftoi4(PG_FUNCTION_ARGS)
/* Range check */
if (unlikely(isnan(num) || !FLOAT4_FITS_IN_INT32(num)))
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
@@ -1323,7 +1323,7 @@ ftoi2(PG_FUNCTION_ARGS)
/* Range check */
if (unlikely(isnan(num) || !FLOAT4_FITS_IN_INT16(num)))
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range")));
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index 3dcbd36bd8e..8f7c117557a 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -1295,7 +1295,7 @@ ftoi8(PG_FUNCTION_ARGS)
/* Range check */
if (unlikely(isnan(num) || !FLOAT4_FITS_IN_INT64(num)))
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("bigint out of range")));
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 40d2709b64a..b89c35eee65 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -4658,7 +4658,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] v12-0010-error-safe-for-casting-numeric-to-other-types-per-pg_cast.patch (5.2K, 11-v12-0010-error-safe-for-casting-numeric-to-other-types-per-pg_cast.patch)
download | inline diff:
From a4c0b5c782ead8aa18301657bd070b9df0354f2e Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Sat, 22 Nov 2025 00:25:37 +0800
Subject: [PATCH v12 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 | 58 ++++++++++++++++++++++++---------
1 file changed, 43 insertions(+), 15 deletions(-)
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 2501007d981..40d2709b64a 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,11 +4506,11 @@ numeric_int2(PG_FUNCTION_ARGS)
if (NUMERIC_IS_SPECIAL(num))
{
if (NUMERIC_IS_NAN(num))
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot convert NaN to %s", "smallint")));
else
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot convert infinity to %s", "smallint")));
}
@@ -4502,12 +4519,12 @@ numeric_int2(PG_FUNCTION_ARGS)
init_var_from_num(num, &x);
if (!numericvar_to_int64(&x, &val))
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range")));
if (unlikely(val < PG_INT16_MIN) || unlikely(val > PG_INT16_MAX))
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range")));
@@ -4572,10 +4589,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 +4688,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] v12-0009-error-safe-for-casting-bigint-to-other-types-per-pg_cast.patch (3.9K, 12-v12-0009-error-safe-for-casting-bigint-to-other-types-per-pg_cast.patch)
download | inline diff:
From 67e46c5a4ec3964e66b186ead03edcd046b03c68 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Sat, 22 Nov 2025 00:22:00 +0800
Subject: [PATCH v12 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 | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index bdea490202a..3dcbd36bd8e 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -1204,7 +1204,7 @@ int84(PG_FUNCTION_ARGS)
int64 arg = PG_GETARG_INT64(0);
if (unlikely(arg < PG_INT32_MIN) || unlikely(arg > PG_INT32_MAX))
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
@@ -1225,7 +1225,7 @@ int82(PG_FUNCTION_ARGS)
int64 arg = PG_GETARG_INT64(0);
if (unlikely(arg < PG_INT16_MIN) || unlikely(arg > PG_INT16_MAX))
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range")));
@@ -1308,7 +1308,7 @@ i8tooid(PG_FUNCTION_ARGS)
int64 arg = PG_GETARG_INT64(0);
if (unlikely(arg < 0) || unlikely(arg > PG_UINT32_MAX))
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("OID out of range")));
--
2.34.1
[text/x-patch] v12-0008-error-safe-for-casting-integer-to-other-types-per-pg_cast.patch (2.9K, 13-v12-0008-error-safe-for-casting-integer-to-other-types-per-pg_cast.patch)
download | inline diff:
From 2a3fce700e11e1a9bf0eb9f7fa5c48e0d4ec10d6 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Sat, 22 Nov 2025 00:20:10 +0800
Subject: [PATCH v12 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 | 2 +-
src/backend/utils/adt/int.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/backend/utils/adt/char.c b/src/backend/utils/adt/char.c
index 22dbfc950b1..e90844a29f0 100644
--- a/src/backend/utils/adt/char.c
+++ b/src/backend/utils/adt/char.c
@@ -192,7 +192,7 @@ i4tochar(PG_FUNCTION_ARGS)
int32 arg1 = PG_GETARG_INT32(0);
if (arg1 < SCHAR_MIN || arg1 > SCHAR_MAX)
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("\"char\" out of range")));
diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c
index b5781989a64..b45599d402d 100644
--- a/src/backend/utils/adt/int.c
+++ b/src/backend/utils/adt/int.c
@@ -350,7 +350,7 @@ i4toi2(PG_FUNCTION_ARGS)
int32 arg1 = PG_GETARG_INT32(0);
if (unlikely(arg1 < SHRT_MIN) || unlikely(arg1 > SHRT_MAX))
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range")));
--
2.34.1
[text/x-patch] v12-0007-error-safe-for-casting-macaddr8-to-other-types-per-pg_cast.patch (1.5K, 14-v12-0007-error-safe-for-casting-macaddr8-to-other-types-per-pg_cast.patch)
download | inline diff:
From 7db397a55d6ff5e1b96176b704556649b86630c2 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Sat, 22 Nov 2025 00:17:11 +0800
Subject: [PATCH v12 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 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/backend/utils/adt/mac8.c b/src/backend/utils/adt/mac8.c
index 08e41ba4eea..1c903f152de 100644
--- a/src/backend/utils/adt/mac8.c
+++ b/src/backend/utils/adt/mac8.c
@@ -550,7 +550,7 @@ macaddr8tomacaddr(PG_FUNCTION_ARGS)
result = (macaddr *) palloc0(sizeof(macaddr));
if ((addr->d != 0xFF) || (addr->e != 0xFE))
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("macaddr8 data out of range to convert to macaddr"),
errhint("Only addresses that have FF and FE as values in the "
--
2.34.1
[text/x-patch] v12-0006-error-safe-for-casting-inet-to-other-types-per-pg_cast.patch (1.8K, 15-v12-0006-error-safe-for-casting-inet-to-other-types-per-pg_cast.patch)
download | inline diff:
From 4ec193ef249c61a71f0ecb7c7601e73399f9aaf9 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Sat, 22 Nov 2025 10:28:54 +0800
Subject: [PATCH v12 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 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/backend/utils/adt/network.c b/src/backend/utils/adt/network.c
index 3cb0ab6829a..648c8d95f51 100644
--- a/src/backend/utils/adt/network.c
+++ b/src/backend/utils/adt/network.c
@@ -1137,7 +1137,7 @@ network_show(PG_FUNCTION_ARGS)
if (pg_inet_net_ntop(ip_family(ip), ip_addr(ip), ip_maxbits(ip),
tmp, sizeof(tmp)) == NULL)
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("could not format inet value: %m")));
--
2.34.1
[text/x-patch] v12-0005-error-safe-for-casting-character-varying-to-other-types-per-pg_c.patch (2.1K, 16-v12-0005-error-safe-for-casting-character-varying-to-other-types-per-pg_c.patch)
download | inline diff:
From 5a31cec8bce660f0f323a7842c27330cb4cb7d60 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Sat, 22 Nov 2025 00:13:45 +0800
Subject: [PATCH v12 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 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c
index 5cb5c8c46f9..08f1bf5a24d 100644
--- a/src/backend/utils/adt/varchar.c
+++ b/src/backend/utils/adt/varchar.c
@@ -634,7 +634,7 @@ varchar(PG_FUNCTION_ARGS)
{
for (i = maxmblen; i < len; i++)
if (s_data[i] != ' ')
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
errmsg("value too long for type character varying(%d)",
maxlen)));
--
2.34.1
[text/x-patch] v12-0003-error-safe-for-casting-character-to-other-types-per-pg_cast.patch (4.6K, 17-v12-0003-error-safe-for-casting-character-to-other-types-per-pg_cast.patch)
download | inline diff:
From d81dc3b37242eaa9e8e4a018343c15e4753c8e91 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Mon, 24 Nov 2025 12:52:16 +0800
Subject: [PATCH v12 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 | 2 +-
src/backend/utils/adt/xml.c | 18 ++++++++++++------
src/include/utils/xml.h | 2 +-
4 files changed, 15 insertions(+), 9 deletions(-)
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 0e1a74976f7..67f4e00eac4 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -4542,7 +4542,7 @@ ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op)
*op->resvalue = PointerGetDatum(xmlparse(data,
xexpr->xmloption,
- preserve_whitespace));
+ preserve_whitespace, NULL));
*op->resnull = false;
}
break;
diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c
index 3f40c9da1a0..5cb5c8c46f9 100644
--- a/src/backend/utils/adt/varchar.c
+++ b/src/backend/utils/adt/varchar.c
@@ -307,7 +307,7 @@ bpchar(PG_FUNCTION_ARGS)
{
for (i = maxmblen; i < len; i++)
if (s[i] != ' ')
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
errmsg("value too long for type character(%d)",
maxlen)));
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 41e775570ec..9e8016456ce 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,19 +1028,25 @@ 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
- NO_XML_SUPPORT();
- return NULL;
+ ereturn(escontext, NULL
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("unsupported XML feature"),
+ errdetail("This functionality requires the server to be built with libxml support."));
#endif
}
diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h
index 732dac47bc4..b15168c430e 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,
const Datum *named_argvalue, const bool *named_argnull,
const Datum *argvalue, const 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] v12-0002-error-safe-for-casting-bit-varbit-to-other-types-per-pg_cast.patch (2.6K, 18-v12-0002-error-safe-for-casting-bit-varbit-to-other-types-per-pg_cast.patch)
download | inline diff:
From c35febdcc1b540e80cd6b3d779498cbac7de0f99 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Sat, 22 Nov 2025 10:33:08 +0800
Subject: [PATCH v12 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 | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/backend/utils/adt/varbit.c b/src/backend/utils/adt/varbit.c
index 205a67dafc5..6e9b808e20a 100644
--- a/src/backend/utils/adt/varbit.c
+++ b/src/backend/utils/adt/varbit.c
@@ -401,7 +401,7 @@ bit(PG_FUNCTION_ARGS)
PG_RETURN_VARBIT_P(arg);
if (!isExplicit)
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_STRING_DATA_LENGTH_MISMATCH),
errmsg("bit string length %d does not match type bit(%d)",
VARBITLEN(arg), len)));
@@ -752,7 +752,7 @@ varbit(PG_FUNCTION_ARGS)
PG_RETURN_VARBIT_P(arg);
if (!isExplicit)
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
errmsg("bit string too long for type bit varying(%d)",
len)));
@@ -1591,7 +1591,7 @@ bittoint4(PG_FUNCTION_ARGS)
/* Check that the bit string is not too long */
if (VARBITLEN(arg) > sizeof(result) * BITS_PER_BYTE)
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
@@ -1671,7 +1671,7 @@ bittoint8(PG_FUNCTION_ARGS)
/* Check that the bit string is not too long */
if (VARBITLEN(arg) > sizeof(result) * BITS_PER_BYTE)
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("bigint out of range")));
--
2.34.1
[text/x-patch] v12-0004-error-safe-for-casting-character-to-other-types-per-pg_cast.patch (10.2K, 19-v12-0004-error-safe-for-casting-character-to-other-types-per-pg_cast.patch)
download | inline diff:
From 0120c7f31e3b37247ecdc9945fbba850856d626e Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Tue, 25 Nov 2025 20:07:28 +0800
Subject: [PATCH v12 04/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/catalog/namespace.c | 58 ++++++++++++++++++++++++++-------
src/backend/utils/adt/regproc.c | 13 ++++++--
src/backend/utils/adt/varlena.c | 10 ++++--
src/backend/utils/adt/xml.c | 2 +-
src/include/catalog/namespace.h | 6 ++++
src/include/utils/varlena.h | 1 +
6 files changed, 73 insertions(+), 17 deletions(-)
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index d23474da4fb..bef2de5dd39 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -440,6 +440,16 @@ Oid
RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode,
uint32 flags,
RangeVarGetRelidCallback callback, void *callback_arg)
+{
+ return RangeVarGetRelidExtendedSafe(relation, lockmode, flags,
+ callback, callback_arg,
+ NULL);
+}
+
+Oid
+RangeVarGetRelidExtendedSafe(const RangeVar *relation, LOCKMODE lockmode, uint32 flags,
+ RangeVarGetRelidCallback callback, void *callback_arg,
+ Node *escontext)
{
uint64 inval_count;
Oid relId;
@@ -456,7 +466,7 @@ RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode,
if (relation->catalogname)
{
if (strcmp(relation->catalogname, get_database_name(MyDatabaseId)) != 0)
- ereport(ERROR,
+ ereturn(escontext, InvalidOid,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
relation->catalogname, relation->schemaname,
@@ -513,7 +523,7 @@ RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode,
* return InvalidOid.
*/
if (namespaceId != myTempNamespace)
- ereport(ERROR,
+ ereturn(escontext, InvalidOid,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("temporary tables cannot specify a schema name")));
}
@@ -593,13 +603,23 @@ RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode,
{
int elevel = (flags & RVR_SKIP_LOCKED) ? DEBUG1 : ERROR;
- if (relation->schemaname)
- ereport(elevel,
+ if (relation->schemaname && elevel == DEBUG1)
+ ereport(DEBUG1,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("could not obtain lock on relation \"%s.%s\"",
relation->schemaname, relation->relname)));
- else
- ereport(elevel,
+ else if (relation->schemaname && elevel == ERROR)
+ ereturn(escontext, InvalidOid,
+ errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on relation \"%s.%s\"",
+ relation->schemaname, relation->relname));
+ else if (elevel == DEBUG1)
+ ereport(DEBUG1,
+ errcode(ERRCODE_LOCK_NOT_AVAILABLE),
+ errmsg("could not obtain lock on relation \"%s\"",
+ relation->relname));
+ else if (elevel == ERROR)
+ ereturn(escontext, InvalidOid,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("could not obtain lock on relation \"%s\"",
relation->relname)));
@@ -626,13 +646,23 @@ RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode,
{
int elevel = missing_ok ? DEBUG1 : ERROR;
- if (relation->schemaname)
- ereport(elevel,
+ if (relation->schemaname && elevel == DEBUG1)
+ ereport(DEBUG1,
(errcode(ERRCODE_UNDEFINED_TABLE),
errmsg("relation \"%s.%s\" does not exist",
relation->schemaname, relation->relname)));
- else
- ereport(elevel,
+ else if (relation->schemaname && elevel == ERROR)
+ ereturn(escontext, InvalidOid,
+ errcode(ERRCODE_UNDEFINED_TABLE),
+ errmsg("relation \"%s.%s\" does not exist",
+ relation->schemaname, relation->relname));
+ else if (elevel == DEBUG1)
+ ereport(DEBUG1,
+ errcode(ERRCODE_UNDEFINED_TABLE),
+ errmsg("relation \"%s\" does not exist",
+ relation->relname));
+ else if (elevel == ERROR)
+ ereturn(escontext, InvalidOid,
(errcode(ERRCODE_UNDEFINED_TABLE),
errmsg("relation \"%s\" does not exist",
relation->relname)));
@@ -3622,6 +3652,12 @@ get_namespace_oid(const char *nspname, bool missing_ok)
*/
RangeVar *
makeRangeVarFromNameList(const List *names)
+{
+ return makeRangeVarFromNameListSafe(names, NULL);
+}
+
+RangeVar *
+makeRangeVarFromNameListSafe(const List *names, Node *escontext)
{
RangeVar *rel = makeRangeVar(NULL, NULL, -1);
@@ -3640,7 +3676,7 @@ makeRangeVarFromNameList(const List *names)
rel->relname = strVal(lthird(names));
break;
default:
- ereport(ERROR,
+ ereturn(escontext, NULL,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("improper relation name (too many dotted names): %s",
NameListToString(names))));
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index e5c2246f2c9..59cc508f805 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -1901,12 +1901,19 @@ text_regclass(PG_FUNCTION_ARGS)
text *relname = PG_GETARG_TEXT_PP(0);
Oid result;
RangeVar *rv;
+ List *namelist;
- rv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
+ namelist = textToQualifiedNameListSafe(relname, fcinfo->context);
+ if (SOFT_ERROR_OCCURRED(fcinfo->context))
+ PG_RETURN_NULL();
+
+ rv = makeRangeVarFromNameListSafe(namelist, fcinfo->context);
+ if (SOFT_ERROR_OCCURRED(fcinfo->context))
+ PG_RETURN_NULL();
/* We might not even have permissions on this relation; don't lock it. */
- result = RangeVarGetRelid(rv, NoLock, false);
-
+ result = RangeVarGetRelidExtendedSafe(rv, NoLock, 0, NULL, NULL,
+ fcinfo->context);
PG_RETURN_OID(result);
}
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 3894457ab40..f8becbcde51 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -2684,6 +2684,12 @@ name_text(PG_FUNCTION_ARGS)
*/
List *
textToQualifiedNameList(text *textval)
+{
+ return textToQualifiedNameListSafe(textval, NULL);
+}
+
+List *
+textToQualifiedNameListSafe(text *textval, Node *escontext)
{
char *rawname;
List *result = NIL;
@@ -2695,12 +2701,12 @@ textToQualifiedNameList(text *textval)
rawname = text_to_cstring(textval);
if (!SplitIdentifierString(rawname, '.', &namelist))
- ereport(ERROR,
+ ereturn(escontext, NIL,
(errcode(ERRCODE_INVALID_NAME),
errmsg("invalid name syntax")));
if (namelist == NIL)
- ereport(ERROR,
+ ereturn(escontext, NIL,
(errcode(ERRCODE_INVALID_NAME),
errmsg("invalid name syntax")));
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 9e8016456ce..8de1f1fc741 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -1043,7 +1043,7 @@ xmlparse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace, Node
return (xmltype *) data;
#else
- ereturn(escontext, NULL
+ ereturn(escontext, NULL,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("unsupported XML feature"),
errdetail("This functionality requires the server to be built with libxml support."));
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] v12-0020-CAST-expr-AS-newtype-DEFAULT-ON-ERROR.patch (145.8K, 20-v12-0020-CAST-expr-AS-newtype-DEFAULT-ON-ERROR.patch)
download | inline diff:
From 60228e5c831f170b122da208641061e5ae07b06b Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Tue, 25 Nov 2025 10:37:33 +0800
Subject: [PATCH v12 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 | 29 +
src/backend/catalog/pg_cast.c | 1 +
src/backend/executor/execExpr.c | 61 +-
src/backend/executor/execExprInterp.c | 29 +
src/backend/jit/llvm/llvmjit_expr.c | 26 +
src/backend/nodes/nodeFuncs.c | 61 ++
src/backend/optimizer/util/clauses.c | 26 +
src/backend/parser/gram.y | 22 +
src/backend/parser/parse_agg.c | 9 +
src/backend/parser/parse_expr.c | 424 +++++++++-
src/backend/parser/parse_func.c | 3 +
src/backend/parser/parse_target.c | 14 +
src/backend/utils/adt/arrayfuncs.c | 8 +
src/backend/utils/adt/ruleutils.c | 21 +
src/include/catalog/pg_cast.dat | 330 ++++----
src/include/catalog/pg_cast.h | 4 +
src/include/executor/execExpr.h | 7 +
src/include/nodes/execnodes.h | 21 +
src/include/nodes/parsenodes.h | 11 +
src/include/nodes/primnodes.h | 36 +
src/include/parser/parse_node.h | 2 +
src/test/regress/expected/cast.out | 791 ++++++++++++++++++
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 | 340 ++++++++
src/test/regress/sql/create_cast.sql | 1 +
src/tools/pgindent/typedefs.list | 3 +
31 files changed, 2157 insertions(+), 194 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 2fc63442980..ccd1d256003 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 error safe type cast.
+ For further details see <xref linkend="sql-syntax-type-casts-safe"/>.
+ </para></entry>
+ </row>
+
</tbody>
</tgroup>
</table>
diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml
index 34c83880a66..9b2482e9b99 100644
--- a/doc/src/sgml/syntax.sgml
+++ b/doc/src/sgml/syntax.sgml
@@ -2106,6 +2106,10 @@ CAST ( <replaceable>expression</replaceable> AS <replaceable>type</replaceable>
The <literal>CAST</literal> syntax conforms to SQL; the syntax with
<literal>::</literal> is historical <productname>PostgreSQL</productname>
usage.
+ The equivalent ON CONVERSION ERROR behavior is:
+<synopsis>
+CAST ( <replaceable>expression</replaceable> AS <replaceable>type</replaceable> ERROR ON CONVERSION ERROR )
+</synopsis>
</para>
<para>
@@ -2160,6 +2164,31 @@ CAST ( <replaceable>expression</replaceable> AS <replaceable>type</replaceable>
<xref linkend="sql-createcast"/>.
</para>
</note>
+
+ <sect3 id="sql-syntax-type-casts-safe">
+ <title>Safe Type Cast</title>
+ <para>
+Sometimes a type cast may fail; to handle such cases, an <literal>ON ERROR</literal> clause can be
+specified to avoid potential errors. The syntax is:
+<synopsis>
+CAST ( <replaceable>expression</replaceable> AS <replaceable>type</replaceable> DEFAULT <replaceable>expression</replaceable> ON CONVERSION ERROR )
+</synopsis>
+ If cast the source expression to target type fails, it falls back to
+ evaluating the optionally supplied default <replaceable>expression</replaceable>
+ specified in ON <literal>ON ERROR</literal> clause.
+ Currently, this only support built-in system type casts,
+ casts created using <link linkend="sql-createcast">CREATE CAST</link> are not supported.
+ </para>
+
+ <para>
+ For example, the following query attempts to cast a <type>text</type> type value to <type>integer</type> type,
+ but when the conversion fails, it falls back to evaluate the supplied default expression.
+<programlisting>
+SELECT CAST(TEXT 'error' AS integer DEFAULT NULL ON CONVERSION ERROR) IS NULL; <lineannotation>true</lineannotation>
+</programlisting>
+ </para>
+ </sect3>
+
</sect2>
<sect2 id="sql-syntax-collate-exprs">
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..1de0d465f56 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,53 @@ 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 we can not coerce to the target type, fallback to the DEFAULT
+ * expression specified in the ON CONVERSION ERROR clause, and we are done.
+ */
+ if (stcexpr->cast_expr == NULL)
+ {
+ ExecInitExprRec((Expr *) stcexpr->default_expr,
+ state, resv, resnull);
+ return;
+ }
+ else
+ {
+ SafeTypeCastState *stcstate;
+ ErrorSaveContext *saved_escontext;
+
+ 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);
+
+ /* Steps to evaluate the DEFAULT expression */
+ saved_escontext = state->escontext;
+ state->escontext = NULL;
+ ExecInitExprRec((Expr *) stcstate->stcexpr->default_expr,
+ state, resv, resnull);
+ state->escontext = saved_escontext;
+
+ 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..f1d8df1ac41 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,28 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
+ EEO_CASE(EEOP_SAFETYPE_CAST)
+ {
+ SafeTypeCastState *stcstate = op->d.stcexpr.stcstate;
+
+ if (SOFT_ERROR_OCCURRED(&stcstate->escontext))
+ {
+ *op->resvalue = (Datum) 0;
+ *op->resnull = true;
+
+ /*
+ * Reset for next use such as for catching errors when coercing
+ * a expression.
+ */
+ stcstate->escontext.error_occurred = false;
+ stcstate->escontext.details_wanted = false;
+
+ EEO_NEXT();
+ }
+ else
+ EEO_JUMP(stcstate->jump_end);
+ }
+
EEO_CASE(EEOP_JSONEXPR_PATH)
{
/* too complex for an inline implementation */
@@ -3644,6 +3667,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;
+ }
}
/*
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index ac88881e995..bc7cd3283a5 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -2256,6 +2256,32 @@ llvm_compile_expr(ExprState *state)
LLVMBuildBr(b, opblocks[opno + 1]);
break;
+ case EEOP_SAFETYPE_CAST:
+ {
+ SafeTypeCastState *stcstate = op->d.stcexpr.stcstate;
+
+ if (SOFT_ERROR_OCCURRED(&stcstate->escontext))
+ {
+ /*
+ * Reset for next use such as for catching errors when
+ * coercing a expression.
+ */
+ stcstate->escontext.error_occurred = false;
+ stcstate->escontext.details_wanted = false;
+
+ /* set resnull to true */
+ LLVMBuildStore(b, l_sbool_const(1), v_resnullp);
+ /* reset resvalue */
+ LLVMBuildStore(b, l_datum_const(0), v_resvaluep);
+
+ LLVMBuildBr(b, opblocks[opno + 1]);
+ }
+ else
+ 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..fe3e6b11a08 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;
@@ -1706,6 +1717,9 @@ exprLocation(const Node *expr)
loc = leftmostLoc(loc, tc->location);
}
break;
+ case T_SafeTypeCastExpr:
+ loc = ((const SafeTypeCastExpr *) expr)->location;
+ break;
case T_CollateClause:
/* just use argument's location */
loc = exprLocation(((const CollateClause *) expr)->arg);
@@ -2321,6 +2335,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 +3356,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 +4503,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->raw_default))
+ 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 32af0df6c70..b6a9dc2f5fd 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -2951,6 +2951,32 @@ 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 c3a0a354a9c..8a27e045bc0 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -16013,6 +16013,28 @@ func_expr_common_subexpr:
}
| CAST '(' a_expr AS Typename ')'
{ $$ = makeTypeCast($3, $5, @1); }
+ | CAST '(' a_expr AS Typename ERROR_P ON CONVERSION_P ERROR_P ')'
+ { $$ = makeTypeCast($3, $5, @1); }
+ | CAST '(' a_expr AS Typename NULL_P ON CONVERSION_P ERROR_P ')'
+ {
+ TypeCast *cast = (TypeCast *) makeTypeCast($3, $5, @1);
+
+ SafeTypeCast *safecast = makeNode(SafeTypeCast);
+ safecast->cast = (Node *) cast;
+ safecast->raw_default = makeNullAConst(-1);;
+
+ $$ = (Node *) safecast;
+ }
+ | CAST '(' a_expr AS Typename DEFAULT a_expr ON CONVERSION_P ERROR_P ')'
+ {
+ TypeCast *cast = (TypeCast *) makeTypeCast($3, $5, @1);
+
+ SafeTypeCast *safecast = makeNode(SafeTypeCast);
+ safecast->cast = (Node *) cast;
+ safecast->raw_default = $7;
+
+ $$ = (Node *) safecast;
+ }
| EXTRACT '(' extract_list ')'
{
$$ = (Node *) makeFuncCall(SystemFuncName("extract"),
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index b8340557b34..de77f6e9302 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -490,6 +490,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:
@@ -983,6 +989,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 32d6ae918ca..ec43fcd18fb 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);
@@ -164,13 +169,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;
@@ -564,6 +573,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
case EXPR_KIND_VALUES_SINGLE:
case EXPR_KIND_CHECK_CONSTRAINT:
case EXPR_KIND_DOMAIN_CHECK:
+ case EXPR_KIND_CAST_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
case EXPR_KIND_INDEX_EXPRESSION:
case EXPR_KIND_INDEX_PREDICATE:
@@ -1824,6 +1834,9 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
case EXPR_KIND_DOMAIN_CHECK:
err = _("cannot use subquery in check constraint");
break;
+ case EXPR_KIND_CAST_DEFAULT:
+ err = _("cannot use subquery in CAST DEFAULT expression");
+ break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
err = _("cannot use subquery in DEFAULT expression");
@@ -2011,16 +2024,23 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
* 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, can_coerce will be NULL.
+ * can_coerce is not NULL only when performing parse analysis for expressions
+ * like CAST(DEFAULT ... ON CONVERSION ERROR). can_coerce 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;
List *newcoercedelems = NIL;
ListCell *element;
Oid coerce_type;
+ Oid coerce_type_coll;
bool coerce_hard;
/*
@@ -2045,9 +2065,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 +2109,9 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
}
else
{
+ /* The target type is valid when performing type-safe cast */
+ Assert(can_coerce == NULL);
+
/* Can't handle an empty array without a target type */
if (newelems == NIL)
ereport(ERROR,
@@ -2125,6 +2149,8 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
coerce_hard = false;
}
+ coerce_type_coll = get_typcollation(coerce_type);
+
/*
* Coerce elements to target type
*
@@ -2134,13 +2160,53 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
* If the array's type was merely derived from the common type of its
* elements, then the elements are implicitly coerced to the common type.
* This is consistent with other uses of select_common_type().
+ *
+ * If can_coerce is not NULL, we need to safely test whether each element
+ * can be coerced to the target type, similar to what is done in
+ * transformTypeSafeCast.
*/
foreach(element, newelems)
{
Node *e = (Node *) lfirst(element);
- Node *newe;
+ bool expr_is_const = false;
+ Node *newe = NULL;
- if (coerce_hard)
+ if (can_coerce != NULL && IsA(e, Const))
+ {
+ expr_is_const = true;
+
+ /* coerce UNKNOWN Const to the target type */
+ if (*can_coerce && exprType(e) == UNKNOWNOID)
+ {
+ newe = CoerceUnknownConstSafe(pstate, e, coerce_type, typmod,
+ COERCION_IMPLICIT,
+ COERCE_IMPLICIT_CAST,
+ -1,
+ false);
+ if (newe == NULL)
+ {
+ newe = e;
+ *can_coerce = false;
+ }
+ newcoercedelems = lappend(newcoercedelems, newe);
+
+ /*
+ * Done handling this UNKNOWN Const element; move on to the
+ * next.
+ */
+ continue;
+ }
+ }
+
+ if (can_coerce != NULL && (!*can_coerce))
+ {
+ /*
+ * Can not coerce source expression to the target type, just append
+ * the transformed element expression to the list.
+ */
+ newe = e;
+ }
+ else if (coerce_hard)
{
newe = coerce_to_target_type(pstate, e,
exprType(e),
@@ -2150,12 +2216,74 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
COERCE_EXPLICIT_CAST,
-1);
if (newe == 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)
+ 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))));
+ else
+ {
+ /*
+ * Can not coerce source expression to the target type, just
+ * append the transformed element expression to the list.
+ */
+ newe = e;
+ *can_coerce = false;
+ }
+ }
+ else if (can_coerce != NULL && expr_is_const)
+ {
+ Node *origexpr = newe;
+ Node *result;
+ bool have_collate_expr = false;
+
+ if (IsA(newe, CollateExpr))
+ have_collate_expr = true;
+
+ while (newe && IsA(newe, CollateExpr))
+ newe = (Node *) ((CollateExpr *) newe)->arg;
+
+ if (!IsA(newe, FuncExpr))
+ newe = origexpr;
+ else
+ {
+ /*
+ * pre-evaluate simple constant cast expressions in a way
+ * that tolerate errors.
+ */
+ FuncExpr *newexpr = (FuncExpr *) copyObject(newe);
+
+ assign_expr_collations(pstate, (Node *) newexpr);
+
+ result = (Node *) evaluate_expr_extended((Expr *) newexpr,
+ coerce_type,
+ typmod,
+ coerce_type_coll,
+ true);
+
+ if (result == NULL)
+ {
+ newe = e;
+ *can_coerce = false;
+ }
+ else if (have_collate_expr)
+ {
+ /* Reinstall top CollateExpr */
+ CollateExpr *coll = (CollateExpr *) origexpr;
+ CollateExpr *newcoll = makeNode(CollateExpr);
+
+ newcoll->arg = (Expr *) result;
+ newcoll->collOid = coll->collOid;
+ newcoll->location = coll->location;
+ newe = (Node *) newcoll;
+ }
+ else
+ newe = result;
+ }
+ }
}
else
newe = coerce_to_common_type(pstate, e,
@@ -2743,7 +2871,8 @@ transformTypeCast(ParseState *pstate, TypeCast *tc)
(A_ArrayExpr *) arg,
targetBaseType,
elementType,
- targetBaseTypmod);
+ targetBaseTypmod,
+ NULL);
}
else
expr = transformExprRecurse(pstate, arg);
@@ -2780,6 +2909,271 @@ 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 *source_expr;
+ Node *def_expr;
+ Node *cast_expr = NULL;
+ Oid inputType;
+ Oid targetType;
+ Oid targetBaseType;
+ Oid typeColl;
+ int32 targetTypmod;
+ int32 targetBaseTypmod;
+ bool can_coerce = true;
+ bool expr_is_const = false;
+ ParseLoc 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 DEFAULT expression */
+ def_expr = transformExpr(pstate, tc->raw_default, 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));
+ 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->raw_default), def_expr));
+
+ assign_expr_collations(pstate, 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 */
+
+ /*
+ * 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 && IsA(source_expr, Const))
+ {
+ expr_is_const = true;
+
+ /* coerce UNKNOWN Const to the target type */
+ if (exprType(source_expr) == UNKNOWNOID)
+ {
+ cast_expr = CoerceUnknownConstSafe(pstate, source_expr,
+ targetType, targetTypmod,
+ COERCION_EXPLICIT,
+ COERCE_EXPLICIT_CAST,
+ location, false);
+ if (cast_expr == NULL)
+ can_coerce = false;
+ }
+ }
+
+ if (can_coerce && cast_expr == NULL)
+ {
+ cast_expr = coerce_to_target_type(pstate, source_expr, inputType,
+ targetType, targetTypmod,
+ COERCION_EXPLICIT,
+ COERCE_EXPLICIT_CAST,
+ location);
+ if (cast_expr == NULL)
+ can_coerce = false;
+ }
+
+ if (can_coerce)
+ {
+ Node *origexpr = cast_expr;
+ bool have_collate_expr = false;
+
+ Assert(cast_expr != NULL);
+
+ if (IsA(cast_expr, CollateExpr))
+ have_collate_expr = true;
+
+ while (cast_expr && IsA(cast_expr, CollateExpr))
+ cast_expr = (Node *) ((CollateExpr *) cast_expr)->arg;
+
+ if (IsA(cast_expr, FuncExpr))
+ {
+ HeapTuple tuple;
+ ListCell *lc;
+ Node *sexpr;
+ Node *result;
+ 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("Explicit cast is defined but definition is not declared as safe"),
+ 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)
+ cast_expr = origexpr;
+ else
+ {
+ /*
+ * pre-evaluate simple constant cast expressions early in a way
+ * that tolerate 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.
+ * 3. Even if FuncExpr (for castfunc) node has three arguments,
+ * the second and third arguments will also be constants per
+ * coerce_to_target_type. Evaluating a FuncExpr whose
+ * arguments are all Const is safe.
+ */
+ FuncExpr *newexpr = (FuncExpr *) copyObject(cast_expr);
+
+ assign_expr_collations(pstate, (Node *) newexpr);
+
+ result = (Node *) evaluate_expr_extended((Expr *) newexpr,
+ targetType,
+ targetTypmod,
+ typeColl,
+ true);
+ if (result == NULL)
+ {
+ /*
+ * source expression can not coerce to the target type.
+ */
+ can_coerce = false;
+ cast_expr = NULL;
+ }
+ else if (have_collate_expr)
+ {
+ /* Reinstall top CollateExpr */
+ CollateExpr *coll = (CollateExpr *) origexpr;
+ CollateExpr *newcoll = makeNode(CollateExpr);
+
+ newcoll->arg = (Expr *) result;
+ newcoll->collOid = coll->collOid;
+ newcoll->location = coll->location;
+ cast_expr = (Node *) newcoll;
+ }
+ else
+ cast_expr = result;
+ }
+ }
+ else
+ /* Nothing to do, restore cast_expr to its original value */
+ 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;
+ result->location = location;
+
+ return (Node *) result;
+}
+
/*
* Handle an explicit COLLATE clause.
*
@@ -3193,6 +3587,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..dc03cf4ce74 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 a464349ee33..c1a7031a96c 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -3289,6 +3289,14 @@ array_map(Datum arrayd,
/* Apply the given expression to source element */
values[i] = ExecEvalExpr(exprstate, econtext, &nulls[i]);
+ /* Exit early if the evaluation fails */
+ 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 556ab057e5a..3e41c7b8188 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -10562,6 +10562,27 @@ get_rule_expr(Node *node, deparse_context *context,
}
break;
+ case T_SafeTypeCastExpr:
+ {
+ SafeTypeCastExpr *stcexpr = castNode(SafeTypeCastExpr, node);
+
+ /*
+ * We cannot deparsing cast_expr directly, since
+ * transformTypeSafeCast may have folded it into a simple
+ * constant or NULL. Instead, we use source_expr and
+ * default_expr to reconstruct the CAST DEFAULT clause.
+ */
+ 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..ca52cfcd086 100644
--- a/src/include/catalog/pg_cast.dat
+++ b/src/include/catalog/pg_cast.dat
@@ -19,65 +19,65 @@
# int2->int4->int8->numeric->float4->float8, while casts in the
# reverse direction are assignment-only.
{ castsource => 'int8', casttarget => 'int2', castfunc => 'int2(int8)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int8', casttarget => 'int4', castfunc => 'int4(int8)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int8', casttarget => 'float4', castfunc => 'float4(int8)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int8', casttarget => 'float8', castfunc => 'float8(int8)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int8', casttarget => 'numeric', castfunc => 'numeric(int8)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'int8', castfunc => 'int8(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'int4', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'float4', castfunc => 'float4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'float8', castfunc => 'float8(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'numeric', castfunc => 'numeric(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'int8', castfunc => 'int8(int4)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'int2', castfunc => 'int2(int4)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'float4', castfunc => 'float4(int4)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'float8', castfunc => 'float8(int4)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'numeric', castfunc => 'numeric(int4)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'float4', casttarget => 'int8', castfunc => 'int8(float4)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'float4', casttarget => 'int2', castfunc => 'int2(float4)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'float4', casttarget => 'int4', castfunc => 'int4(float4)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'float4', casttarget => 'float8', castfunc => 'float8(float4)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'float4', casttarget => 'numeric',
- castfunc => 'numeric(float4)', castcontext => 'a', castmethod => 'f' },
+ castfunc => 'numeric(float4)', castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'float8', casttarget => 'int8', castfunc => 'int8(float8)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'float8', casttarget => 'int2', castfunc => 'int2(float8)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'float8', casttarget => 'int4', castfunc => 'int4(float8)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'float8', casttarget => 'float4', castfunc => 'float4(float8)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'float8', casttarget => 'numeric',
- castfunc => 'numeric(float8)', castcontext => 'a', castmethod => 'f' },
+ castfunc => 'numeric(float8)', castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'numeric', casttarget => 'int8', castfunc => 'int8(numeric)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'numeric', casttarget => 'int2', castfunc => 'int2(numeric)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'numeric', casttarget => 'int4', castfunc => 'int4(numeric)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'numeric', casttarget => 'float4',
- castfunc => 'float4(numeric)', castcontext => 'i', castmethod => 'f' },
+ castfunc => 'float4(numeric)', castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'numeric', casttarget => 'float8',
- castfunc => 'float8(numeric)', castcontext => 'i', castmethod => 'f' },
+ castfunc => 'float8(numeric)', castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'money', casttarget => 'numeric', castfunc => 'numeric(money)',
castcontext => 'a', castmethod => 'f' },
{ castsource => 'numeric', casttarget => 'money', castfunc => 'money(numeric)',
@@ -89,13 +89,13 @@
# Allow explicit coercions between int4 and bool
{ castsource => 'int4', casttarget => 'bool', castfunc => 'bool(int4)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bool', casttarget => 'int4', castfunc => 'int4(bool)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
# Allow explicit coercions between xid8 and xid
{ castsource => 'xid8', casttarget => 'xid', castfunc => 'xid(xid8)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
# OID category: allow implicit conversion from any integral type (including
# int8, to support OID literals > 2G) to OID, as well as assignment coercion
@@ -106,13 +106,13 @@
# casts from text and varchar to regclass, which exist mainly to support
# legacy forms of nextval() and related functions.
{ castsource => 'int8', casttarget => 'oid', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'oid', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'oid', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'oid', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'oid', casttarget => 'regproc', castfunc => '0',
@@ -120,13 +120,13 @@
{ castsource => 'regproc', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'int8', casttarget => 'regproc', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'regproc', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'regproc', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regproc', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'regproc', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'regproc', casttarget => 'regprocedure', castfunc => '0',
@@ -138,13 +138,13 @@
{ castsource => 'regprocedure', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'int8', casttarget => 'regprocedure', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'regprocedure', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'regprocedure', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regprocedure', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'regprocedure', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'oid', casttarget => 'regoper', castfunc => '0',
@@ -152,13 +152,13 @@
{ castsource => 'regoper', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'int8', casttarget => 'regoper', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'regoper', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'regoper', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regoper', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'regoper', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'regoper', casttarget => 'regoperator', castfunc => '0',
@@ -170,13 +170,13 @@
{ castsource => 'regoperator', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'int8', casttarget => 'regoperator', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'regoperator', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'regoperator', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regoperator', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'regoperator', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'oid', casttarget => 'regclass', castfunc => '0',
@@ -184,13 +184,13 @@
{ castsource => 'regclass', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'int8', casttarget => 'regclass', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'regclass', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'regclass', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regclass', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'regclass', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'oid', casttarget => 'regcollation', castfunc => '0',
@@ -198,13 +198,13 @@
{ castsource => 'regcollation', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'int8', casttarget => 'regcollation', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'regcollation', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'regcollation', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regcollation', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'regcollation', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'oid', casttarget => 'regtype', castfunc => '0',
@@ -212,13 +212,13 @@
{ castsource => 'regtype', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'int8', casttarget => 'regtype', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'regtype', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'regtype', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regtype', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'regtype', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'oid', casttarget => 'regconfig', castfunc => '0',
@@ -226,13 +226,13 @@
{ castsource => 'regconfig', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'int8', casttarget => 'regconfig', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'regconfig', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'regconfig', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regconfig', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'regconfig', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'oid', casttarget => 'regdictionary', castfunc => '0',
@@ -240,31 +240,31 @@
{ castsource => 'regdictionary', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'int8', casttarget => 'regdictionary', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'regdictionary', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'regdictionary', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regdictionary', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'regdictionary', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'text', casttarget => 'regclass', castfunc => 'regclass',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'varchar', casttarget => 'regclass', castfunc => 'regclass',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'oid', casttarget => 'regrole', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regrole', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'int8', casttarget => 'regrole', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'regrole', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'regrole', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regrole', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'regrole', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'oid', casttarget => 'regnamespace', castfunc => '0',
@@ -272,13 +272,13 @@
{ castsource => 'regnamespace', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'int8', casttarget => 'regnamespace', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'regnamespace', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'regnamespace', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regnamespace', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'regnamespace', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'oid', casttarget => 'regdatabase', castfunc => '0',
@@ -286,13 +286,13 @@
{ castsource => 'regdatabase', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'int8', casttarget => 'regdatabase', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'regdatabase', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'regdatabase', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regdatabase', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'regdatabase', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
@@ -302,57 +302,57 @@
{ castsource => 'text', casttarget => 'varchar', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'bpchar', casttarget => 'text', castfunc => 'text(bpchar)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bpchar', casttarget => 'varchar', castfunc => 'text(bpchar)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'varchar', casttarget => 'text', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'varchar', casttarget => 'bpchar', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'char', casttarget => 'text', castfunc => 'text(char)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'char', casttarget => 'bpchar', castfunc => 'bpchar(char)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'char', casttarget => 'varchar', castfunc => 'text(char)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'name', casttarget => 'text', castfunc => 'text(name)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'name', casttarget => 'bpchar', castfunc => 'bpchar(name)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'name', casttarget => 'varchar', castfunc => 'varchar(name)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'text', casttarget => 'char', castfunc => 'char(text)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bpchar', casttarget => 'char', castfunc => 'char(text)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'varchar', casttarget => 'char', castfunc => 'char(text)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'text', casttarget => 'name', castfunc => 'name(text)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bpchar', casttarget => 'name', castfunc => 'name(bpchar)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'varchar', casttarget => 'name', castfunc => 'name(varchar)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
# Allow explicit coercions between bytea and integer types
{ castsource => 'int2', casttarget => 'bytea', castfunc => 'bytea(int2)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'bytea', castfunc => 'bytea(int4)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int8', casttarget => 'bytea', castfunc => 'bytea(int8)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bytea', casttarget => 'int2', castfunc => 'int2(bytea)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bytea', casttarget => 'int4', castfunc => 'int4(bytea)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bytea', casttarget => 'int8', castfunc => 'int8(bytea)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
# Allow explicit coercions between int4 and "char"
{ castsource => 'char', casttarget => 'int4', castfunc => 'int4(char)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'char', castfunc => 'char(int4)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
# pg_node_tree can be coerced to, but not from, text
{ castsource => 'pg_node_tree', casttarget => 'text', castfunc => '0',
@@ -378,73 +378,73 @@
# Datetime category
{ castsource => 'date', casttarget => 'timestamp',
- castfunc => 'timestamp(date)', castcontext => 'i', castmethod => 'f' },
+ castfunc => 'timestamp(date)', castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'date', casttarget => 'timestamptz',
- castfunc => 'timestamptz(date)', castcontext => 'i', castmethod => 'f' },
+ castfunc => 'timestamptz(date)', castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'time', casttarget => 'interval', castfunc => 'interval(time)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'time', casttarget => 'timetz', castfunc => 'timetz(time)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'timestamp', casttarget => 'date',
- castfunc => 'date(timestamp)', castcontext => 'a', castmethod => 'f' },
+ castfunc => 'date(timestamp)', castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'timestamp', casttarget => 'time',
- castfunc => 'time(timestamp)', castcontext => 'a', castmethod => 'f' },
+ castfunc => 'time(timestamp)', castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'timestamp', casttarget => 'timestamptz',
- castfunc => 'timestamptz(timestamp)', castcontext => 'i', castmethod => 'f' },
+ castfunc => 'timestamptz(timestamp)', castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'timestamptz', casttarget => 'date',
- castfunc => 'date(timestamptz)', castcontext => 'a', castmethod => 'f' },
+ castfunc => 'date(timestamptz)', castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'timestamptz', casttarget => 'time',
- castfunc => 'time(timestamptz)', castcontext => 'a', castmethod => 'f' },
+ castfunc => 'time(timestamptz)', castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'timestamptz', casttarget => 'timestamp',
- castfunc => 'timestamp(timestamptz)', castcontext => 'a', castmethod => 'f' },
+ castfunc => 'timestamp(timestamptz)', castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'timestamptz', casttarget => 'timetz',
- castfunc => 'timetz(timestamptz)', castcontext => 'a', castmethod => 'f' },
+ castfunc => 'timetz(timestamptz)', castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'interval', casttarget => 'time', castfunc => 'time(interval)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'timetz', casttarget => 'time', castfunc => 'time(timetz)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
# Geometric category
{ castsource => 'point', casttarget => 'box', castfunc => 'box(point)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'lseg', casttarget => 'point', castfunc => 'point(lseg)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'path', casttarget => 'polygon', castfunc => 'polygon(path)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'box', casttarget => 'point', castfunc => 'point(box)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'box', casttarget => 'lseg', castfunc => 'lseg(box)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'box', casttarget => 'polygon', castfunc => 'polygon(box)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'box', casttarget => 'circle', castfunc => 'circle(box)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'polygon', casttarget => 'point', castfunc => 'point(polygon)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'polygon', casttarget => 'path', castfunc => 'path',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'polygon', casttarget => 'box', castfunc => 'box(polygon)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'polygon', casttarget => 'circle',
- castfunc => 'circle(polygon)', castcontext => 'e', castmethod => 'f' },
+ castfunc => 'circle(polygon)', castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'circle', casttarget => 'point', castfunc => 'point(circle)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'circle', casttarget => 'box', castfunc => 'box(circle)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'circle', casttarget => 'polygon',
- castfunc => 'polygon(circle)', castcontext => 'e', castmethod => 'f' },
+ castfunc => 'polygon(circle)', castcontext => 'e', castmethod => 'f'},
# MAC address category
{ castsource => 'macaddr', casttarget => 'macaddr8', castfunc => 'macaddr8',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'macaddr8', casttarget => 'macaddr', castfunc => 'macaddr',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
# INET category
{ castsource => 'cidr', casttarget => 'inet', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'inet', casttarget => 'cidr', castfunc => 'cidr',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
# BitString category
{ castsource => 'bit', casttarget => 'varbit', castfunc => '0',
@@ -454,13 +454,13 @@
# Cross-category casts between bit and int4, int8
{ castsource => 'int8', casttarget => 'bit', castfunc => 'bit(int8,int4)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'bit', castfunc => 'bit(int4,int4)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bit', casttarget => 'int8', castfunc => 'int8(bit)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bit', casttarget => 'int4', castfunc => 'int4(bit)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
# Cross-category casts to and from TEXT
# We need entries here only for a few specialized cases where the behavior
@@ -471,68 +471,68 @@
# behavior will ensue when the automatic cast is applied instead of the
# pg_cast entry!
{ castsource => 'cidr', casttarget => 'text', castfunc => 'text(inet)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'inet', casttarget => 'text', castfunc => 'text(inet)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bool', casttarget => 'text', castfunc => 'text(bool)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'xml', casttarget => 'text', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'text', casttarget => 'xml', castfunc => 'xml',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
# Cross-category casts to and from VARCHAR
# We support all the same casts as for TEXT.
{ castsource => 'cidr', casttarget => 'varchar', castfunc => 'text(inet)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'inet', casttarget => 'varchar', castfunc => 'text(inet)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bool', casttarget => 'varchar', castfunc => 'text(bool)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'xml', casttarget => 'varchar', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'varchar', casttarget => 'xml', castfunc => 'xml',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
# Cross-category casts to and from BPCHAR
# We support all the same casts as for TEXT.
{ castsource => 'cidr', casttarget => 'bpchar', castfunc => 'text(inet)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'inet', casttarget => 'bpchar', castfunc => 'text(inet)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bool', casttarget => 'bpchar', castfunc => 'text(bool)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'xml', casttarget => 'bpchar', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'bpchar', casttarget => 'xml', castfunc => 'xml',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
# Length-coercion functions
{ castsource => 'bpchar', casttarget => 'bpchar',
castfunc => 'bpchar(bpchar,int4,bool)', castcontext => 'i',
- castmethod => 'f' },
+ castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'varchar', casttarget => 'varchar',
castfunc => 'varchar(varchar,int4,bool)', castcontext => 'i',
- castmethod => 'f' },
+ castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'time', casttarget => 'time', castfunc => 'time(time,int4)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'timestamp', casttarget => 'timestamp',
castfunc => 'timestamp(timestamp,int4)', castcontext => 'i',
- castmethod => 'f' },
+ castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'timestamptz', casttarget => 'timestamptz',
castfunc => 'timestamptz(timestamptz,int4)', castcontext => 'i',
- castmethod => 'f' },
+ castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'interval', casttarget => 'interval',
castfunc => 'interval(interval,int4)', castcontext => 'i',
- castmethod => 'f' },
+ castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'timetz', casttarget => 'timetz',
- castfunc => 'timetz(timetz,int4)', castcontext => 'i', castmethod => 'f' },
+ castfunc => 'timetz(timetz,int4)', castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bit', casttarget => 'bit', castfunc => 'bit(bit,int4,bool)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'varbit', casttarget => 'varbit', castfunc => 'varbit',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'numeric', casttarget => 'numeric',
- castfunc => 'numeric(numeric,int4)', castcontext => 'i', castmethod => 'f' },
+ castfunc => 'numeric(numeric,int4)', castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
# json to/from jsonb
{ castsource => 'json', casttarget => 'jsonb', castfunc => '0',
@@ -542,36 +542,36 @@
# jsonb to numeric and bool types
{ castsource => 'jsonb', casttarget => 'bool', castfunc => 'bool(jsonb)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'jsonb', casttarget => 'numeric', castfunc => 'numeric(jsonb)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'jsonb', casttarget => 'int2', castfunc => 'int2(jsonb)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'jsonb', casttarget => 'int4', castfunc => 'int4(jsonb)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'jsonb', casttarget => 'int8', castfunc => 'int8(jsonb)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'jsonb', casttarget => 'float4', castfunc => 'float4(jsonb)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'jsonb', casttarget => 'float8', castfunc => 'float8(jsonb)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
# range to multirange
{ castsource => 'int4range', casttarget => 'int4multirange',
castfunc => 'int4multirange(int4range)', castcontext => 'e',
- castmethod => 'f' },
+ castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int8range', casttarget => 'int8multirange',
castfunc => 'int8multirange(int8range)', castcontext => 'e',
- castmethod => 'f' },
+ castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'numrange', casttarget => 'nummultirange',
castfunc => 'nummultirange(numrange)', castcontext => 'e',
- castmethod => 'f' },
+ castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'daterange', casttarget => 'datemultirange',
castfunc => 'datemultirange(daterange)', castcontext => 'e',
- castmethod => 'f' },
+ castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'tsrange', casttarget => 'tsmultirange',
- castfunc => 'tsmultirange(tsrange)', castcontext => 'e', castmethod => 'f' },
+ castfunc => 'tsmultirange(tsrange)', castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'tstzrange', casttarget => 'tstzmultirange',
castfunc => 'tstzmultirange(tstzrange)', castcontext => 'e',
- castmethod => 'f' },
+ castmethod => 'f', casterrorsafe => 't' },
]
diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.h
index 6a0ca337153..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..2f8d163a13e 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
{
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 18ae8f0d4bb..9bd4cecad63 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1059,6 +1059,27 @@ typedef struct DomainConstraintState
ExprState *check_exprstate; /* check_expr's eval state, or NULL */
} DomainConstraintState;
+typedef struct SafeTypeCastState
+{
+ SafeTypeCastExpr *stcexpr;
+
+ /*
+ * Address to jump to when skipping all the steps to evaulate the default
+ * expression.
+ */
+ int jump_end;
+
+ /*
+ * For error-safe evaluation of coercions. 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 d14294a4ece..4b2eb340f97 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -400,6 +400,17 @@ typedef struct TypeCast
ParseLoc location; /* token location, or -1 if unknown */
} TypeCast;
+/*
+ * SafeTypeCast - a CAST(source_expr AS target_type) DEFAULT ON CONVERSION ERROR
+ * construct
+ */
+typedef struct SafeTypeCast
+{
+ NodeTag type;
+ Node *cast; /* TypeCast expression */
+ Node *raw_default; /* DEFAULT expression */
+} SafeTypeCast;
+
/*
* CollateClause - a COLLATE expression
*/
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 1b4436f2ff6..c3bd722cb2f 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -769,6 +769,42 @@ 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; NULL if we can not cocerce source
+ * expression to the 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;
+
+ /* Original SafeTypeCastExpr's location */
+ ParseLoc location;
+
+} 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..cb40ff0f200
--- /dev/null
+++ b/src/test/regress/expected/cast.out
@@ -0,0 +1,791 @@
+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(B'01' AS date DEFAULT NULL ON CONVERSION ERROR);
+ date
+------
+
+(1 row)
+
+SELECT CAST(BIT'01' AS date DEFAULT NULL ON CONVERSION ERROR);
+ date
+------
+
+(1 row)
+
+SELECT CAST(TRUE AS date DEFAULT NULL ON CONVERSION ERROR);
+ date
+------
+
+(1 row)
+
+SELECT CAST(1.1 AS date DEFAULT NULL ON CONVERSION ERROR);
+ date
+------
+
+(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: Explicit cast is defined but definition is not declared as safe
+SELECT CAST(1111 AS "char" DEFAULT NULL ON CONVERSION ERROR);
+ char
+------
+
+(1 row)
+
+--the default expression’s collation should match the collation of the cast
+--target type
+VALUES (CAST('error' AS text DEFAULT '1' COLLATE "C" ON CONVERSION ERROR));
+ERROR: the collation of CAST DEFAULT expression conflicts with target type collation
+LINE 1: VALUES (CAST('error' AS text DEFAULT '1' COLLATE "C" ON CONV...
+ ^
+DETAIL: "default" versus "C"
+VALUES (CAST('error' AS int2vector DEFAULT '1 3' ON CONVERSION ERROR));
+ column1
+---------
+ 1 3
+(1 row)
+
+VALUES (CAST('error' AS int2vector[] DEFAULT '{1 3}' ON CONVERSION ERROR));
+ column1
+---------
+ {"1 3"}
+(1 row)
+
+-- test source expression contain subquery
+SELECT CAST((SELECT b FROM generate_series(1, 1), (VALUES('H')) s(b))
+ AS int2vector[] DEFAULT '{1 3}' ON CONVERSION ERROR);
+ b
+---------
+ {"1 3"}
+(1 row)
+
+SELECT CAST((SELECT ARRAY((SELECT b FROM generate_series(1, 2) g, (VALUES('H')) s(b))))
+ AS INT[]
+ DEFAULT NULL ON CONVERSION ERROR);
+ array
+-------
+
+(1 row)
+
+CREATE 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['a'::text] AS int[] DEFAULT NULL ON CONVERSION ERROR); --error
+ERROR: invalid input syntax for type integer: "a"
+SELECT CAST(array['a'] AS int[] DEFAULT NULL ON CONVERSION ERROR); --ok
+ array
+-------
+
+(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 int[] 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 int[] DEFAULT '{1,2}' ON CONVERSION ERROR);
+ array
+-------
+ {1,2}
+(1 row)
+
+SELECT CAST(ARRAY[['1', '2'], ['three', 'a']] AS int[] DEFAULT '{21,22}' ON CONVERSION ERROR); --ok
+ array
+---------
+ {21,22}
+(1 row)
+
+SELECT CAST(ARRAY[['1', '2'], ['three'::int, 'a']] AS int[] DEFAULT '{21,22}' ON CONVERSION ERROR); --error
+ERROR: invalid input syntax for type integer: "three"
+LINE 1: SELECT CAST(ARRAY[['1', '2'], ['three'::int, 'a']] AS int[] ...
+ ^
+SELECT CAST(ARRAY[1, 'three'::int] AS int[] DEFAULT '{21,22}' ON CONVERSION ERROR); --error
+ERROR: invalid input syntax for type integer: "three"
+LINE 1: SELECT CAST(ARRAY[1, 'three'::int] AS int[] DEFAULT '{21,22}...
+ ^
+-- test valid DEFAULT expression for CAST = ON CONVERSION ERROR
+CREATE 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(c::date::json as date DEFAULT NULL ON CONVERSION ERROR) FROM tcast; --error
+ERROR: cannot cast type date to json
+LINE 1: SELECT CAST(c::date::json as date DEFAULT NULL ON CONVERSION...
+ ^
+SELECT CAST('a'::int as int DEFAULT NULL ON CONVERSION ERROR); --error
+ERROR: invalid input syntax for type integer: "a"
+LINE 1: SELECT CAST('a'::int as int DEFAULT NULL ON CONVERSION ERROR...
+ ^
+SELECT CAST('a' as int DEFAULT NULL ON CONVERSION ERROR); --ok
+ int4
+------
+
+(1 row)
+
+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 (SELECT NULL) ON CONVERSION ERROR); --error
+ERROR: cannot use subquery in CAST DEFAULT expression
+LINE 1: SELECT CAST('a' as int DEFAULT (SELECT NULL) 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)
+
+-- not supported because the cast from circle to polygon is implemented as a SQL
+-- function, which cannot be error-safe.
+SELECT CAST('<(3,5),0>'::circle as polygon DEFAULT NULL ON CONVERSION ERROR);
+ERROR: cannot cast type circle to polygon when DEFAULT expression is specified in CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST('<(3,5),0>'::circle as polygon DEFAULT NULL ON C...
+ ^
+HINT: Explicit cast is defined but definition is not declared as safe
+-----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: Explicit cast is defined but definition is not declared as safe
+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: Explicit cast is defined but definition is not declared as safe
+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: Explicit cast is defined but definition is not declared as safe
+SELECT CAST(NULL::money AS numeric DEFAULT NULL ON CONVERSION ERROR);
+ERROR: cannot cast type money to numeric when DEFAULT expression is specified in CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(NULL::money AS numeric DEFAULT NULL ON CONVERSIO...
+ ^
+HINT: Explicit cast is defined but definition is not declared as safe
+-----safe cast from bytea type to other data types
+SELECT CAST ('\x112233445566778899'::bytea AS int8 DEFAULT NULL ON CONVERSION ERROR);
+ int8
+------
+
+(1 row)
+
+SELECT CAST('\x123456789A'::bytea AS int4 DEFAULT NULL ON CONVERSION ERROR);
+ int4
+------
+
+(1 row)
+
+SELECT CAST('\x123456'::bytea AS int2 DEFAULT NULL ON CONVERSION ERROR);
+ int2
+------
+
+(1 row)
+
+-----safe cast from bit type to other data types
+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)
+
+-----safe cast from text type to other data types
+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)
+
+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 betweeen range data types
+SELECT CAST('[1,2]'::int4range AS int4multirange DEFAULT NULL ON CONVERSION ERROR);
+ int4multirange
+----------------
+ {[1,3)}
+(1 row)
+
+SELECT CAST('[1,2]'::int8range AS int8multirange DEFAULT NULL ON CONVERSION ERROR);
+ int8multirange
+----------------
+ {[1,3)}
+(1 row)
+
+SELECT CAST('[1,2]'::numrange AS nummultirange DEFAULT NULL ON CONVERSION ERROR);
+ nummultirange
+---------------
+ {[1,2]}
+(1 row)
+
+SELECT CAST('[-infinity,infinity]'::daterange AS datemultirange DEFAULT NULL ON CONVERSION ERROR);
+ datemultirange
+------------------------
+ {[-infinity,infinity]}
+(1 row)
+
+SELECT CAST('[-infinity,infinity]'::tsrange AS tsmultirange DEFAULT NULL ON CONVERSION ERROR);
+ tsmultirange
+------------------------
+ {[-infinity,infinity]}
+(1 row)
+
+SELECT CAST('[-infinity,infinity]'::tstzrange AS tstzmultirange DEFAULT NULL ON CONVERSION ERROR);
+ tstzmultirange
+------------------------
+ {[-infinity,infinity]}
+(1 row)
+
+-----safe cast betweeen numeric data types
+CREATE TABLE test_safecast1(
+ col1 float4, col2 float8, col3 numeric, col4 numeric[],
+ col5 int2 default 32767,
+ col6 int4 default 32768,
+ col7 int8 default 4294967296);
+INSERT INTO test_safecast1 VALUES('11.1234', '11.1234', '9223372036854775808', '{11.1234}'::numeric[]);
+INSERT INTO test_safecast1 VALUES('inf', 'inf', 'inf', '{11.1234, 12, inf, NaN}'::numeric[]);
+INSERT INTO test_safecast1 VALUES('-inf', '-inf', '-inf', '{11.1234, 12, -inf, NaN}'::numeric[]);
+INSERT INTO test_safecast1 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 test_safecast1;
+ 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 test_safecast1;
+ 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 test_safecast1;
+ 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 test_safecast1;
+ 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 test_safecast1;
+ 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 test_safecast1;
+ 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 test_safecast1;
+ 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)
+
+-- date/timestamp/interval related safe type cast
+CREATE TABLE test_safecast2(
+ col0 date, col1 timestamp, col2 timestamptz,
+ col3 interval, col4 time, col5 timetz);
+INSERT INTO test_safecast2 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 test_safecast2;
+ 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 test_safecast2;
+ 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 test_safecast2;
+ 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 test_safecast2;
+ 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) IS NOT NULL 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 test_safecast2;
+ time | to_times | to_timetz | to_interval | to_interval_scale
+-------------+-------------+-----------+-------------------------------+-------------------------------
+ 15:36:39 | 15:36:39 | t | @ 15 hours 36 mins 39 secs | @ 15 hours 36 mins 39 secs
+ 23:59:59.99 | 23:59:59.99 | t | @ 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 test_safecast2;
+ timetz | to_time | to_time_scale | to_timetz | to_timetz_scale | to_interval | to_interval_scale
+----------------+-------------+---------------+----------------+-----------------+-------------+-------------------
+ 15:36:39-04 | 15:36:39 | 15:36:39 | 15:36:39-04 | 15:36:39-04 | |
+ 23:59:59.99-07 | 23:59:59.99 | 23:59:59.99 | 23:59:59.99-07 | 23:59:59.99-07 | |
+(2 rows)
+
+CREATE TABLE test_safecast3(col0 jsonb);
+INSERT INTO test_safecast3(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_safecast3;
+ 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 test_safecast1,
+ CAST(ARRAY[['1'], ['three'],['a']] AS int[] DEFAULT '{1,2}' ON CONVERSION ERROR) as cast1,
+ CAST(ARRAY[['1', '2'], ['three', 'a']] AS date[] DEFAULT NULL ON CONVERSION ERROR) as cast2,
+ CAST(ARRAY['three'] AS INT[] DEFAULT '{1,2}' ON CONVERSION ERROR) as cast3;
+\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 test_safecast1,
+ CAST(ARRAY[ARRAY[1], ARRAY['three'], ARRAY['a']] AS integer[] DEFAULT '{1,2}'::integer[] ON CONVERSION ERROR) AS cast1,
+ CAST(ARRAY[ARRAY['1', '2'], ARRAY['three', 'a']] AS date[] DEFAULT NULL::date[] ON CONVERSION ERROR) AS cast2,
+ CAST(ARRAY['three'] AS integer[] DEFAULT '{1,2}'::integer[] ON CONVERSION ERROR) AS cast3
+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'], ARRAY['a']] AS d_int_arr DEFAULT '{1,2}'::integer[]::d_int_arr ON CONVERSION ERROR) AS cast1,
+ CAST(ARRAY[ARRAY[1, 2], ARRAY['three', 'a']] 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)
+
+--error, default expression is mutable
+CREATE INDEX cast_error_idx ON test_safecast3((CAST(col0 as int DEFAULT random(min=>1, max=>1) ON CONVERSION ERROR)));
+ERROR: functions in index expression must be marked IMMUTABLE
+CREATE INDEX cast_error_idx ON test_safecast3((CAST(col0 as xid DEFAULT NULL ON CONVERSION ERROR)));
+ERROR: data type xid has no default operator class for access method "btree"
+HINT: You must specify an operator class for the index or define a default operator class for the data type.
+CREATE INDEX test_safecast3_idx ON test_safecast3((CAST(col0 as int DEFAULT NULL ON CONVERSION ERROR))); --ok
+SELECT pg_get_indexdef('test_safecast3_idx'::regclass);
+ pg_get_indexdef
+------------------------------------------------------------------------------------------------------------------------------------------
+ CREATE INDEX test_safecast3_idx ON public.test_safecast3 USING btree ((CAST(col0 AS integer DEFAULT NULL::integer ON CONVERSION ERROR)))
+(1 row)
+
+DROP TABLE test_safecast;
+DROP TABLE test_safecast1;
+DROP TABLE test_safecast2;
+DROP TABLE test_safecast3;
+DROP TABLE tcast;
+DROP FUNCTION ret_int8;
+DROP FUNCTION ret_setint;
+DROP TYPE comp_domain_with_typmod;
+DROP TYPE comp2;
+DROP DOMAIN d_varchar;
+DROP DOMAIN d_int42;
+DROP DOMAIN d_char3_not_null;
+RESET extra_float_digits;
diff --git a/src/test/regress/expected/create_cast.out b/src/test/regress/expected/create_cast.out
index 0e69644bca2..f4035c338aa 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: Explicit cast is defined but definition is not declared as safe
-- check dependencies generated for that
SELECT pg_describe_object(classid, objid, objsubid) as obj,
pg_describe_object(refclassid, refobjid, refobjsubid) as objref,
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index a357e1d0c0e..81ea244859f 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -943,8 +943,8 @@ SELECT *
FROM pg_cast c
WHERE castsource = 0 OR casttarget = 0 OR castcontext NOT IN ('e', 'a', 'i')
OR castmethod NOT IN ('f', 'b' ,'i');
- oid | castsource | casttarget | castfunc | castcontext | castmethod
------+------------+------------+----------+-------------+------------
+ oid | castsource | casttarget | castfunc | castcontext | castmethod | casterrorsafe
+-----+------------+------------+----------+-------------+------------+---------------
(0 rows)
-- Check that castfunc is nonzero only for cast methods that need a function,
@@ -953,8 +953,8 @@ SELECT *
FROM pg_cast c
WHERE (castmethod = 'f' AND castfunc = 0)
OR (castmethod IN ('b', 'i') AND castfunc <> 0);
- oid | castsource | casttarget | castfunc | castcontext | castmethod
------+------------+------------+----------+-------------+------------
+ oid | castsource | casttarget | castfunc | castcontext | castmethod | casterrorsafe
+-----+------------+------------+----------+-------------+------------+---------------
(0 rows)
-- Look for casts to/from the same type that aren't length coercion functions.
@@ -963,15 +963,15 @@ WHERE (castmethod = 'f' AND castfunc = 0)
SELECT *
FROM pg_cast c
WHERE castsource = casttarget AND castfunc = 0;
- oid | castsource | casttarget | castfunc | castcontext | castmethod
------+------------+------------+----------+-------------+------------
+ oid | castsource | casttarget | castfunc | castcontext | castmethod | casterrorsafe
+-----+------------+------------+----------+-------------+------------+---------------
(0 rows)
SELECT c.*
FROM pg_cast c, pg_proc p
WHERE c.castfunc = p.oid AND p.pronargs < 2 AND castsource = casttarget;
- oid | castsource | casttarget | castfunc | castcontext | castmethod
------+------------+------------+----------+-------------+------------
+ oid | castsource | casttarget | castfunc | castcontext | castmethod | casterrorsafe
+-----+------------+------------+----------+-------------+------------+---------------
(0 rows)
-- Look for cast functions that don't have the right signature. The
@@ -989,8 +989,8 @@ WHERE c.castfunc = p.oid AND
OR (c.castsource = 'character'::regtype AND
p.proargtypes[0] = 'text'::regtype))
OR NOT binary_coercible(p.prorettype, c.casttarget));
- oid | castsource | casttarget | castfunc | castcontext | castmethod
------+------------+------------+----------+-------------+------------
+ oid | castsource | casttarget | castfunc | castcontext | castmethod | casterrorsafe
+-----+------------+------------+----------+-------------+------------+---------------
(0 rows)
SELECT c.*
@@ -998,8 +998,8 @@ FROM pg_cast c, pg_proc p
WHERE c.castfunc = p.oid AND
((p.pronargs > 1 AND p.proargtypes[1] != 'int4'::regtype) OR
(p.pronargs > 2 AND p.proargtypes[2] != 'bool'::regtype));
- oid | castsource | casttarget | castfunc | castcontext | castmethod
------+------------+------------+----------+-------------+------------
+ oid | castsource | casttarget | castfunc | castcontext | castmethod | casterrorsafe
+-----+------------+------------+----------+-------------+------------+---------------
(0 rows)
-- Look for binary compatible casts that do not have the reverse
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index f56482fb9f1..2e086cbc7af 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -81,7 +81,7 @@ test: create_table_like alter_generic alter_operator misc async dbsize merge mis
# collate.linux.utf8 and collate.icu.utf8 tests cannot be run in parallel with each other
# psql depends on create_am
# amutils depends on geometry, create_index_spgist, hash_index, brin
-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..905d1f318b5
--- /dev/null
+++ b/src/test/regress/sql/cast.sql
@@ -0,0 +1,340 @@
+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(B'01' AS date DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST(BIT'01' AS date DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST(TRUE AS date DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST(1.1 AS date DEFAULT NULL 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);
+
+--the default expression’s collation should match the collation of the cast
+--target type
+VALUES (CAST('error' AS text DEFAULT '1' COLLATE "C" ON CONVERSION ERROR));
+
+VALUES (CAST('error' AS int2vector DEFAULT '1 3' ON CONVERSION ERROR));
+VALUES (CAST('error' AS int2vector[] DEFAULT '{1 3}' ON CONVERSION ERROR));
+
+-- test source expression contain subquery
+SELECT CAST((SELECT b FROM generate_series(1, 1), (VALUES('H')) s(b))
+ AS int2vector[] DEFAULT '{1 3}' ON CONVERSION ERROR);
+SELECT CAST((SELECT ARRAY((SELECT b FROM generate_series(1, 2) g, (VALUES('H')) s(b))))
+ AS INT[]
+ DEFAULT NULL ON CONVERSION ERROR);
+
+CREATE 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['a'::text] AS int[] DEFAULT NULL ON CONVERSION ERROR); --error
+SELECT CAST(array['a'] AS int[] DEFAULT NULL ON CONVERSION ERROR); --ok
+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 int[] 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 int[] DEFAULT '{1,2}' ON CONVERSION ERROR);
+SELECT CAST(ARRAY[['1', '2'], ['three', 'a']] AS int[] DEFAULT '{21,22}' ON CONVERSION ERROR); --ok
+SELECT CAST(ARRAY[['1', '2'], ['three'::int, 'a']] AS int[] DEFAULT '{21,22}' ON CONVERSION ERROR); --error
+SELECT CAST(ARRAY[1, 'three'::int] AS int[] DEFAULT '{21,22}' ON CONVERSION ERROR); --error
+
+-- test valid DEFAULT expression for CAST = ON CONVERSION ERROR
+CREATE 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(c::date::json as date DEFAULT NULL ON CONVERSION ERROR) FROM tcast; --error
+SELECT CAST('a'::int as int DEFAULT NULL ON CONVERSION ERROR); --error
+SELECT CAST('a' as int DEFAULT NULL ON CONVERSION ERROR); --ok
+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 (SELECT NULL) 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);
+-- not supported because the cast from circle to polygon is implemented as a SQL
+-- function, which cannot be error-safe.
+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);
+
+-----safe cast from bytea type to other data types
+SELECT CAST ('\x112233445566778899'::bytea AS int8 DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('\x123456789A'::bytea AS int4 DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('\x123456'::bytea AS int2 DEFAULT NULL ON CONVERSION ERROR);
+
+-----safe cast from bit type to other data types
+SELECT CAST('111111111100001'::bit(100) AS INT DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST ('111111111100001'::bit(100) AS INT8 DEFAULT NULL ON CONVERSION ERROR);
+
+-----safe cast from text type to other data types
+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;
+
+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 betweeen range data types
+SELECT CAST('[1,2]'::int4range AS int4multirange DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('[1,2]'::int8range AS int8multirange DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('[1,2]'::numrange AS nummultirange DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('[-infinity,infinity]'::daterange AS datemultirange DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('[-infinity,infinity]'::tsrange AS tsmultirange DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('[-infinity,infinity]'::tstzrange AS tstzmultirange DEFAULT NULL ON CONVERSION ERROR);
+
+-----safe cast betweeen numeric data types
+CREATE TABLE test_safecast1(
+ col1 float4, col2 float8, col3 numeric, col4 numeric[],
+ col5 int2 default 32767,
+ col6 int4 default 32768,
+ col7 int8 default 4294967296);
+INSERT INTO test_safecast1 VALUES('11.1234', '11.1234', '9223372036854775808', '{11.1234}'::numeric[]);
+INSERT INTO test_safecast1 VALUES('inf', 'inf', 'inf', '{11.1234, 12, inf, NaN}'::numeric[]);
+INSERT INTO test_safecast1 VALUES('-inf', '-inf', '-inf', '{11.1234, 12, -inf, NaN}'::numeric[]);
+INSERT INTO test_safecast1 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 test_safecast1;
+
+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 test_safecast1;
+
+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 test_safecast1;
+
+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 test_safecast1;
+
+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 test_safecast1;
+
+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 test_safecast1;
+
+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 test_safecast1;
+
+-- date/timestamp/interval related safe type cast
+CREATE TABLE test_safecast2(
+ col0 date, col1 timestamp, col2 timestamptz,
+ col3 interval, col4 time, col5 timetz);
+INSERT INTO test_safecast2 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 test_safecast2;
+
+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 test_safecast2;
+
+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 test_safecast2;
+
+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 test_safecast2;
+
+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) IS NOT NULL 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 test_safecast2;
+
+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 test_safecast2;
+
+CREATE TABLE test_safecast3(col0 jsonb);
+INSERT INTO test_safecast3(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_safecast3;
+
+-- 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 test_safecast1,
+ CAST(ARRAY[['1'], ['three'],['a']] AS int[] DEFAULT '{1,2}' ON CONVERSION ERROR) as cast1,
+ CAST(ARRAY[['1', '2'], ['three', 'a']] AS date[] DEFAULT NULL ON CONVERSION ERROR) as cast2,
+ CAST(ARRAY['three'] AS INT[] DEFAULT '{1,2}' ON CONVERSION ERROR) as cast3;
+\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;
+
+--error, default expression is mutable
+CREATE INDEX cast_error_idx ON test_safecast3((CAST(col0 as int DEFAULT random(min=>1, max=>1) ON CONVERSION ERROR)));
+CREATE INDEX cast_error_idx ON test_safecast3((CAST(col0 as xid DEFAULT NULL ON CONVERSION ERROR)));
+CREATE INDEX test_safecast3_idx ON test_safecast3((CAST(col0 as int DEFAULT NULL ON CONVERSION ERROR))); --ok
+SELECT pg_get_indexdef('test_safecast3_idx'::regclass);
+
+DROP TABLE test_safecast;
+DROP TABLE test_safecast1;
+DROP TABLE test_safecast2;
+DROP TABLE test_safecast3;
+DROP TABLE tcast;
+DROP FUNCTION ret_int8;
+DROP FUNCTION ret_setint;
+DROP TYPE comp_domain_with_typmod;
+DROP TYPE comp2;
+DROP DOMAIN d_varchar;
+DROP DOMAIN d_int42;
+DROP DOMAIN d_char3_not_null;
+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 57a8f0366a5..9bbb1e560c7 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2668,6 +2668,9 @@ STRLEN
SV
SYNCHRONIZATION_BARRIER
SYSTEM_INFO
+SafeTypeCast
+SafeTypeCastExpr
+SafeTypeCastState
SampleScan
SampleScanGetSampleSize_function
SampleScanState
--
2.34.1
[text/x-patch] v12-0001-error-safe-for-casting-bytea-to-other-types-per-pg_cast.patch (2.1K, 21-v12-0001-error-safe-for-casting-bytea-to-other-types-per-pg_cast.patch)
download | inline diff:
From 5875ac4fdbc6393286aeabaa293a192524400eeb Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Sat, 22 Nov 2025 00:08:00 +0800
Subject: [PATCH v12 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 | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/backend/utils/adt/bytea.c b/src/backend/utils/adt/bytea.c
index 6e7b914c563..f43ad6b4aff 100644
--- a/src/backend/utils/adt/bytea.c
+++ b/src/backend/utils/adt/bytea.c
@@ -1027,7 +1027,7 @@ bytea_int2(PG_FUNCTION_ARGS)
/* Check that the byte array is not too long */
if (len > sizeof(result))
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range"));
@@ -1052,7 +1052,7 @@ bytea_int4(PG_FUNCTION_ARGS)
/* Check that the byte array is not too long */
if (len > sizeof(result))
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range"));
@@ -1077,7 +1077,7 @@ bytea_int8(PG_FUNCTION_ARGS)
/* Check that the byte array is not too long */
if (len > sizeof(result))
- ereport(ERROR,
+ ereturn(fcinfo->context, (Datum) 0,
errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("bigint out of range"));
--
2.34.1
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: <CACJufxH5OSeY0-qirksn8S2FUycxON-O=iwc0-Nne1MTAguGhQ@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