v7-0020-CAST-expr-AS-newtype-DEFAULT-ON-ERROR.patch
text/x-patch
Filename: v7-0020-CAST-expr-AS-newtype-DEFAULT-ON-ERROR.patch
Type: text/x-patch
Part: 1
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v7-0020
Subject: CAST(expr AS newtype DEFAULT ON ERROR)
| File | + | − |
|---|---|---|
| contrib/pg_stat_statements/expected/select.out | 22 | 1 |
| contrib/pg_stat_statements/sql/select.sql | 5 | 0 |
| doc/src/sgml/catalogs.sgml | 12 | 0 |
| doc/src/sgml/syntax.sgml | 16 | 0 |
| src/backend/catalog/pg_cast.c | 1 | 0 |
| src/backend/executor/execExpr.c | 75 | 1 |
| src/backend/executor/execExprInterp.c | 37 | 0 |
| src/backend/jit/llvm/llvmjit_expr.c | 48 | 0 |
| src/backend/nodes/nodeFuncs.c | 67 | 0 |
| src/backend/optimizer/util/clauses.c | 24 | 0 |
| src/backend/parser/gram.y | 22 | 2 |
| src/backend/parser/parse_agg.c | 9 | 0 |
| src/backend/parser/parse_expr.c | 375 | 8 |
| src/backend/parser/parse_func.c | 3 | 0 |
| src/backend/parser/parse_target.c | 14 | 0 |
| src/backend/utils/adt/arrayfuncs.c | 13 | 0 |
| src/backend/utils/adt/ruleutils.c | 16 | 0 |
| src/include/catalog/pg_cast.dat | 165 | 165 |
| src/include/catalog/pg_cast.h | 4 | 0 |
| src/include/executor/execExpr.h | 8 | 0 |
| src/include/nodes/execnodes.h | 30 | 0 |
| src/include/nodes/parsenodes.h | 6 | 0 |
| src/include/nodes/primnodes.h | 33 | 0 |
| src/include/parser/parse_node.h | 2 | 0 |
| src/test/regress/expected/cast.out | 681 | 0 |
| src/test/regress/expected/create_cast.out | 5 | 0 |
| src/test/regress/expected/opr_sanity.out | 12 | 12 |
| src/test/regress/parallel_schedule | 1 | 1 |
| src/test/regress/sql/cast.sql | 301 | 0 |
| src/test/regress/sql/create_cast.sql | 1 | 0 |
| src/tools/pgindent/typedefs.list | 3 | 0 |
From 3e350ca387117f05388e340e048c14a95ce724e1 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Fri, 10 Oct 2025 19:06:14 +0800
Subject: [PATCH v7 20/20] CAST(expr AS newtype DEFAULT ON ERROR)
Now that the type coercion node is error-safe, we also need to ensure that when
a coercion fails, it falls back to evaluating the default node.
draft doc also added.
We cannot simply prohibit user-defined functions in pg_cast for safe cast
evaluation because CREATE CAST can also utilize built-in functions. So, to
completely disallow custom casts created via CREATE CAST used in safe cast
evaluation, a new field in pg_cast would unfortunately be necessary.
demo:
SELECT CAST('1' AS date DEFAULT '2011-01-01' ON ERROR),
CAST('{234,def,567}'::text[] AS integer[] DEFAULT '{-1011}' ON ERROR);
date | int4
------------+---------
2011-01-01 | {-1011}
[0]: https://git.postgresql.org/cgit/postgresql.git/commit/?id=aaaf9449ec6be62cb0d30ed3588dc384f56274b
discussion: https://postgr.es/m/CADkLM=fv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ@mail.gmail.com
---
.../pg_stat_statements/expected/select.out | 23 +-
contrib/pg_stat_statements/sql/select.sql | 5 +
doc/src/sgml/catalogs.sgml | 12 +
doc/src/sgml/syntax.sgml | 16 +
src/backend/catalog/pg_cast.c | 1 +
src/backend/executor/execExpr.c | 76 +-
src/backend/executor/execExprInterp.c | 37 +
src/backend/jit/llvm/llvmjit_expr.c | 48 ++
src/backend/nodes/nodeFuncs.c | 67 ++
src/backend/optimizer/util/clauses.c | 24 +
src/backend/parser/gram.y | 24 +-
src/backend/parser/parse_agg.c | 9 +
src/backend/parser/parse_expr.c | 383 +++++++++-
src/backend/parser/parse_func.c | 3 +
src/backend/parser/parse_target.c | 14 +
src/backend/utils/adt/arrayfuncs.c | 13 +
src/backend/utils/adt/ruleutils.c | 16 +
src/include/catalog/pg_cast.dat | 330 ++++-----
src/include/catalog/pg_cast.h | 4 +
src/include/executor/execExpr.h | 8 +
src/include/nodes/execnodes.h | 30 +
src/include/nodes/parsenodes.h | 6 +
src/include/nodes/primnodes.h | 33 +
src/include/parser/parse_node.h | 2 +
src/test/regress/expected/cast.out | 681 ++++++++++++++++++
src/test/regress/expected/create_cast.out | 5 +
src/test/regress/expected/opr_sanity.out | 24 +-
src/test/regress/parallel_schedule | 2 +-
src/test/regress/sql/cast.sql | 301 ++++++++
src/test/regress/sql/create_cast.sql | 1 +
src/tools/pgindent/typedefs.list | 3 +
31 files changed, 2011 insertions(+), 190 deletions(-)
create mode 100644 src/test/regress/expected/cast.out
create mode 100644 src/test/regress/sql/cast.sql
diff --git a/contrib/pg_stat_statements/expected/select.out b/contrib/pg_stat_statements/expected/select.out
index 75c896f3885..6e67997f0a2 100644
--- a/contrib/pg_stat_statements/expected/select.out
+++ b/contrib/pg_stat_statements/expected/select.out
@@ -73,6 +73,25 @@ SELECT 1 AS "int" OFFSET 2 FETCH FIRST 3 ROW ONLY;
-----
(0 rows)
+--error safe type cast
+SELECT CAST('a' AS int DEFAULT 2 ON CONVERSION ERROR);
+ int4
+------
+ 2
+(1 row)
+
+SELECT CAST('11' AS int DEFAULT 2 ON CONVERSION ERROR);
+ int4
+------
+ 11
+(1 row)
+
+SELECT CAST('12' AS numeric DEFAULT 2 ON CONVERSION ERROR);
+ numeric
+---------
+ 12
+(1 row)
+
-- DISTINCT and ORDER BY patterns
-- Try some query permutations which once produced identical query IDs
SELECT DISTINCT 1 AS "int";
@@ -222,6 +241,8 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
2 | 2 | SELECT $1 AS "int" ORDER BY 1
1 | 2 | SELECT $1 AS i UNION SELECT $2 ORDER BY i
1 | 1 | SELECT $1 || $2
+ 2 | 2 | SELECT CAST($1 AS int DEFAULT $2 ON CONVERSION ERROR)
+ 1 | 1 | SELECT CAST($1 AS numeric DEFAULT $2 ON CONVERSION ERROR)
2 | 2 | SELECT DISTINCT $1 AS "int"
0 | 0 | SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"
1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
@@ -230,7 +251,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | ) +
| | SELECT f FROM t ORDER BY f
1 | 1 | select $1::jsonb ? $2
-(17 rows)
+(19 rows)
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
diff --git a/contrib/pg_stat_statements/sql/select.sql b/contrib/pg_stat_statements/sql/select.sql
index 11662cde08c..7ee8160fd84 100644
--- a/contrib/pg_stat_statements/sql/select.sql
+++ b/contrib/pg_stat_statements/sql/select.sql
@@ -25,6 +25,11 @@ SELECT 1 AS "int" LIMIT 3 OFFSET 3;
SELECT 1 AS "int" OFFSET 1 FETCH FIRST 2 ROW ONLY;
SELECT 1 AS "int" OFFSET 2 FETCH FIRST 3 ROW ONLY;
+--error safe type cast
+SELECT CAST('a' AS int DEFAULT 2 ON CONVERSION ERROR);
+SELECT CAST('11' AS int DEFAULT 2 ON CONVERSION ERROR);
+SELECT CAST('12' AS numeric DEFAULT 2 ON CONVERSION ERROR);
+
-- DISTINCT and ORDER BY patterns
-- Try some query permutations which once produced identical query IDs
SELECT DISTINCT 1 AS "int";
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 9b3aae8603b..957041add7b 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1849,6 +1849,18 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
<literal>b</literal> means that the types are binary-coercible, thus no conversion is required.
</para></entry>
</row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>casterrorsafe</structfield> <type>bool</type>
+ </para>
+ <para>
+ This indicates whether the <structfield>castfunc</structfield> function is error safe.
+ If the <structfield>castfunc</structfield> function is error safe, it can be used in
+ For further details see <xref linkend="sql-syntax-type-casts"/>.
+ </para></entry>
+ </row>
+
</tbody>
</tgroup>
</table>
diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml
index 237d7306fe8..9acb7d69645 100644
--- a/doc/src/sgml/syntax.sgml
+++ b/doc/src/sgml/syntax.sgml
@@ -2106,6 +2106,22 @@ CAST ( <replaceable>expression</replaceable> AS <replaceable>type</replaceable>
The <literal>CAST</literal> syntax conforms to SQL; the syntax with
<literal>::</literal> is historical <productname>PostgreSQL</productname>
usage.
+ It can also be written as:
+<synopsis>
+CAST ( <replaceable>expression</replaceable> AS <replaceable>type</replaceable> ERROR ON CONVERSION ERROR )
+</synopsis>
+ </para>
+
+ <para>
+Type cast can also be error safe.
+<synopsis>
+CAST ( <replaceable>expression</replaceable> AS <replaceable>type</replaceable> DEFAULT <replaceable>defexpression</replaceable> ON CONVERSION ERROR )
+</synopsis>
+ For example, the following query will evaluate the default expression and return 42.
+ TODO: more explanation
+<programlisting>
+SELECT CAST('error' AS integer DEFAULT 42 ON CONVERSION ERROR);
+</programlisting>
</para>
<para>
diff --git a/src/backend/catalog/pg_cast.c b/src/backend/catalog/pg_cast.c
index 1773c9c5491..6fe65d24d31 100644
--- a/src/backend/catalog/pg_cast.c
+++ b/src/backend/catalog/pg_cast.c
@@ -84,6 +84,7 @@ CastCreate(Oid sourcetypeid, Oid targettypeid,
values[Anum_pg_cast_castfunc - 1] = ObjectIdGetDatum(funcid);
values[Anum_pg_cast_castcontext - 1] = CharGetDatum(castcontext);
values[Anum_pg_cast_castmethod - 1] = CharGetDatum(castmethod);
+ values[Anum_pg_cast_casterrorsafe - 1] = BoolGetDatum(false);
tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index b302be36f73..88a647e44fa 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -99,6 +99,9 @@ static void ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
static void ExecInitJsonExpr(JsonExpr *jsexpr, ExprState *state,
Datum *resv, bool *resnull,
ExprEvalStep *scratch);
+static void ExecInitSafeTypeCastExpr(SafeTypeCastExpr *stcexpr, ExprState *state,
+ Datum *resv, bool *resnull,
+ ExprEvalStep *scratch);
static void ExecInitJsonCoercion(ExprState *state, JsonReturning *returning,
ErrorSaveContext *escontext, bool omit_quotes,
bool exists_coerce,
@@ -1742,6 +1745,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
elemstate->innermost_caseval = (Datum *) palloc(sizeof(Datum));
elemstate->innermost_casenull = (bool *) palloc(sizeof(bool));
+ elemstate->escontext = state->escontext;
ExecInitExprRec(acoerce->elemexpr, elemstate,
&elemstate->resvalue, &elemstate->resnull);
@@ -2218,6 +2222,14 @@ ExecInitExprRec(Expr *node, ExprState *state,
break;
}
+ case T_SafeTypeCastExpr:
+ {
+ SafeTypeCastExpr *stcexpr = castNode(SafeTypeCastExpr, node);
+
+ ExecInitSafeTypeCastExpr(stcexpr, state, resv, resnull, &scratch);
+ break;
+ }
+
case T_CoalesceExpr:
{
CoalesceExpr *coalesce = (CoalesceExpr *) node;
@@ -2784,7 +2796,7 @@ ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
/* Initialize function call parameter structure too */
InitFunctionCallInfoData(*fcinfo, flinfo,
- nargs, inputcollid, NULL, NULL);
+ nargs, inputcollid, (Node *) state->escontext, NULL);
/* Keep extra copies of this info to save an indirection at runtime */
scratch->d.func.fn_addr = flinfo->fn_addr;
@@ -4782,6 +4794,68 @@ ExecBuildParamSetEqual(TupleDesc desc,
return state;
}
+/*
+ * Push steps to evaluate a SafeTypeCastExpr and its various subsidiary
+ * expressions.
+ */
+static void
+ExecInitSafeTypeCastExpr(SafeTypeCastExpr *stcexpr , ExprState *state,
+ Datum *resv, bool *resnull,
+ ExprEvalStep *scratch)
+{
+ /*
+ * If coercion to the target type fails, fallback to the DEFAULT expression
+ * specified in the ON CONVERSION ERROR clause.
+ */
+ if (stcexpr->cast_expr == NULL)
+ {
+ ExecInitExprRec((Expr *) stcexpr->default_expr,
+ state, resv, resnull);
+ return;
+ }
+ else
+ {
+ ExprEvalStep *as;
+ SafeTypeCastState *stcstate;
+ ErrorSaveContext *saved_escontext;
+ int jumps_to_end;
+
+ stcstate = palloc0(sizeof(SafeTypeCastState));
+ stcstate->stcexpr = stcexpr;
+ stcstate->escontext.type = T_ErrorSaveContext;
+ state->escontext = &stcstate->escontext;
+
+ /* evaluate argument expression into step's result area */
+ ExecInitExprRec((Expr *) stcexpr->cast_expr,
+ state, resv, resnull);
+
+ scratch->opcode = EEOP_SAFETYPE_CAST;
+ scratch->d.stcexpr.stcstate = stcstate;
+ ExprEvalPushStep(state, scratch);
+ stcstate->jump_error = state->steps_len;
+
+ /* JUMP to end if false, that is, skip the ON ERROR expression. */
+ jumps_to_end = state->steps_len;
+ scratch->opcode = EEOP_JUMP_IF_NOT_TRUE;
+ scratch->resvalue = &stcstate->error.value;
+ scratch->resnull = &stcstate->error.isnull;
+ scratch->d.jump.jumpdone = -1; /* set below */
+ ExprEvalPushStep(state, scratch);
+
+ /* Steps to evaluate the ON ERROR expression */
+ saved_escontext = state->escontext;
+ state->escontext = NULL;
+ ExecInitExprRec((Expr *) stcstate->stcexpr->default_expr,
+ state, resv, resnull);
+ state->escontext = saved_escontext;
+
+ as = &state->steps[jumps_to_end];
+ as->d.jump.jumpdone = state->steps_len;
+
+ stcstate->jump_end = state->steps_len;
+ }
+}
+
/*
* Push steps to evaluate a JsonExpr and its various subsidiary expressions.
*/
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 67f4e00eac4..49da2905810 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -568,6 +568,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_XMLEXPR,
&&CASE_EEOP_JSON_CONSTRUCTOR,
&&CASE_EEOP_IS_JSON,
+ &&CASE_EEOP_SAFETYPE_CAST,
&&CASE_EEOP_JSONEXPR_PATH,
&&CASE_EEOP_JSONEXPR_COERCION,
&&CASE_EEOP_JSONEXPR_COERCION_FINISH,
@@ -1926,6 +1927,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
+ EEO_CASE(EEOP_SAFETYPE_CAST)
+ {
+ /* too complex for an inline implementation */
+ EEO_JUMP(ExecEvalSafeTypeCast(state, op));
+ }
+
EEO_CASE(EEOP_JSONEXPR_PATH)
{
/* too complex for an inline implementation */
@@ -3644,6 +3651,12 @@ ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
econtext,
op->d.arraycoerce.resultelemtype,
op->d.arraycoerce.amstate);
+
+ if (SOFT_ERROR_OCCURRED(op->d.arraycoerce.elemexprstate->escontext))
+ {
+ *op->resvalue = (Datum) 0;
+ *op->resnull = true;
+ }
}
/*
@@ -5183,6 +5196,30 @@ GetJsonBehaviorValueString(JsonBehavior *behavior)
return pstrdup(behavior_names[behavior->btype]);
}
+int
+ExecEvalSafeTypeCast(ExprState *state, ExprEvalStep *op)
+{
+ SafeTypeCastState *stcstate = op->d.stcexpr.stcstate;
+
+ if (SOFT_ERROR_OCCURRED(&stcstate->escontext))
+ {
+ *op->resvalue = (Datum) 0;
+ *op->resnull = true;
+
+ stcstate->error.value = BoolGetDatum(true);
+
+ /*
+ * Reset for next use such as for catching errors when coercing a
+ * stcexpr expression.
+ */
+ stcstate->escontext.error_occurred = false;
+ stcstate->escontext.details_wanted = false;
+
+ return stcstate->jump_error;
+ }
+ return stcstate->jump_end;
+}
+
/*
* Checks if an error occurred in ExecEvalJsonCoercion(). If so, this sets
* JsonExprState.error to trigger the ON ERROR handling steps, unless the
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index 712b35df7e5..ebea7c4445f 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -2256,6 +2256,54 @@ llvm_compile_expr(ExprState *state)
LLVMBuildBr(b, opblocks[opno + 1]);
break;
+ case EEOP_SAFETYPE_CAST:
+ {
+ SafeTypeCastState *stcstate = op->d.stcexpr.stcstate;
+ LLVMValueRef v_ret;
+
+ /*
+ * Call ExecEvalSafeTypeCast(). It returns the address of
+ * the step to perform next.
+ */
+ v_ret = build_EvalXFunc(b, mod, "ExecEvalSafeTypeCast",
+ v_state, op, v_econtext);
+
+ /*
+ * Build a switch to map the return value (v_ret above),
+ * which is a runtime value of the step address to perform
+ * next to jump_error
+ */
+ if (stcstate->jump_error >= 0)
+ {
+ LLVMValueRef v_jump_error;
+ LLVMValueRef v_switch;
+ LLVMBasicBlockRef b_done,
+ b_error;
+
+ b_error =
+ l_bb_before_v(opblocks[opno + 1],
+ "op.%d.stcexpr_error", opno);
+ b_done =
+ l_bb_before_v(opblocks[opno + 1],
+ "op.%d.stcexpr_done", opno);
+
+ v_switch = LLVMBuildSwitch(b,
+ v_ret,
+ b_done,
+ 1);
+
+ /* Returned stcstate->jump_error? */
+ v_jump_error = l_int32_const(lc, stcstate->jump_error);
+ LLVMAddCase(v_switch, v_jump_error, b_error);
+
+ /* ON ERROR code */
+ LLVMPositionBuilderAtEnd(b, b_error);
+ LLVMBuildBr(b, opblocks[stcstate->jump_error]);
+
+ LLVMPositionBuilderAtEnd(b, b_done);
+ }
+ LLVMBuildBr(b, opblocks[stcstate->jump_end]);
+ break;
case EEOP_JSONEXPR_PATH:
{
JsonExprState *jsestate = op->d.jsonexpr.jsestate;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index ede838cd40c..533e21120a6 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -206,6 +206,9 @@ exprType(const Node *expr)
case T_RowCompareExpr:
type = BOOLOID;
break;
+ case T_SafeTypeCastExpr:
+ type = ((const SafeTypeCastExpr *) expr)->resulttype;
+ break;
case T_CoalesceExpr:
type = ((const CoalesceExpr *) expr)->coalescetype;
break;
@@ -450,6 +453,8 @@ exprTypmod(const Node *expr)
return typmod;
}
break;
+ case T_SafeTypeCastExpr:
+ return ((const SafeTypeCastExpr *) expr)->resulttypmod;
case T_CoalesceExpr:
{
/*
@@ -965,6 +970,9 @@ exprCollation(const Node *expr)
/* RowCompareExpr's result is boolean ... */
coll = InvalidOid; /* ... so it has no collation */
break;
+ case T_SafeTypeCastExpr:
+ coll = ((const SafeTypeCastExpr *) expr)->resultcollid;
+ break;
case T_CoalesceExpr:
coll = ((const CoalesceExpr *) expr)->coalescecollid;
break;
@@ -1232,6 +1240,9 @@ exprSetCollation(Node *expr, Oid collation)
/* RowCompareExpr's result is boolean ... */
Assert(!OidIsValid(collation)); /* ... so never set a collation */
break;
+ case T_SafeTypeCastExpr:
+ ((SafeTypeCastExpr *) expr)->resultcollid = collation;
+ break;
case T_CoalesceExpr:
((CoalesceExpr *) expr)->coalescecollid = collation;
break;
@@ -1550,6 +1561,15 @@ exprLocation(const Node *expr)
/* just use leftmost argument's location */
loc = exprLocation((Node *) ((const RowCompareExpr *) expr)->largs);
break;
+ case T_SafeTypeCastExpr:
+ {
+ const SafeTypeCastExpr *cast_expr = (const SafeTypeCastExpr *) expr;
+ if (cast_expr->cast_expr)
+ loc = exprLocation(cast_expr->cast_expr);
+ else
+ loc = exprLocation(cast_expr->default_expr);
+ break;
+ }
case T_CoalesceExpr:
/* COALESCE keyword should always be the first thing */
loc = ((const CoalesceExpr *) expr)->location;
@@ -2321,6 +2341,18 @@ expression_tree_walker_impl(Node *node,
return true;
}
break;
+ case T_SafeTypeCastExpr:
+ {
+ SafeTypeCastExpr *scexpr = (SafeTypeCastExpr *) node;
+
+ if (WALK(scexpr->source_expr))
+ return true;
+ if (WALK(scexpr->cast_expr))
+ return true;
+ if (WALK(scexpr->default_expr))
+ return true;
+ }
+ break;
case T_CoalesceExpr:
return WALK(((CoalesceExpr *) node)->args);
case T_MinMaxExpr:
@@ -3330,6 +3362,19 @@ expression_tree_mutator_impl(Node *node,
return (Node *) newnode;
}
break;
+ case T_SafeTypeCastExpr:
+ {
+ SafeTypeCastExpr *scexpr = (SafeTypeCastExpr *) node;
+ SafeTypeCastExpr *newnode;
+
+ FLATCOPY(newnode, scexpr, SafeTypeCastExpr);
+ MUTATE(newnode->source_expr, scexpr->source_expr, Node *);
+ MUTATE(newnode->cast_expr, scexpr->cast_expr, Node *);
+ MUTATE(newnode->default_expr, scexpr->default_expr, Node *);
+
+ return (Node *) newnode;
+ }
+ break;
case T_CoalesceExpr:
{
CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
@@ -4464,6 +4509,28 @@ raw_expression_tree_walker_impl(Node *node,
return true;
}
break;
+ case T_SafeTypeCast:
+ {
+ SafeTypeCast *sc = (SafeTypeCast *) node;
+
+ if (WALK(sc->cast))
+ return true;
+ if (WALK(sc->def_expr))
+ return true;
+ }
+ break;
+ case T_SafeTypeCastExpr:
+ {
+ SafeTypeCastExpr *stc = (SafeTypeCastExpr *) node;
+
+ if (WALK(stc->source_expr))
+ return true;
+ if (WALK(stc->cast_expr))
+ return true;
+ if (WALK(stc->default_expr))
+ return true;
+ }
+ break;
case T_CollateClause:
return WALK(((CollateClause *) node)->arg);
case T_SortBy:
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 3f9ff455ba6..14e0df3bee2 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -2945,6 +2945,30 @@ eval_const_expressions_mutator(Node *node,
copyObject(jve->format));
}
+ case T_SafeTypeCastExpr:
+ {
+ SafeTypeCastExpr *stc = (SafeTypeCastExpr *) node;
+ SafeTypeCastExpr *newexpr;
+ Node *source_expr = stc->source_expr;
+ Node *default_expr = stc->default_expr;
+
+ source_expr = eval_const_expressions_mutator(source_expr, context);
+ default_expr = eval_const_expressions_mutator(default_expr, context);
+
+ /*
+ * We must not reduce any recognizably constant subexpressions
+ * in cast_expr here, since we don’t want it to fail
+ * prematurely.
+ */
+ newexpr = makeNode(SafeTypeCastExpr);
+ newexpr->source_expr = source_expr;
+ newexpr->cast_expr = stc->cast_expr;
+ newexpr->default_expr = default_expr;
+ newexpr->resulttype = stc->resulttype;
+ newexpr->resulttypmod = stc->resulttypmod;
+ newexpr->resultcollid = stc->resultcollid;
+ return (Node *) newexpr;
+ }
case T_SubPlan:
case T_AlternativeSubPlan:
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 21caf2d43bf..c62e9ac8edf 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -649,6 +649,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <partboundspec> PartitionBoundSpec
%type <list> hash_partbound
%type <defelt> hash_partbound_elem
+%type <node> cast_on_error_clause_opt
%type <node> json_format_clause
json_format_clause_opt
@@ -15982,8 +15983,20 @@ func_expr_common_subexpr:
{
$$ = makeSQLValueFunction(SVFOP_CURRENT_SCHEMA, -1, @1);
}
- | CAST '(' a_expr AS Typename ')'
- { $$ = makeTypeCast($3, $5, @1); }
+ | CAST '(' a_expr AS Typename cast_on_error_clause_opt ')'
+ {
+ TypeCast *cast = (TypeCast *) makeTypeCast($3, $5, @1);
+ if ($6 == NULL)
+ $$ = (Node *) cast;
+ else
+ {
+ SafeTypeCast *safecast = makeNode(SafeTypeCast);
+ safecast->cast = (Node *) cast;
+ safecast->def_expr = $6;
+
+ $$ = (Node *) safecast;
+ }
+ }
| EXTRACT '(' extract_list ')'
{
$$ = (Node *) makeFuncCall(SystemFuncName("extract"),
@@ -16370,6 +16383,13 @@ func_expr_common_subexpr:
;
+cast_on_error_clause_opt:
+ DEFAULT a_expr ON CONVERSION_P ERROR_P { $$ = $2; }
+ | ERROR_P ON CONVERSION_P ERROR_P { $$ = NULL; }
+ | NULL_P ON CONVERSION_P ERROR_P { $$ = makeNullAConst(-1); }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
/*
* SQL/XML support
*/
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 3254c83cc6c..6573e2a93ea 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -486,6 +486,12 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
err = _("grouping operations are not allowed in check constraints");
break;
+ case EXPR_KIND_CAST_DEFAULT:
+ if (isAgg)
+ err = _("aggregate functions are not allowed in CAST DEFAULT expressions");
+ else
+ err = _("grouping operations are not allowed in CAST DEFAULT expressions");
+ break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
@@ -956,6 +962,9 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
case EXPR_KIND_DOMAIN_CHECK:
err = _("window functions are not allowed in check constraints");
break;
+ case EXPR_KIND_CAST_DEFAULT:
+ err = _("window functions are not allowed in CAST DEFAULT expressions");
+ break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
err = _("window functions are not allowed in DEFAULT expressions");
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 12119f147fc..ac60b74dceb 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -17,6 +17,8 @@
#include "access/htup_details.h"
#include "catalog/pg_aggregate.h"
+#include "catalog/pg_cast.h"
+#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
@@ -37,6 +39,7 @@
#include "utils/date.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
+#include "utils/syscache.h"
#include "utils/timestamp.h"
#include "utils/xml.h"
@@ -60,7 +63,8 @@ static Node *transformMultiAssignRef(ParseState *pstate, MultiAssignRef *maref);
static Node *transformCaseExpr(ParseState *pstate, CaseExpr *c);
static Node *transformSubLink(ParseState *pstate, SubLink *sublink);
static Node *transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
- Oid array_type, Oid element_type, int32 typmod);
+ Oid array_type, Oid element_type, int32 typmod,
+ bool *can_coerce);
static Node *transformRowExpr(ParseState *pstate, RowExpr *r, bool allowDefault);
static Node *transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c);
static Node *transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m);
@@ -76,6 +80,7 @@ static Node *transformWholeRowRef(ParseState *pstate,
int sublevels_up, int location);
static Node *transformIndirection(ParseState *pstate, A_Indirection *ind);
static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
+static Node *transformTypeSafeCast(ParseState *pstate, SafeTypeCast *tc);
static Node *transformCollateClause(ParseState *pstate, CollateClause *c);
static Node *transformJsonObjectConstructor(ParseState *pstate,
JsonObjectConstructor *ctor);
@@ -107,6 +112,8 @@ static Expr *make_distinct_op(ParseState *pstate, List *opname,
Node *ltree, Node *rtree, int location);
static Node *make_nulltest_from_distinct(ParseState *pstate,
A_Expr *distincta, Node *arg);
+static bool CoerceUnknownConstSafe(ParseState *pstate, Node *node,
+ Oid targetType, int32 targetTypeMod);
/*
@@ -164,13 +171,17 @@ transformExprRecurse(ParseState *pstate, Node *expr)
case T_A_ArrayExpr:
result = transformArrayExpr(pstate, (A_ArrayExpr *) expr,
- InvalidOid, InvalidOid, -1);
+ InvalidOid, InvalidOid, -1, NULL);
break;
case T_TypeCast:
result = transformTypeCast(pstate, (TypeCast *) expr);
break;
+ case T_SafeTypeCast:
+ result = transformTypeSafeCast(pstate, (SafeTypeCast *) expr);
+ break;
+
case T_CollateClause:
result = transformCollateClause(pstate, (CollateClause *) expr);
break;
@@ -576,6 +587,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
case EXPR_KIND_COPY_WHERE:
case EXPR_KIND_GENERATED_COLUMN:
case EXPR_KIND_CYCLE_MARK:
+ case EXPR_KIND_CAST_DEFAULT:
/* okay */
break;
@@ -1824,6 +1836,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
case EXPR_KIND_DOMAIN_CHECK:
err = _("cannot use subquery in check constraint");
break;
+ case EXPR_KIND_CAST_DEFAULT:
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
err = _("cannot use subquery in DEFAULT expression");
@@ -2005,16 +2018,73 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
return result;
}
+/*
+ * Return true if successfuly coerced a Unknown Const to targetType
+*/
+static bool
+CoerceUnknownConstSafe(ParseState *pstate, Node *node,
+ Oid targetType, int32 targetTypeMod)
+{
+ Oid baseTypeId;
+ int32 baseTypeMod;
+ int32 inputTypeMod;
+ Type baseType;
+ char *string;
+ Datum datum;
+ bool converted;
+ Const *con;
+
+ Assert(IsA(node, Const));
+ Assert(exprType(node) == UNKNOWNOID);
+
+ con = (Const *) node;
+ baseTypeMod = targetTypeMod;
+ baseTypeId = getBaseTypeAndTypmod(targetType, &baseTypeMod);
+
+ if (baseTypeId == INTERVALOID)
+ inputTypeMod = baseTypeMod;
+ else
+ inputTypeMod = -1;
+
+ baseType = typeidType(baseTypeId);
+
+ /*
+ * We assume here that UNKNOWN's internal representation is the same as
+ * CSTRING.
+ */
+ if (!con->constisnull)
+ string = DatumGetCString(con->constvalue);
+ else
+ string = NULL;
+
+ converted = stringTypeDatumSafe(baseType,
+ string,
+ inputTypeMod,
+ &datum);
+
+ ReleaseSysCache(baseType);
+
+ return converted;
+}
+
+
/*
* transformArrayExpr
*
* If the caller specifies the target type, the resulting array will
* be of exactly that type. Otherwise we try to infer a common type
* for the elements using select_common_type().
+ *
+ * Most of the time, the caller will pass can_coerce as NULL.
+ * can_coerce is not NULL only when performing parse analysis for expressions
+ * like CAST(DEFAULT ... ON CONVERSION ERROR).
+ * When can_coerce is not NULL, it should be assumed to default to true. If
+ * coerce array elements to the target type fails, can_coerce will be set to
+ * false.
*/
static Node *
transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
- Oid array_type, Oid element_type, int32 typmod)
+ Oid array_type, Oid element_type, int32 typmod, bool *can_coerce)
{
ArrayExpr *newa = makeNode(ArrayExpr);
List *newelems = NIL;
@@ -2022,6 +2092,7 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
ListCell *element;
Oid coerce_type;
bool coerce_hard;
+ bool expr_is_const = false;
/*
* Transform the element expressions
@@ -2045,9 +2116,10 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
(A_ArrayExpr *) e,
array_type,
element_type,
- typmod);
+ typmod,
+ can_coerce);
/* we certainly have an array here */
- Assert(array_type == InvalidOid || array_type == exprType(newe));
+ Assert(can_coerce || array_type == InvalidOid || array_type == exprType(newe));
newa->multidims = true;
}
else
@@ -2088,6 +2160,9 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
}
else
{
+ /* We obviously know the target type when performing type-safe cast */
+ Assert(can_coerce == NULL);
+
/* Can't handle an empty array without a target type */
if (newelems == NIL)
ereport(ERROR,
@@ -2140,7 +2215,36 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
Node *e = (Node *) lfirst(element);
Node *newe;
- if (coerce_hard)
+ /*
+ * For error-safe type casting, we need to safely check whether the
+ * UNKNOWN constant can be coerced to the target type. If it cannot, set
+ * can_coerce to false and coerce source element to TEXT data type.
+ * see coerce_type function also.
+ */
+ if (can_coerce != NULL && IsA(e, Const))
+ {
+ if (exprType(e) == UNKNOWNOID)
+ {
+ if (!CoerceUnknownConstSafe(pstate, e, coerce_type, typmod))
+ {
+ *can_coerce = false;
+
+ /* see resolveTargetListUnknowns also */
+ e = coerce_to_target_type(pstate, e,
+ UNKNOWNOID,
+ TEXTOID,
+ -1,
+ COERCION_IMPLICIT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ }
+ }
+ expr_is_const = true;
+ }
+
+ if (can_coerce != NULL && (!*can_coerce))
+ newe = e;
+ else if (coerce_hard)
{
newe = coerce_to_target_type(pstate, e,
exprType(e),
@@ -2149,13 +2253,51 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
COERCION_EXPLICIT,
COERCE_EXPLICIT_CAST,
-1);
- if (newe == NULL)
+ if (newe == NULL && can_coerce == NULL)
ereport(ERROR,
(errcode(ERRCODE_CANNOT_COERCE),
errmsg("cannot cast type %s to %s",
format_type_be(exprType(e)),
format_type_be(coerce_type)),
parser_errposition(pstate, exprLocation(e))));
+
+ if (can_coerce != NULL)
+ {
+ if (newe == NULL)
+ {
+ newe = e;
+ *can_coerce = false;
+ }
+ else if (expr_is_const && !IsA(newe, Const))
+ {
+ /*
+ * Use evaluate_expr_safe to pre-evaluate simple constant
+ * cast expressions early, in a way that tolter errors .
+ *
+ * Rationale:
+ * 1. When deparsing safe cast expressions (or in other
+ * cases), eval_const_expressions might be invoked, but
+ * it cannot handle errors gracefully.
+ * 2. If the cast expression involves only simple constants,
+ * we can safely evaluate it ahead of time. If the
+ * evaluation fails, it indicates that such a cast is not
+ * possible, and we can then fall back to the CAST
+ * DEFAULT expression.
+ */
+ Expr *newe_copy = (Expr *) copyObject(newe);
+
+ newe = (Node *) evaluate_expr_safe(newe_copy,
+ coerce_type,
+ typmod,
+ exprCollation(newe));
+ if (newe == NULL)
+ {
+ newe = e;
+ *can_coerce = false;
+ }
+ }
+ expr_is_const = false;
+ }
}
else
newe = coerce_to_common_type(pstate, e,
@@ -2743,7 +2885,8 @@ transformTypeCast(ParseState *pstate, TypeCast *tc)
(A_ArrayExpr *) arg,
targetBaseType,
elementType,
- targetBaseTypmod);
+ targetBaseTypmod,
+ NULL);
}
else
expr = transformExprRecurse(pstate, arg);
@@ -2780,6 +2923,228 @@ transformTypeCast(ParseState *pstate, TypeCast *tc)
return result;
}
+/*
+ * Handle an explicit CAST(... DEFAULT ... ON CONVERSION ERROR) construct.
+ *
+ * Transform SafeTypeCast node, look up the type name, and apply any necessary
+ * coercion function(s).
+ */
+static Node *
+transformTypeSafeCast(ParseState *pstate, SafeTypeCast *tc)
+{
+ SafeTypeCastExpr *result;
+ TypeCast *tcast = (TypeCast *) tc->cast;
+ Node *def_expr = NULL;
+ Node *cast_expr = NULL;
+ Node *source_expr = NULL;
+ Oid inputType = InvalidOid;
+ Oid targetType;
+ Oid targetBaseType;
+ Oid typeColl;
+ int32 targetTypmod;
+ int32 targetBaseTypmod;
+ bool can_coerce = true;
+ bool expr_is_const = false;
+ int location;
+
+ /* Look up the type name first */
+ typenameTypeIdAndMod(pstate, tcast->typeName, &targetType, &targetTypmod);
+ targetBaseTypmod = targetTypmod;
+
+ typeColl = get_typcollation(targetType);
+ targetBaseType = getBaseTypeAndTypmod(targetType, &targetBaseTypmod);
+
+ /* now looking at cast default expression */
+ def_expr = transformExpr(pstate, tc->def_expr, EXPR_KIND_CAST_DEFAULT);
+
+ def_expr = coerce_to_target_type(pstate, def_expr, exprType(def_expr),
+ targetType, targetTypmod,
+ COERCION_EXPLICIT,
+ COERCE_EXPLICIT_CAST,
+ exprLocation(def_expr));
+
+ assign_expr_collations(pstate, def_expr);
+
+ if (def_expr == NULL)
+ ereport(ERROR,
+ errcode(ERRCODE_CANNOT_COERCE),
+ errmsg("cannot coerce %s expression to type %s",
+ "CAST DEFAULT",
+ format_type_be(targetType)),
+ parser_coercion_errposition(pstate, exprLocation(tc->def_expr), def_expr));
+
+ /*
+ * The collation of DEFAULT expression must match the collation of the
+ * target type.
+ */
+ if (OidIsValid(typeColl))
+ {
+ Oid defColl;
+
+ defColl = exprCollation(def_expr);
+
+ if (OidIsValid(defColl) && typeColl != defColl)
+ ereport(ERROR,
+ errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("the collation of CAST DEFAULT expression conflicts with target type collation"),
+ errdetail("\"%s\" versus \"%s\"",
+ get_collation_name(typeColl),
+ get_collation_name(defColl)),
+ parser_errposition(pstate, exprLocation(def_expr)));
+ }
+
+ /*
+ * If the type cast target type is an array type, we invoke
+ * transformArrayExpr() directly so that we can pass down the type
+ * information. This avoids some cases where transformArrayExpr() might not
+ * infer the correct type. Otherwise, just transform the argument normally.
+ */
+ if (IsA(tcast->arg, A_ArrayExpr))
+ {
+ Oid elementType;
+
+ /*
+ * If target is a domain over array, work with the base array type
+ * here. Below, we'll cast the array type to the domain. In the
+ * usual case that the target is not a domain, the remaining steps
+ * will be a no-op.
+ */
+ elementType = get_element_type(targetBaseType);
+
+ if (OidIsValid(elementType))
+ {
+ source_expr = transformArrayExpr(pstate,
+ (A_ArrayExpr *) tcast->arg,
+ targetBaseType,
+ elementType,
+ targetBaseTypmod,
+ &can_coerce);
+ }
+ else
+ source_expr = transformExprRecurse(pstate, tcast->arg);
+ }
+ else
+ source_expr = transformExprRecurse(pstate, tcast->arg);
+
+ inputType = exprType(source_expr);
+ if (inputType == InvalidOid)
+ return (Node *) NULL; /* return NULL if NULL input */
+
+ if (can_coerce && IsA(source_expr, Const))
+ {
+ if (exprType(source_expr) == UNKNOWNOID)
+ can_coerce = CoerceUnknownConstSafe(pstate,
+ source_expr,
+ targetType,
+ targetTypmod);
+ expr_is_const = true;
+ }
+
+ /*
+ * Location of the coercion is preferentially the location of CAST symbol,
+ * but if there is none then use the location of the type name (this can
+ * happen in TypeName 'string' syntax, for instance).
+ */
+ location = tcast->location;
+ if (location < 0)
+ location = tcast->typeName->location;
+
+ if (can_coerce)
+ {
+ Node *origexpr;
+
+ cast_expr = coerce_to_target_type(pstate, source_expr, inputType,
+ targetType, targetTypmod,
+ COERCION_EXPLICIT,
+ COERCE_EXPLICIT_CAST,
+ location);
+ origexpr = cast_expr;
+
+ while (cast_expr && IsA(cast_expr, CollateExpr))
+ cast_expr = (Node *) ((CollateExpr *) cast_expr)->arg;
+
+ if (cast_expr != NULL && IsA(cast_expr, FuncExpr))
+ {
+ HeapTuple tuple;
+ ListCell *lc;
+ Node *sexpr;
+ FuncExpr *fexpr = (FuncExpr *) cast_expr;
+
+ lc = list_head(fexpr->args);
+ sexpr = (Node *) lfirst(lc);
+
+ /* Look in pg_cast */
+ tuple = SearchSysCache2(CASTSOURCETARGET,
+ ObjectIdGetDatum(exprType(sexpr)),
+ ObjectIdGetDatum(targetType));
+
+ if (HeapTupleIsValid(tuple))
+ {
+ Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple);
+ if (!castForm->casterrorsafe)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot cast type %s to %s when %s expression is specified in %s",
+ format_type_be(inputType),
+ format_type_be(targetType),
+ "DEFAULT",
+ "CAST ... ON CONVERSION ERROR"),
+ errhint("Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast"),
+ parser_errposition(pstate, exprLocation(source_expr)));
+ }
+ else
+ elog(ERROR, "cache lookup failed for pg_cast entry (%s cast to %s)",
+ format_type_be(inputType),
+ format_type_be(targetType));
+ ReleaseSysCache(tuple);
+
+ if (expr_is_const)
+ {
+ /*
+ * Use evaluate_expr_safe to pre-evaluate simple constant cast
+ * expressions early, in a way that tolter errors .
+ *
+ * Rationale:
+ * 1. When deparsing safe cast expressions (or in other cases),
+ * eval_const_expressions might be invoked, but it cannot
+ * handle errors gracefully.
+ * 2. If the cast expression involves only simple constants, we
+ * can safely evaluate it ahead of time. If the evaluation
+ * fails, it indicates that such a cast is not possible, and
+ * we can then fall back to the CAST DEFAULT expression.
+ */
+ FuncExpr *newexpr = (FuncExpr *) copyObject(cast_expr);
+
+ assign_expr_collations(pstate, (Node *) newexpr);
+
+ cast_expr = (Node *) evaluate_expr_safe((Expr *) newexpr,
+ targetType,
+ targetTypmod,
+ typeColl);
+ }
+ }
+
+ /*
+ * set cast_expr back as is, cast_expr can be NULL after
+ * evaluate_expr_safe
+ */
+ if (cast_expr != NULL)
+ cast_expr = origexpr;
+ }
+
+ Assert(can_coerce || cast_expr == NULL);
+
+ result = makeNode(SafeTypeCastExpr);
+ result->source_expr = source_expr;
+ result->cast_expr = cast_expr;
+ result->default_expr = def_expr;
+ result->resulttype = targetType;
+ result->resulttypmod = targetTypmod;
+ result->resultcollid = typeColl;
+
+ return (Node *) result;
+}
+
/*
* Handle an explicit COLLATE clause.
*
@@ -3193,6 +3558,8 @@ ParseExprKindName(ParseExprKind exprKind)
case EXPR_KIND_CHECK_CONSTRAINT:
case EXPR_KIND_DOMAIN_CHECK:
return "CHECK";
+ case EXPR_KIND_CAST_DEFAULT:
+ return "CAST DEFAULT";
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
return "DEFAULT";
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 778d69c6f3c..a90705b9847 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -2743,6 +2743,9 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location)
case EXPR_KIND_DOMAIN_CHECK:
err = _("set-returning functions are not allowed in check constraints");
break;
+ case EXPR_KIND_CAST_DEFAULT:
+ err = _("set-returning functions are not allowed in CAST DEFAULT expressions");
+ break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
err = _("set-returning functions are not allowed in DEFAULT expressions");
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 905c975d83b..d67e5f8a5b7 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -1822,6 +1822,20 @@ FigureColnameInternal(Node *node, char **name)
}
}
break;
+ case T_SafeTypeCast:
+ strength = FigureColnameInternal(((SafeTypeCast *) node)->cast,
+ name);
+ if (strength <= 1)
+ {
+ TypeCast *node_cast;
+ node_cast = (TypeCast *)((SafeTypeCast *) node)->cast;
+ if (node_cast->typeName != NULL)
+ {
+ *name = strVal(llast(node_cast->typeName->names));
+ return 1;
+ }
+ }
+ break;
case T_CollateClause:
return FigureColnameInternal(((CollateClause *) node)->arg, name);
case T_GroupingFunc:
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index a8951f55b93..31cb36a56e6 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -3289,6 +3289,19 @@ array_map(Datum arrayd,
/* Apply the given expression to source element */
values[i] = ExecEvalExpr(exprstate, econtext, &nulls[i]);
+ if (SOFT_ERROR_OCCURRED(exprstate->escontext))
+ {
+ pfree(values);
+ pfree(nulls);
+ return (Datum) 0;
+ }
+
+ if (SOFT_ERROR_OCCURRED(exprstate->escontext))
+ {
+ pfree(values);
+ pfree(nulls);
+ return (Datum) 0;
+ }
if (nulls[i])
hasnulls = true;
else
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 050eef97a4c..4278e44b4a9 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -10558,6 +10558,22 @@ get_rule_expr(Node *node, deparse_context *context,
}
break;
+ case T_SafeTypeCastExpr:
+ {
+ SafeTypeCastExpr *stcexpr = castNode(SafeTypeCastExpr, node);
+
+ appendStringInfoString(buf, "CAST(");
+ get_rule_expr(stcexpr->source_expr, context, showimplicit);
+
+ appendStringInfo(buf, " AS %s ",
+ format_type_with_typemod(stcexpr->resulttype,
+ stcexpr->resulttypmod));
+
+ appendStringInfoString(buf, "DEFAULT ");
+ get_rule_expr(stcexpr->default_expr, context, showimplicit);
+ appendStringInfoString(buf, " ON CONVERSION ERROR)");
+ }
+ break;
case T_JsonExpr:
{
JsonExpr *jexpr = (JsonExpr *) node;
diff --git a/src/include/catalog/pg_cast.dat b/src/include/catalog/pg_cast.dat
index fbfd669587f..1ef59a982af 100644
--- a/src/include/catalog/pg_cast.dat
+++ b/src/include/catalog/pg_cast.dat
@@ -19,65 +19,65 @@
# int2->int4->int8->numeric->float4->float8, while casts in the
# reverse direction are assignment-only.
{ castsource => 'int8', casttarget => 'int2', castfunc => 'int2(int8)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int8', casttarget => 'int4', castfunc => 'int4(int8)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int8', casttarget => 'float4', castfunc => 'float4(int8)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int8', casttarget => 'float8', castfunc => 'float8(int8)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int8', casttarget => 'numeric', castfunc => 'numeric(int8)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'int8', castfunc => 'int8(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'int4', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'float4', castfunc => 'float4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'float8', castfunc => 'float8(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'numeric', castfunc => 'numeric(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'int8', castfunc => 'int8(int4)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'int2', castfunc => 'int2(int4)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'float4', castfunc => 'float4(int4)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'float8', castfunc => 'float8(int4)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'numeric', castfunc => 'numeric(int4)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'float4', casttarget => 'int8', castfunc => 'int8(float4)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'float4', casttarget => 'int2', castfunc => 'int2(float4)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'float4', casttarget => 'int4', castfunc => 'int4(float4)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'float4', casttarget => 'float8', castfunc => 'float8(float4)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'float4', casttarget => 'numeric',
- castfunc => 'numeric(float4)', castcontext => 'a', castmethod => 'f' },
+ castfunc => 'numeric(float4)', castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'float8', casttarget => 'int8', castfunc => 'int8(float8)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'float8', casttarget => 'int2', castfunc => 'int2(float8)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'float8', casttarget => 'int4', castfunc => 'int4(float8)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'float8', casttarget => 'float4', castfunc => 'float4(float8)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'float8', casttarget => 'numeric',
- castfunc => 'numeric(float8)', castcontext => 'a', castmethod => 'f' },
+ castfunc => 'numeric(float8)', castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'numeric', casttarget => 'int8', castfunc => 'int8(numeric)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'numeric', casttarget => 'int2', castfunc => 'int2(numeric)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'numeric', casttarget => 'int4', castfunc => 'int4(numeric)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'numeric', casttarget => 'float4',
- castfunc => 'float4(numeric)', castcontext => 'i', castmethod => 'f' },
+ castfunc => 'float4(numeric)', castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'numeric', casttarget => 'float8',
- castfunc => 'float8(numeric)', castcontext => 'i', castmethod => 'f' },
+ castfunc => 'float8(numeric)', castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'money', casttarget => 'numeric', castfunc => 'numeric(money)',
castcontext => 'a', castmethod => 'f' },
{ castsource => 'numeric', casttarget => 'money', castfunc => 'money(numeric)',
@@ -89,13 +89,13 @@
# Allow explicit coercions between int4 and bool
{ castsource => 'int4', casttarget => 'bool', castfunc => 'bool(int4)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bool', casttarget => 'int4', castfunc => 'int4(bool)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
# Allow explicit coercions between xid8 and xid
{ castsource => 'xid8', casttarget => 'xid', castfunc => 'xid(xid8)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
# OID category: allow implicit conversion from any integral type (including
# int8, to support OID literals > 2G) to OID, as well as assignment coercion
@@ -106,13 +106,13 @@
# casts from text and varchar to regclass, which exist mainly to support
# legacy forms of nextval() and related functions.
{ castsource => 'int8', casttarget => 'oid', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'oid', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'oid', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'oid', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'oid', casttarget => 'regproc', castfunc => '0',
@@ -120,13 +120,13 @@
{ castsource => 'regproc', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'int8', casttarget => 'regproc', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'regproc', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'regproc', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regproc', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'regproc', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'regproc', casttarget => 'regprocedure', castfunc => '0',
@@ -138,13 +138,13 @@
{ castsource => 'regprocedure', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'int8', casttarget => 'regprocedure', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'regprocedure', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'regprocedure', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regprocedure', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'regprocedure', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'oid', casttarget => 'regoper', castfunc => '0',
@@ -152,13 +152,13 @@
{ castsource => 'regoper', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'int8', casttarget => 'regoper', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'regoper', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'regoper', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regoper', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'regoper', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'regoper', casttarget => 'regoperator', castfunc => '0',
@@ -170,13 +170,13 @@
{ castsource => 'regoperator', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'int8', casttarget => 'regoperator', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'regoperator', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'regoperator', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regoperator', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'regoperator', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'oid', casttarget => 'regclass', castfunc => '0',
@@ -184,13 +184,13 @@
{ castsource => 'regclass', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'int8', casttarget => 'regclass', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'regclass', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'regclass', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regclass', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'regclass', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'oid', casttarget => 'regcollation', castfunc => '0',
@@ -198,13 +198,13 @@
{ castsource => 'regcollation', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'int8', casttarget => 'regcollation', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'regcollation', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'regcollation', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regcollation', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'regcollation', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'oid', casttarget => 'regtype', castfunc => '0',
@@ -212,13 +212,13 @@
{ castsource => 'regtype', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'int8', casttarget => 'regtype', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'regtype', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'regtype', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regtype', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'regtype', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'oid', casttarget => 'regconfig', castfunc => '0',
@@ -226,13 +226,13 @@
{ castsource => 'regconfig', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'int8', casttarget => 'regconfig', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'regconfig', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'regconfig', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regconfig', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'regconfig', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'oid', casttarget => 'regdictionary', castfunc => '0',
@@ -240,31 +240,31 @@
{ castsource => 'regdictionary', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'int8', casttarget => 'regdictionary', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'regdictionary', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'regdictionary', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regdictionary', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'regdictionary', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'text', casttarget => 'regclass', castfunc => 'regclass',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'varchar', casttarget => 'regclass', castfunc => 'regclass',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'oid', casttarget => 'regrole', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regrole', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'int8', casttarget => 'regrole', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'regrole', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'regrole', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regrole', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'regrole', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'oid', casttarget => 'regnamespace', castfunc => '0',
@@ -272,13 +272,13 @@
{ castsource => 'regnamespace', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'int8', casttarget => 'regnamespace', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'regnamespace', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'regnamespace', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regnamespace', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'regnamespace', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'oid', casttarget => 'regdatabase', castfunc => '0',
@@ -286,13 +286,13 @@
{ castsource => 'regdatabase', casttarget => 'oid', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'int8', casttarget => 'regdatabase', castfunc => 'oid',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int2', casttarget => 'regdatabase', castfunc => 'int4(int2)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'regdatabase', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'regdatabase', casttarget => 'int8', castfunc => 'int8(oid)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'regdatabase', casttarget => 'int4', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
@@ -302,57 +302,57 @@
{ castsource => 'text', casttarget => 'varchar', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'bpchar', casttarget => 'text', castfunc => 'text(bpchar)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bpchar', casttarget => 'varchar', castfunc => 'text(bpchar)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'varchar', casttarget => 'text', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'varchar', casttarget => 'bpchar', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'char', casttarget => 'text', castfunc => 'text(char)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'char', casttarget => 'bpchar', castfunc => 'bpchar(char)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'char', casttarget => 'varchar', castfunc => 'text(char)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'name', casttarget => 'text', castfunc => 'text(name)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'name', casttarget => 'bpchar', castfunc => 'bpchar(name)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'name', casttarget => 'varchar', castfunc => 'varchar(name)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'text', casttarget => 'char', castfunc => 'char(text)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bpchar', casttarget => 'char', castfunc => 'char(text)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'varchar', casttarget => 'char', castfunc => 'char(text)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'text', casttarget => 'name', castfunc => 'name(text)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bpchar', casttarget => 'name', castfunc => 'name(bpchar)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'varchar', casttarget => 'name', castfunc => 'name(varchar)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
# Allow explicit coercions between bytea and integer types
{ castsource => 'int2', casttarget => 'bytea', castfunc => 'bytea(int2)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'bytea', castfunc => 'bytea(int4)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int8', casttarget => 'bytea', castfunc => 'bytea(int8)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bytea', casttarget => 'int2', castfunc => 'int2(bytea)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bytea', casttarget => 'int4', castfunc => 'int4(bytea)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bytea', casttarget => 'int8', castfunc => 'int8(bytea)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
# Allow explicit coercions between int4 and "char"
{ castsource => 'char', casttarget => 'int4', castfunc => 'int4(char)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'char', castfunc => 'char(int4)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
# pg_node_tree can be coerced to, but not from, text
{ castsource => 'pg_node_tree', casttarget => 'text', castfunc => '0',
@@ -378,73 +378,73 @@
# Datetime category
{ castsource => 'date', casttarget => 'timestamp',
- castfunc => 'timestamp(date)', castcontext => 'i', castmethod => 'f' },
+ castfunc => 'timestamp(date)', castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'date', casttarget => 'timestamptz',
- castfunc => 'timestamptz(date)', castcontext => 'i', castmethod => 'f' },
+ castfunc => 'timestamptz(date)', castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'time', casttarget => 'interval', castfunc => 'interval(time)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'time', casttarget => 'timetz', castfunc => 'timetz(time)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'timestamp', casttarget => 'date',
- castfunc => 'date(timestamp)', castcontext => 'a', castmethod => 'f' },
+ castfunc => 'date(timestamp)', castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'timestamp', casttarget => 'time',
- castfunc => 'time(timestamp)', castcontext => 'a', castmethod => 'f' },
+ castfunc => 'time(timestamp)', castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'timestamp', casttarget => 'timestamptz',
- castfunc => 'timestamptz(timestamp)', castcontext => 'i', castmethod => 'f' },
+ castfunc => 'timestamptz(timestamp)', castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'timestamptz', casttarget => 'date',
- castfunc => 'date(timestamptz)', castcontext => 'a', castmethod => 'f' },
+ castfunc => 'date(timestamptz)', castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'timestamptz', casttarget => 'time',
- castfunc => 'time(timestamptz)', castcontext => 'a', castmethod => 'f' },
+ castfunc => 'time(timestamptz)', castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'timestamptz', casttarget => 'timestamp',
- castfunc => 'timestamp(timestamptz)', castcontext => 'a', castmethod => 'f' },
+ castfunc => 'timestamp(timestamptz)', castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'timestamptz', casttarget => 'timetz',
- castfunc => 'timetz(timestamptz)', castcontext => 'a', castmethod => 'f' },
+ castfunc => 'timetz(timestamptz)', castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'interval', casttarget => 'time', castfunc => 'time(interval)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'timetz', casttarget => 'time', castfunc => 'time(timetz)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
# Geometric category
{ castsource => 'point', casttarget => 'box', castfunc => 'box(point)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'lseg', casttarget => 'point', castfunc => 'point(lseg)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'path', casttarget => 'polygon', castfunc => 'polygon(path)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'box', casttarget => 'point', castfunc => 'point(box)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'box', casttarget => 'lseg', castfunc => 'lseg(box)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'box', casttarget => 'polygon', castfunc => 'polygon(box)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'box', casttarget => 'circle', castfunc => 'circle(box)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'polygon', casttarget => 'point', castfunc => 'point(polygon)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'polygon', casttarget => 'path', castfunc => 'path',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'polygon', casttarget => 'box', castfunc => 'box(polygon)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'polygon', casttarget => 'circle',
- castfunc => 'circle(polygon)', castcontext => 'e', castmethod => 'f' },
+ castfunc => 'circle(polygon)', castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'circle', casttarget => 'point', castfunc => 'point(circle)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'circle', casttarget => 'box', castfunc => 'box(circle)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'circle', casttarget => 'polygon',
- castfunc => 'polygon(circle)', castcontext => 'e', castmethod => 'f' },
+ castfunc => 'polygon(circle)', castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
# MAC address category
{ castsource => 'macaddr', casttarget => 'macaddr8', castfunc => 'macaddr8',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'macaddr8', casttarget => 'macaddr', castfunc => 'macaddr',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
# INET category
{ castsource => 'cidr', casttarget => 'inet', castfunc => '0',
castcontext => 'i', castmethod => 'b' },
{ castsource => 'inet', casttarget => 'cidr', castfunc => 'cidr',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
# BitString category
{ castsource => 'bit', casttarget => 'varbit', castfunc => '0',
@@ -454,13 +454,13 @@
# Cross-category casts between bit and int4, int8
{ castsource => 'int8', casttarget => 'bit', castfunc => 'bit(int8,int4)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int4', casttarget => 'bit', castfunc => 'bit(int4,int4)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bit', casttarget => 'int8', castfunc => 'int8(bit)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bit', casttarget => 'int4', castfunc => 'int4(bit)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
# Cross-category casts to and from TEXT
# We need entries here only for a few specialized cases where the behavior
@@ -471,68 +471,68 @@
# behavior will ensue when the automatic cast is applied instead of the
# pg_cast entry!
{ castsource => 'cidr', casttarget => 'text', castfunc => 'text(inet)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'inet', casttarget => 'text', castfunc => 'text(inet)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bool', casttarget => 'text', castfunc => 'text(bool)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'xml', casttarget => 'text', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'text', casttarget => 'xml', castfunc => 'xml',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
# Cross-category casts to and from VARCHAR
# We support all the same casts as for TEXT.
{ castsource => 'cidr', casttarget => 'varchar', castfunc => 'text(inet)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'inet', casttarget => 'varchar', castfunc => 'text(inet)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bool', casttarget => 'varchar', castfunc => 'text(bool)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'xml', casttarget => 'varchar', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'varchar', casttarget => 'xml', castfunc => 'xml',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
# Cross-category casts to and from BPCHAR
# We support all the same casts as for TEXT.
{ castsource => 'cidr', casttarget => 'bpchar', castfunc => 'text(inet)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'inet', casttarget => 'bpchar', castfunc => 'text(inet)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bool', casttarget => 'bpchar', castfunc => 'text(bool)',
- castcontext => 'a', castmethod => 'f' },
+ castcontext => 'a', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'xml', casttarget => 'bpchar', castfunc => '0',
castcontext => 'a', castmethod => 'b' },
{ castsource => 'bpchar', casttarget => 'xml', castfunc => 'xml',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
# Length-coercion functions
{ castsource => 'bpchar', casttarget => 'bpchar',
castfunc => 'bpchar(bpchar,int4,bool)', castcontext => 'i',
- castmethod => 'f' },
+ castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'varchar', casttarget => 'varchar',
castfunc => 'varchar(varchar,int4,bool)', castcontext => 'i',
- castmethod => 'f' },
+ castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'time', casttarget => 'time', castfunc => 'time(time,int4)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'timestamp', casttarget => 'timestamp',
castfunc => 'timestamp(timestamp,int4)', castcontext => 'i',
- castmethod => 'f' },
+ castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'timestamptz', casttarget => 'timestamptz',
castfunc => 'timestamptz(timestamptz,int4)', castcontext => 'i',
- castmethod => 'f' },
+ castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'interval', casttarget => 'interval',
castfunc => 'interval(interval,int4)', castcontext => 'i',
- castmethod => 'f' },
+ castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'timetz', casttarget => 'timetz',
- castfunc => 'timetz(timetz,int4)', castcontext => 'i', castmethod => 'f' },
+ castfunc => 'timetz(timetz,int4)', castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'bit', casttarget => 'bit', castfunc => 'bit(bit,int4,bool)',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'varbit', casttarget => 'varbit', castfunc => 'varbit',
- castcontext => 'i', castmethod => 'f' },
+ castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'numeric', casttarget => 'numeric',
- castfunc => 'numeric(numeric,int4)', castcontext => 'i', castmethod => 'f' },
+ castfunc => 'numeric(numeric,int4)', castcontext => 'i', castmethod => 'f', casterrorsafe => 't' },
# json to/from jsonb
{ castsource => 'json', casttarget => 'jsonb', castfunc => '0',
@@ -542,36 +542,36 @@
# jsonb to numeric and bool types
{ castsource => 'jsonb', casttarget => 'bool', castfunc => 'bool(jsonb)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'jsonb', casttarget => 'numeric', castfunc => 'numeric(jsonb)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'jsonb', casttarget => 'int2', castfunc => 'int2(jsonb)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'jsonb', casttarget => 'int4', castfunc => 'int4(jsonb)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'jsonb', casttarget => 'int8', castfunc => 'int8(jsonb)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'jsonb', casttarget => 'float4', castfunc => 'float4(jsonb)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'jsonb', casttarget => 'float8', castfunc => 'float8(jsonb)',
- castcontext => 'e', castmethod => 'f' },
+ castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
# range to multirange
{ castsource => 'int4range', casttarget => 'int4multirange',
castfunc => 'int4multirange(int4range)', castcontext => 'e',
- castmethod => 'f' },
+ castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'int8range', casttarget => 'int8multirange',
castfunc => 'int8multirange(int8range)', castcontext => 'e',
- castmethod => 'f' },
+ castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'numrange', casttarget => 'nummultirange',
castfunc => 'nummultirange(numrange)', castcontext => 'e',
- castmethod => 'f' },
+ castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'daterange', casttarget => 'datemultirange',
castfunc => 'datemultirange(daterange)', castcontext => 'e',
- castmethod => 'f' },
+ castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'tsrange', casttarget => 'tsmultirange',
- castfunc => 'tsmultirange(tsrange)', castcontext => 'e', castmethod => 'f' },
+ castfunc => 'tsmultirange(tsrange)', castcontext => 'e', castmethod => 'f', casterrorsafe => 't' },
{ castsource => 'tstzrange', casttarget => 'tstzmultirange',
castfunc => 'tstzmultirange(tstzrange)', castcontext => 'e',
- castmethod => 'f' },
+ castmethod => 'f', casterrorsafe => 't' },
]
diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.h
index 6a0ca337153..218d81d535a 100644
--- a/src/include/catalog/pg_cast.h
+++ b/src/include/catalog/pg_cast.h
@@ -47,6 +47,10 @@ CATALOG(pg_cast,2605,CastRelationId)
/* cast method */
char castmethod;
+
+ /* cast function error safe */
+ bool casterrorsafe BKI_DEFAULT(f);
+
} FormData_pg_cast;
/* ----------------
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 75366203706..34a884e3196 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -265,6 +265,7 @@ typedef enum ExprEvalOp
EEOP_XMLEXPR,
EEOP_JSON_CONSTRUCTOR,
EEOP_IS_JSON,
+ EEOP_SAFETYPE_CAST,
EEOP_JSONEXPR_PATH,
EEOP_JSONEXPR_COERCION,
EEOP_JSONEXPR_COERCION_FINISH,
@@ -750,6 +751,12 @@ typedef struct ExprEvalStep
JsonIsPredicate *pred; /* original expression node */
} is_json;
+ /* for EEOP_SAFETYPE_CAST */
+ struct
+ {
+ struct SafeTypeCastState *stcstate; /* original expression node */
+ } stcexpr;
+
/* for EEOP_JSONEXPR_PATH */
struct
{
@@ -892,6 +899,7 @@ extern int ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
extern void ExecEvalJsonCoercion(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
+extern int ExecEvalSafeTypeCast(ExprState *state, ExprEvalStep *op);
extern void ExecEvalJsonCoercionFinish(ExprState *state, ExprEvalStep *op);
extern void ExecEvalGroupingFunc(ExprState *state, ExprEvalStep *op);
extern void ExecEvalMergeSupportFunc(ExprState *state, ExprEvalStep *op,
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index a36653c37f9..015cd9f47e7 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1053,6 +1053,36 @@ typedef struct DomainConstraintState
ExprState *check_exprstate; /* check_expr's eval state, or NULL */
} DomainConstraintState;
+typedef struct SafeTypeCastState
+{
+ SafeTypeCastExpr *stcexpr;
+
+ /* Set to true if type cast cause an error. */
+ NullableDatum error;
+
+ /*
+ * Addresses of steps that implement DEFAULT expr ON CONVERSION ERROR for
+ * safe type cast.
+ */
+ int jump_error;
+
+ /*
+ * Address to jump to when skipping all the steps to evaulate the default
+ * expression after performing ExecEvalSafeTypeCast().
+ */
+ int jump_end;
+
+ /*
+ * For error-safe evaluation of coercions. When DEFAULT expr ON CONVERSION
+ * ON ERROR is specified, a pointer to this is passed to ExecInitExprRec()
+ * when initializing the coercion expressions, see ExecInitSafeTypeCastExpr.
+ *
+ * Reset for each evaluation of EEOP_SAFETYPE_CAST.
+ */
+ ErrorSaveContext escontext;
+
+} SafeTypeCastState;
+
/*
* State for JsonExpr evaluation, too big to inline.
*
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index dc09d1a3f03..83549151bfb 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -400,6 +400,12 @@ typedef struct TypeCast
ParseLoc location; /* token location, or -1 if unknown */
} TypeCast;
+typedef struct SafeTypeCast
+{
+ NodeTag type;
+ Node *cast;
+ Node *def_expr; /* default expr */
+} SafeTypeCast;
/*
* CollateClause - a COLLATE expression
*/
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 1b4436f2ff6..70b72ed2f40 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -769,6 +769,39 @@ typedef enum CoercionForm
COERCE_SQL_SYNTAX, /* display with SQL-mandated special syntax */
} CoercionForm;
+/*
+ * SafeTypeCastExpr -
+ * Transformed representation of
+ * CAST(expr AS typename DEFAULT expr2 ON ERROR)
+ * CAST(expr AS typename NULL ON ERROR)
+ */
+typedef struct SafeTypeCastExpr
+{
+ Expr xpr;
+
+ /* transformed expression being casted */
+ Node *source_expr;
+
+ /*
+ * The transformed cast expression; It will NULL if can not cast source type
+ * to target type
+ */
+ Node *cast_expr pg_node_attr(query_jumble_ignore);
+
+ /* Fall back to the default expression if cast evaluation fails */
+ Node *default_expr;
+
+ /* cast result data type */
+ Oid resulttype;
+
+ /* cast result data type typmod (usually -1) */
+ int32 resulttypmod;
+
+ /* cast result data type collation */
+ Oid resultcollid;
+
+} SafeTypeCastExpr;
+
/*
* FuncExpr - expression node for a function call
*
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index f7d07c84542..9f5b32e0360 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -67,6 +67,8 @@ typedef enum ParseExprKind
EXPR_KIND_VALUES_SINGLE, /* single-row VALUES (in INSERT only) */
EXPR_KIND_CHECK_CONSTRAINT, /* CHECK constraint for a table */
EXPR_KIND_DOMAIN_CHECK, /* CHECK constraint for a domain */
+ EXPR_KIND_CAST_DEFAULT, /* default expression in
+ CAST DEFAULT ON CONVERSION ERROR */
EXPR_KIND_COLUMN_DEFAULT, /* default value for a table column */
EXPR_KIND_FUNCTION_DEFAULT, /* default parameter value for function */
EXPR_KIND_INDEX_EXPRESSION, /* index expression */
diff --git a/src/test/regress/expected/cast.out b/src/test/regress/expected/cast.out
new file mode 100644
index 00000000000..6614ba3370c
--- /dev/null
+++ b/src/test/regress/expected/cast.out
@@ -0,0 +1,681 @@
+SET extra_float_digits = 0;
+-- CAST DEFAULT ON CONVERSION ERROR
+VALUES (CAST('error' AS integer ERROR ON CONVERSION ERROR)); --error
+ERROR: invalid input syntax for type integer: "error"
+LINE 1: VALUES (CAST('error' AS integer ERROR ON CONVERSION ERROR));
+ ^
+VALUES (CAST('error' AS integer NULL ON CONVERSION ERROR));
+ column1
+---------
+
+(1 row)
+
+VALUES (CAST('error' AS integer DEFAULT 42 ON CONVERSION ERROR));
+ column1
+---------
+ 42
+(1 row)
+
+SELECT CAST(1 AS date DEFAULT NULL ON CONVERSION ERROR);
+ date
+------
+
+(1 row)
+
+SELECT CAST(1::numeric AS money DEFAULT NULL ON CONVERSION ERROR);
+ERROR: cannot cast type numeric to money when DEFAULT expression is specified in CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(1::numeric AS money DEFAULT NULL ON CONVERSION E...
+ ^
+HINT: Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
+SELECT CAST(1111 AS "char" DEFAULT NULL ON CONVERSION ERROR);
+ char
+------
+
+(1 row)
+
+CREATE OR REPLACE FUNCTION ret_int8() RETURNS bigint AS
+$$
+BEGIN RETURN 2147483648; END;
+$$
+LANGUAGE plpgsql IMMUTABLE;
+SELECT CAST('a' as int DEFAULT ret_int8() ON CONVERSION ERROR); --error
+ERROR: integer out of range
+SELECT CAST('a' as date DEFAULT ret_int8() ON CONVERSION ERROR); --error
+ERROR: cannot coerce CAST DEFAULT expression to type date
+LINE 1: SELECT CAST('a' as date DEFAULT ret_int8() ON CONVERSION ERR...
+ ^
+-- test array coerce
+SELECT CAST(array[11.12] AS int[] DEFAULT NULL ON CONVERSION ERROR);
+ array
+-------
+ {11}
+(1 row)
+
+SELECT CAST(array[11.12] AS date[] DEFAULT NULL ON CONVERSION ERROR);
+ array
+-------
+
+(1 row)
+
+SELECT CAST(array[11111111111111111] AS int[] DEFAULT NULL ON CONVERSION ERROR);
+ array
+-------
+
+(1 row)
+
+SELECT CAST(array[['abc'],[456]] AS int[] DEFAULT NULL ON CONVERSION ERROR);
+ array
+-------
+
+(1 row)
+
+SELECT CAST('{123,abc,456}' AS integer[] DEFAULT '{-789}' ON CONVERSION ERROR);
+ int4
+--------
+ {-789}
+(1 row)
+
+SELECT CAST('{234,def,567}'::text[] AS integer[] DEFAULT '{-1011}' ON CONVERSION ERROR);
+ int4
+---------
+ {-1011}
+(1 row)
+
+SELECT CAST(ARRAY[['1'], ['three'],['a']] AS INTEGER[] DEFAULT '{1,2}' ON CONVERSION ERROR);
+ array
+-------
+ {1,2}
+(1 row)
+
+SELECT CAST(ARRAY[['1', '2'], ['three', 'a']] AS text[] DEFAULT '{21,22}' ON CONVERSION ERROR);
+ array
+-------------------
+ {{1,2},{three,a}}
+(1 row)
+
+-- test valid DEFAULT expression for CAST = ON CONVERSION ERROR
+CREATE OR REPLACE FUNCTION ret_setint() RETURNS SETOF integer AS
+$$
+BEGIN RETURN QUERY EXECUTE 'select 1 union all select 1'; END;
+$$
+LANGUAGE plpgsql IMMUTABLE;
+CREATE TABLE tcast(a text[], b int GENERATED BY DEFAULT AS IDENTITY, c text default '1');
+INSERT INTO tcast VALUES ('{12}'), ('{1,a, b}'), ('{{1,2}, {c,d}}'), ('{13}');
+SELECT CAST('a' as int DEFAULT ret_setint() ON CONVERSION ERROR) FROM tcast; --error
+ERROR: set-returning functions are not allowed in CAST DEFAULT expressions
+LINE 1: SELECT CAST('a' as int DEFAULT ret_setint() ON CONVERSION ER...
+ ^
+SELECT CAST('a' as int DEFAULT sum(1) ON CONVERSION ERROR); --error
+ERROR: aggregate functions are not allowed in CAST DEFAULT expressions
+LINE 1: SELECT CAST('a' as int DEFAULT sum(1) ON CONVERSION ERROR);
+ ^
+SELECT CAST('a' as int DEFAULT sum(1) over() ON CONVERSION ERROR); --error
+ERROR: window functions are not allowed in CAST DEFAULT expressions
+LINE 1: SELECT CAST('a' as int DEFAULT sum(1) over() ON CONVERSION E...
+ ^
+SELECT CAST('a' as int DEFAULT 'b' ON CONVERSION ERROR); --error
+ERROR: invalid input syntax for type integer: "b"
+LINE 1: SELECT CAST('a' as int DEFAULT 'b' ON CONVERSION ERROR);
+ ^
+SELECT CAST(t AS text[] DEFAULT '{21,22, ' || b || '}' ON CONVERSION ERROR) FROM tcast as t;
+ t
+-----------
+ {21,22,1}
+ {21,22,2}
+ {21,22,3}
+ {21,22,4}
+(4 rows)
+
+SELECT CAST(t.a AS int[] DEFAULT '{21,22}'::int[] || b ON CONVERSION ERROR) FROM tcast as t;
+ a
+-----------
+ {12}
+ {21,22,2}
+ {21,22,3}
+ {13}
+(4 rows)
+
+-- test with domain
+CREATE DOMAIN d_int42 as int check (value = 42) NOT NULL;
+CREATE DOMAIN d_char3_not_null as char(3) NOT NULL;
+CREATE DOMAIN d_varchar as varchar(3) NOT NULL;
+CREATE TYPE comp_domain_with_typmod AS (a d_char3_not_null, b int);
+CREATE TYPE comp2 AS (a d_varchar);
+SELECT CAST('(NULL)' AS comp2 DEFAULT '(1232)' ON CONVERSION ERROR); --error
+ERROR: value too long for type character varying(3)
+LINE 1: SELECT CAST('(NULL)' AS comp2 DEFAULT '(1232)' ON CONVERSION...
+ ^
+SELECT CAST('(NULL)' AS comp2 DEFAULT '(123)' ON CONVERSION ERROR); --ok
+ comp2
+-------
+ (123)
+(1 row)
+
+SELECT CAST(11 AS d_int42 DEFAULT 41 ON CONVERSION ERROR); --error
+ERROR: value for domain d_int42 violates check constraint "d_int42_check"
+SELECT CAST(11 AS d_int42 DEFAULT 42 ON CONVERSION ERROR); --ok
+ d_int42
+---------
+ 42
+(1 row)
+
+SELECT CAST(NULL AS d_int42 DEFAULT NULL ON CONVERSION ERROR); --error
+ERROR: domain d_int42 does not allow null values
+SELECT CAST(NULL AS d_int42 DEFAULT 42 ON CONVERSION ERROR); --ok
+ d_int42
+---------
+ 42
+(1 row)
+
+SELECT CAST('(,42)' AS comp_domain_with_typmod DEFAULT NULL ON CONVERSION ERROR);
+ comp_domain_with_typmod
+-------------------------
+
+(1 row)
+
+SELECT CAST('(NULL,42)' AS comp_domain_with_typmod DEFAULT '(1,2)' ON CONVERSION ERROR);
+ comp_domain_with_typmod
+-------------------------
+ ("1 ",2)
+(1 row)
+
+SELECT CAST('(NULL,42)' AS comp_domain_with_typmod DEFAULT '(1234,2)' ON CONVERSION ERROR); --error
+ERROR: value too long for type character(3)
+LINE 1: ...ST('(NULL,42)' AS comp_domain_with_typmod DEFAULT '(1234,2)'...
+ ^
+-----safe cast with geometry data type
+SELECT CAST('(1,2)'::point AS box DEFAULT NULL ON CONVERSION ERROR);
+ box
+-------------
+ (1,2),(1,2)
+(1 row)
+
+SELECT CAST('[(NaN,1),(NaN,infinity)]'::lseg AS point DEFAULT NULL ON CONVERSION ERROR);
+ point
+----------------
+ (NaN,Infinity)
+(1 row)
+
+SELECT CAST('[(1e+300,Infinity),(1e+300,Infinity)]'::lseg AS point DEFAULT NULL ON CONVERSION ERROR);
+ point
+-------------------
+ (1e+300,Infinity)
+(1 row)
+
+SELECT CAST('[(1,2),(3,4)]'::path as polygon DEFAULT NULL ON CONVERSION ERROR);
+ polygon
+---------
+
+(1 row)
+
+SELECT CAST('(NaN,1.0,NaN,infinity)'::box AS point DEFAULT NULL ON CONVERSION ERROR);
+ point
+----------------
+ (NaN,Infinity)
+(1 row)
+
+SELECT CAST('(2.0,2.0,0.0,0.0)'::box AS lseg DEFAULT NULL ON CONVERSION ERROR);
+ lseg
+---------------
+ [(2,2),(0,0)]
+(1 row)
+
+SELECT CAST('(2.0,2.0,0.0,0.0)'::box AS polygon DEFAULT NULL ON CONVERSION ERROR);
+ polygon
+---------------------------
+ ((0,0),(0,2),(2,2),(2,0))
+(1 row)
+
+SELECT CAST('(2.0,2.0,0.0,0.0)'::box AS path DEFAULT NULL ON CONVERSION ERROR);
+ path
+------
+
+(1 row)
+
+SELECT CAST('(2.0,infinity,NaN,infinity)'::box AS circle DEFAULT NULL ON CONVERSION ERROR);
+ circle
+----------------------
+ <(NaN,Infinity),NaN>
+(1 row)
+
+SELECT CAST('(NaN,0.0),(2.0,4.0),(0.0,infinity)'::polygon AS point DEFAULT NULL ON CONVERSION ERROR);
+ point
+----------------
+ (NaN,Infinity)
+(1 row)
+
+SELECT CAST('(2.0,0.0),(2.0,4.0),(0.0,0.0)'::polygon AS path DEFAULT NULL ON CONVERSION ERROR);
+ path
+---------------------
+ ((2,0),(2,4),(0,0))
+(1 row)
+
+SELECT CAST('(2.0,0.0),(2.0,4.0),(0.0,0.0)'::polygon AS box DEFAULT NULL ON CONVERSION ERROR);
+ box
+-------------
+ (2,4),(0,0)
+(1 row)
+
+SELECT CAST('(NaN,infinity),(2.0,4.0),(0.0,infinity)'::polygon AS circle DEFAULT NULL ON CONVERSION ERROR);
+ circle
+----------------------
+ <(NaN,Infinity),NaN>
+(1 row)
+
+SELECT CAST('<(5,1),3>'::circle AS point DEFAULT NULL ON CONVERSION ERROR);
+ point
+-------
+ (5,1)
+(1 row)
+
+SELECT CAST('<(3,5),0>'::circle as box DEFAULT NULL ON CONVERSION ERROR);
+ box
+-------------
+ (3,5),(3,5)
+(1 row)
+
+SELECT CAST('<(3,5),0>'::circle as polygon DEFAULT NULL ON CONVERSION ERROR);
+ERROR: cannot convert circle with radius zero to polygon
+CONTEXT: SQL function "polygon" statement 1
+-----safe cast from/to money type is not supported, all the following would result error
+SELECT CAST(NULL::int8 AS money DEFAULT NULL ON CONVERSION ERROR);
+ERROR: cannot cast type bigint to money when DEFAULT expression is specified in CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(NULL::int8 AS money DEFAULT NULL ON CONVERSION E...
+ ^
+HINT: Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
+SELECT CAST(NULL::int4 AS money DEFAULT NULL ON CONVERSION ERROR);
+ERROR: cannot cast type integer to money when DEFAULT expression is specified in CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(NULL::int4 AS money DEFAULT NULL ON CONVERSION E...
+ ^
+HINT: Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
+SELECT CAST(NULL::numeric AS money DEFAULT NULL ON CONVERSION ERROR);
+ERROR: cannot cast type numeric to money when DEFAULT expression is specified in CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(NULL::numeric AS money DEFAULT NULL ON CONVERSIO...
+ ^
+HINT: Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
+SELECT CAST(NULL::money AS numeric DEFAULT NULL ON CONVERSION ERROR);
+ERROR: cannot cast type money to numeric when DEFAULT expression is specified in CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(NULL::money AS numeric DEFAULT NULL ON CONVERSIO...
+ ^
+HINT: Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
+/*
+the following are safe cast test with type
+{
+ bytea, bit character, text, character-varying,
+ inet, macaddr8
+ int2, integer, bigint, numeric, float4, float8,
+ date, interval, timestamptz, timestamp
+ jsonb
+}
+*/
+-----safe cast from bytea type to other type
+SELECT CAST ('\x112233445566778899'::bytea AS int8 DEFAULT NULL ON CONVERSION ERROR);
+ int8
+------
+
+(1 row)
+
+SELECT CAST('\x123456789A'::bytea AS int4 DEFAULT NULL ON CONVERSION ERROR);
+ int4
+------
+
+(1 row)
+
+SELECT CAST('\x123456'::bytea AS int2 DEFAULT NULL ON CONVERSION ERROR);
+ int2
+------
+
+(1 row)
+
+SELECT CAST('111111111100001'::bit(100) AS INT DEFAULT NULL ON CONVERSION ERROR);
+ int4
+------
+
+(1 row)
+
+SELECT CAST ('111111111100001'::bit(100) AS INT8 DEFAULT NULL ON CONVERSION ERROR);
+ int8
+------
+
+(1 row)
+
+select CAST('a.b.c.d'::text as regclass default NULL on conversion error);
+ regclass
+----------
+
+(1 row)
+
+CREATE TABLE test_safecast(col0 text);
+INSERT INTO test_safecast(col0) VALUES ('<value>one</value');
+SELECT col0 as text,
+ CAST(col0 AS regclass DEFAULT NULL ON CONVERSION ERROR) as to_regclass,
+ CAST(col0 AS regclass DEFAULT NULL ON CONVERSION ERROR) IS NULL as expect_true,
+ CAST(col0 AS "char" DEFAULT NULL ON CONVERSION ERROR) as to_char,
+ CAST(col0 AS name DEFAULT NULL ON CONVERSION ERROR) as to_name,
+ CAST(col0 AS xml DEFAULT NULL ON CONVERSION ERROR) as to_xml
+FROM test_safecast;
+ text | to_regclass | expect_true | to_char | to_name | to_xml
+-------------------+-------------+-------------+---------+-------------------+--------
+ <value>one</value | | t | < | <value>one</value |
+(1 row)
+
+-----safe cast from other type to inet/cidr
+SELECT CAST('192.168.1.x' as inet DEFAULT NULL ON CONVERSION ERROR);
+ inet
+------
+
+(1 row)
+
+SELECT CAST('192.168.1.2/30' as cidr DEFAULT NULL ON CONVERSION ERROR);
+ cidr
+------
+
+(1 row)
+
+SELECT CAST('22:00:5c:08:55:08:01:02'::macaddr8 as macaddr DEFAULT NULL ON CONVERSION ERROR);
+ macaddr
+---------
+
+(1 row)
+
+-----safe cast from range type to other type
+SELECT CAST('[1,2]'::int4range AS int4multirange DEFAULT NULL ON CONVERSION ERROR);
+ int4multirange
+----------------
+ {[1,3)}
+(1 row)
+
+SELECT CAST('[1,2]'::int8range AS int8multirange DEFAULT NULL ON CONVERSION ERROR);
+ int8multirange
+----------------
+ {[1,3)}
+(1 row)
+
+SELECT CAST('[1,2]'::numrange AS nummultirange DEFAULT NULL ON CONVERSION ERROR);
+ nummultirange
+---------------
+ {[1,2]}
+(1 row)
+
+SELECT CAST('[-infinity,infinity]'::daterange AS datemultirange DEFAULT NULL ON CONVERSION ERROR);
+ datemultirange
+------------------------
+ {[-infinity,infinity]}
+(1 row)
+
+SELECT CAST('[-infinity,infinity]'::tsrange AS tsmultirange DEFAULT NULL ON CONVERSION ERROR);
+ tsmultirange
+------------------------
+ {[-infinity,infinity]}
+(1 row)
+
+SELECT CAST('[-infinity,infinity]'::tstzrange AS tstzmultirange DEFAULT NULL ON CONVERSION ERROR);
+ tstzmultirange
+------------------------
+ {[-infinity,infinity]}
+(1 row)
+
+--test cast numeric value with fraction to another numeric value
+CREATE TABLE safecast(col1 float4, col2 float8, col3 numeric, col4 numeric[],
+ col5 int2 default 32767,
+ col6 int4 default 32768,
+ col7 int8 default 4294967296);
+INSERT INTO safecast VALUES('11.1234', '11.1234', '9223372036854775808', '{11.1234}'::numeric[]);
+INSERT INTO safecast VALUES('inf', 'inf', 'inf', '{11.1234, 12, inf, NaN}'::numeric[]);
+INSERT INTO safecast VALUES('-inf', '-inf', '-inf', '{11.1234, 12, -inf, NaN}'::numeric[]);
+INSERT INTO safecast VALUES('NaN', 'NaN', 'NaN', '{11.1234, 12, -inf, NaN}'::numeric[]);
+SELECT col5 as int2,
+ CAST(col5 AS int2 DEFAULT NULL ON CONVERSION ERROR) as to_int2,
+ CAST(col5 AS int4 DEFAULT NULL ON CONVERSION ERROR) as to_int4,
+ CAST(col5 AS oid DEFAULT NULL ON CONVERSION ERROR) as to_oid,
+ CAST(col5 as int8 DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+ CAST(col5 as float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+ CAST(col5 as float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+ CAST(col5 as numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+ CAST(col5 as numeric(10,1) DEFAULT NULL ON CONVERSION ERROR) as to_numeric_scale
+FROM safecast;
+ int2 | to_int2 | to_int4 | to_oid | to_int8 | to_float4 | to_float8 | to_numeric | to_numeric_scale
+-------+---------+---------+--------+---------+-----------+-----------+------------+------------------
+ 32767 | 32767 | 32767 | 32767 | 32767 | 32767 | 32767 | 32767 | 32767.0
+ 32767 | 32767 | 32767 | 32767 | 32767 | 32767 | 32767 | 32767 | 32767.0
+ 32767 | 32767 | 32767 | 32767 | 32767 | 32767 | 32767 | 32767 | 32767.0
+ 32767 | 32767 | 32767 | 32767 | 32767 | 32767 | 32767 | 32767 | 32767.0
+(4 rows)
+
+SELECT col6 as int4,
+ CAST(col6 AS int2 DEFAULT NULL ON CONVERSION ERROR) as to_int2,
+ CAST(col6 AS int4 DEFAULT NULL ON CONVERSION ERROR) as to_int4,
+ CAST(col6 AS oid DEFAULT NULL ON CONVERSION ERROR) as to_oid,
+ CAST(col6 as int8 DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+ CAST(col6 as float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+ CAST(col6 as float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+ CAST(col6 as numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+ CAST(col6 as numeric(10,1) DEFAULT NULL ON CONVERSION ERROR) as to_numeric_scale
+FROM safecast;
+ int4 | to_int2 | to_int4 | to_oid | to_int8 | to_float4 | to_float8 | to_numeric | to_numeric_scale
+-------+---------+---------+--------+---------+-----------+-----------+------------+------------------
+ 32768 | | 32768 | 32768 | 32768 | 32768 | 32768 | 32768 | 32768.0
+ 32768 | | 32768 | 32768 | 32768 | 32768 | 32768 | 32768 | 32768.0
+ 32768 | | 32768 | 32768 | 32768 | 32768 | 32768 | 32768 | 32768.0
+ 32768 | | 32768 | 32768 | 32768 | 32768 | 32768 | 32768 | 32768.0
+(4 rows)
+
+SELECT col7 as int8,
+ CAST(col7 AS int2 DEFAULT NULL ON CONVERSION ERROR) as to_int2,
+ CAST(col7 AS int4 DEFAULT NULL ON CONVERSION ERROR) as to_int4,
+ CAST(col7 AS oid DEFAULT NULL ON CONVERSION ERROR) as to_oid,
+ CAST(col7 as int8 DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+ CAST(col7 as float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+ CAST(col7 as float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+ CAST(col7 as numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+ CAST(col7 as numeric(10,1) DEFAULT NULL ON CONVERSION ERROR) as to_numeric_scale
+FROM safecast;
+ int8 | to_int2 | to_int4 | to_oid | to_int8 | to_float4 | to_float8 | to_numeric | to_numeric_scale
+------------+---------+---------+--------+------------+-------------+------------+------------+------------------
+ 4294967296 | | | | 4294967296 | 4.29497e+09 | 4294967296 | 4294967296 |
+ 4294967296 | | | | 4294967296 | 4.29497e+09 | 4294967296 | 4294967296 |
+ 4294967296 | | | | 4294967296 | 4.29497e+09 | 4294967296 | 4294967296 |
+ 4294967296 | | | | 4294967296 | 4.29497e+09 | 4294967296 | 4294967296 |
+(4 rows)
+
+SELECT col3 as numeric,
+ CAST(col3 AS int2 DEFAULT NULL ON CONVERSION ERROR) as to_int2,
+ CAST(col3 AS int4 DEFAULT NULL ON CONVERSION ERROR) as to_int4,
+ CAST(col3 AS oid DEFAULT NULL ON CONVERSION ERROR) as to_oid,
+ CAST(col3 as int8 DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+ CAST(col3 as float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+ CAST(col3 as float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+ CAST(col3 as numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+ CAST(col3 as numeric(10,1) DEFAULT NULL ON CONVERSION ERROR) as to_numeric_scale
+FROM safecast;
+ numeric | to_int2 | to_int4 | to_oid | to_int8 | to_float4 | to_float8 | to_numeric | to_numeric_scale
+---------------------+---------+---------+--------+---------+-------------+----------------------+---------------------+------------------
+ 9223372036854775808 | | | | | 9.22337e+18 | 9.22337203685478e+18 | 9223372036854775808 |
+ Infinity | | | | | Infinity | Infinity | Infinity |
+ -Infinity | | | | | -Infinity | -Infinity | -Infinity |
+ NaN | | | | | NaN | NaN | NaN | NaN
+(4 rows)
+
+SELECT col4 as num_arr,
+ CAST(col4 AS int2[] DEFAULT NULL ON CONVERSION ERROR) as int2arr,
+ CAST(col4 AS int4[] DEFAULT NULL ON CONVERSION ERROR) as int4arr,
+ CAST(col4 as int8[] DEFAULT NULL ON CONVERSION ERROR) as int8arr,
+ CAST(col4 as float4[] DEFAULT NULL ON CONVERSION ERROR) as f4arr,
+ CAST(col4 as float8[] DEFAULT NULL ON CONVERSION ERROR) as f8arr,
+ CAST(col4 as numeric(10,1)[] DEFAULT NULL ON CONVERSION ERROR) as numarr
+FROM safecast;
+ num_arr | int2arr | int4arr | int8arr | f4arr | f8arr | numarr
+----------------------------+---------+---------+---------+----------------------------+----------------------------+--------
+ {11.1234} | {11} | {11} | {11} | {11.1234} | {11.1234} | {11.1}
+ {11.1234,12,Infinity,NaN} | | | | {11.1234,12,Infinity,NaN} | {11.1234,12,Infinity,NaN} |
+ {11.1234,12,-Infinity,NaN} | | | | {11.1234,12,-Infinity,NaN} | {11.1234,12,-Infinity,NaN} |
+ {11.1234,12,-Infinity,NaN} | | | | {11.1234,12,-Infinity,NaN} | {11.1234,12,-Infinity,NaN} |
+(4 rows)
+
+SELECT col1 as float4,
+ CAST(col1 AS int2 DEFAULT NULL ON CONVERSION ERROR) as to_int2,
+ CAST(col1 AS int4 DEFAULT NULL ON CONVERSION ERROR) as to_int4,
+ CAST(col1 AS oid DEFAULT NULL ON CONVERSION ERROR) as to_oid,
+ CAST(col1 as int8 DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+ CAST(col1 as float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+ CAST(col1 as float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+ CAST(col1 as numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+ CAST(col1 as numeric(10,1) DEFAULT NULL ON CONVERSION ERROR) as to_numeric_scale
+FROM safecast;
+ float4 | to_int2 | to_int4 | to_oid | to_int8 | to_float4 | to_float8 | to_numeric | to_numeric_scale
+-----------+---------+---------+--------+---------+-----------+------------------+------------+------------------
+ 11.1234 | 11 | 11 | | 11 | 11.1234 | 11.1233997344971 | 11.1234 | 11.1
+ Infinity | | | | | Infinity | Infinity | Infinity |
+ -Infinity | | | | | -Infinity | -Infinity | -Infinity |
+ NaN | | | | | NaN | NaN | NaN | NaN
+(4 rows)
+
+SELECT col2 as float8,
+ CAST(col2 AS int2 DEFAULT NULL ON CONVERSION ERROR) as to_int2,
+ CAST(col2 AS int4 DEFAULT NULL ON CONVERSION ERROR) as to_int4,
+ CAST(col2 AS oid DEFAULT NULL ON CONVERSION ERROR) as to_oid,
+ CAST(col2 as int8 DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+ CAST(col2 as float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+ CAST(col2 as float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+ CAST(col2 as numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+ CAST(col2 as numeric(10,1) DEFAULT NULL ON CONVERSION ERROR) as to_numeric_scale
+FROM safecast;
+ float8 | to_int2 | to_int4 | to_oid | to_int8 | to_float4 | to_float8 | to_numeric | to_numeric_scale
+-----------+---------+---------+--------+---------+-----------+-----------+------------+------------------
+ 11.1234 | 11 | 11 | | 11 | 11.1234 | 11.1234 | 11.1234 | 11.1
+ Infinity | | | | | Infinity | Infinity | Infinity |
+ -Infinity | | | | | -Infinity | -Infinity | -Infinity |
+ NaN | | | | | NaN | NaN | NaN | NaN
+(4 rows)
+
+--test date/timestamp/interval realted cast
+CREATE TABLE safecast1(col0 date, col1 timestamp, col2 timestamptz, col3 interval, col4 time, col5 timetz);
+insert into safecast1 VALUES
+('-infinity', '-infinity', '-infinity', '-infinity',
+ '2003-03-07 15:36:39 America/New_York', '2003-07-07 15:36:39 America/New_York'),
+('-infinity', 'infinity', 'infinity', 'infinity',
+ '11:59:59.99 PM', '11:59:59.99 PM PDT');
+SELECT col0 as date,
+ CAST(col0 AS timestamptz DEFAULT NULL ON CONVERSION ERROR) as to_timestamptz,
+ CAST(col0 AS date DEFAULT NULL ON CONVERSION ERROR) as to_date,
+ CAST(col0 AS time DEFAULT NULL ON CONVERSION ERROR) as to_times,
+ CAST(col0 AS timetz DEFAULT NULL ON CONVERSION ERROR) as to_timetz,
+ CAST(col0 AS timestamp(2) DEFAULT NULL ON CONVERSION ERROR) as to_timestamp_scale
+FROM safecast1;
+ date | to_timestamptz | to_date | to_times | to_timetz | to_timestamp_scale
+-----------+----------------+-----------+----------+-----------+--------------------
+ -infinity | -infinity | -infinity | | | -infinity
+ -infinity | -infinity | -infinity | | | -infinity
+(2 rows)
+
+SELECT col1 as timestamp,
+ CAST(col1 AS timestamptz DEFAULT NULL ON CONVERSION ERROR) as to_timestamptz,
+ CAST(col1 AS date DEFAULT NULL ON CONVERSION ERROR) as to_date,
+ CAST(col1 AS time DEFAULT NULL ON CONVERSION ERROR) as to_times,
+ CAST(col1 AS timetz DEFAULT NULL ON CONVERSION ERROR) as to_timetz,
+ CAST(col1 AS timestamp(2) DEFAULT NULL ON CONVERSION ERROR) as to_timestamp_scale
+FROM safecast1;
+ timestamp | to_timestamptz | to_date | to_times | to_timetz | to_timestamp_scale
+-----------+----------------+-----------+----------+-----------+--------------------
+ -infinity | -infinity | -infinity | | | -infinity
+ infinity | infinity | infinity | | | infinity
+(2 rows)
+
+SELECT col2 as timestamptz,
+ CAST(col2 AS timestamptz DEFAULT NULL ON CONVERSION ERROR) as to_timestamptz,
+ CAST(col2 AS date DEFAULT NULL ON CONVERSION ERROR) as to_date,
+ CAST(col2 AS time DEFAULT NULL ON CONVERSION ERROR) as to_times,
+ CAST(col2 AS timetz DEFAULT NULL ON CONVERSION ERROR) as to_timetz,
+ CAST(col2 AS timestamp(2) DEFAULT NULL ON CONVERSION ERROR) as to_timestamp_scale
+FROM safecast1;
+ timestamptz | to_timestamptz | to_date | to_times | to_timetz | to_timestamp_scale
+-------------+----------------+-----------+----------+-----------+--------------------
+ -infinity | -infinity | -infinity | | | -infinity
+ infinity | infinity | infinity | | | infinity
+(2 rows)
+
+SELECT col3 as interval,
+ CAST(col3 AS timestamptz DEFAULT NULL ON CONVERSION ERROR) as to_timestamptz,
+ CAST(col3 AS date DEFAULT NULL ON CONVERSION ERROR) as to_date,
+ CAST(col3 AS time DEFAULT NULL ON CONVERSION ERROR) as to_times,
+ CAST(col3 AS timetz DEFAULT NULL ON CONVERSION ERROR) as to_timetz,
+ CAST(col3 AS timestamp(2) DEFAULT NULL ON CONVERSION ERROR) as to_timestamp_scale,
+ CAST(col3 AS interval(2) DEFAULT NULL ON CONVERSION ERROR) as to_interval_scale
+FROM safecast1;
+ interval | to_timestamptz | to_date | to_times | to_timetz | to_timestamp_scale | to_interval_scale
+-----------+----------------+---------+----------+-----------+--------------------+-------------------
+ -infinity | | | | | | -infinity
+ infinity | | | | | | infinity
+(2 rows)
+
+SELECT col4 as time,
+ CAST(col4 AS time(2) DEFAULT NULL ON CONVERSION ERROR) as to_times,
+ CAST(col4 AS timetz DEFAULT NULL ON CONVERSION ERROR) as to_timetz,
+ CAST(col4 AS interval(2) DEFAULT NULL ON CONVERSION ERROR) as to_interval,
+ CAST(col4 AS interval(2) DEFAULT NULL ON CONVERSION ERROR) as to_interval_scale
+FROM safecast1;
+ time | to_times | to_timetz | to_interval | to_interval_scale
+-------------+-------------+----------------+-------------------------------+-------------------------------
+ 15:36:39 | 15:36:39 | 15:36:39-07 | @ 15 hours 36 mins 39 secs | @ 15 hours 36 mins 39 secs
+ 23:59:59.99 | 23:59:59.99 | 23:59:59.99-07 | @ 23 hours 59 mins 59.99 secs | @ 23 hours 59 mins 59.99 secs
+(2 rows)
+
+SELECT col5 as timetz,
+ CAST(col5 AS time DEFAULT NULL ON CONVERSION ERROR) as to_time,
+ CAST(col5 AS time(2) DEFAULT NULL ON CONVERSION ERROR) as to_time_scale,
+ CAST(col5 AS timetz DEFAULT NULL ON CONVERSION ERROR) as to_timetz,
+ CAST(col5 AS timetz(6) DEFAULT NULL ON CONVERSION ERROR) as to_timetz_scale,
+ CAST(col5 AS interval DEFAULT NULL ON CONVERSION ERROR) as to_interval,
+ CAST(col5 AS interval(2) DEFAULT NULL ON CONVERSION ERROR) as to_interval_scale
+FROM safecast1;
+ timetz | to_time | to_time_scale | to_timetz | to_timetz_scale | to_interval | to_interval_scale
+----------------+-------------+---------------+----------------+-----------------+-------------+-------------------
+ 15:36:39-04 | 15:36:39 | 15:36:39 | 15:36:39-04 | 15:36:39-04 | |
+ 23:59:59.99-07 | 23:59:59.99 | 23:59:59.99 | 23:59:59.99-07 | 23:59:59.99-07 | |
+(2 rows)
+
+CREATE TEMP TABLE test_safecast1(col0 jsonb);
+INSERT INTO test_safecast1(col0) VALUES ('"test"');
+SELECT col0 as jsonb,
+ CAST(col0 AS integer DEFAULT NULL ON CONVERSION ERROR) as to_integer,
+ CAST(col0 AS numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+ CAST(col0 AS bigint DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+ CAST(col0 AS float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+ CAST(col0 AS float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+ CAST(col0 AS boolean DEFAULT NULL ON CONVERSION ERROR) as to_bool,
+ CAST(col0 AS smallint DEFAULT NULL ON CONVERSION ERROR) as to_smallint
+FROM test_safecast1;
+ jsonb | to_integer | to_numeric | to_int8 | to_float4 | to_float8 | to_bool | to_smallint
+--------+------------+------------+---------+-----------+-----------+---------+-------------
+ "test" | | | | | | |
+(1 row)
+
+-- test deparse
+CREATE DOMAIN d_int_arr as int[];
+CREATE VIEW safecastview AS
+SELECT CAST('1234' as char(3) DEFAULT -1111 ON CONVERSION ERROR),
+ CAST(1 as date DEFAULT ((now()::date + random(min=>1, max=>1::int))) ON CONVERSION ERROR) as safecast,
+ CAST(ARRAY[['1'], ['three'],['a']] AS INTEGER[] DEFAULT '{1,2}' ON CONVERSION ERROR) as cast1,
+ CAST(ARRAY[['1', '2'], ['three', 'a']] AS text[] DEFAULT '{21,22}' ON CONVERSION ERROR) as cast2;
+\sv safecastview
+CREATE OR REPLACE VIEW public.safecastview AS
+ SELECT CAST('1234' AS character(3) DEFAULT '-1111'::integer::character(3) ON CONVERSION ERROR) AS bpchar,
+ CAST(1 AS date DEFAULT now()::date + random(min => 1, max => 1) ON CONVERSION ERROR) AS safecast,
+ CAST(ARRAY[ARRAY[1], ARRAY['three'::text], ARRAY['a'::text]] AS integer[] DEFAULT '{1,2}'::integer[] ON CONVERSION ERROR) AS cast1,
+ CAST(ARRAY[ARRAY['1'::text, '2'::text], ARRAY['three'::text, 'a'::text]] AS text[] DEFAULT '{21,22}'::text[] ON CONVERSION ERROR) AS cast2
+CREATE VIEW safecastview1 AS
+SELECT CAST(ARRAY[['1'], ['three'],['a']] AS d_int_arr DEFAULT '{1,2}' ON CONVERSION ERROR) as cast1,
+ CAST(ARRAY[['1', '2'], ['three', 'a']] AS d_int_arr DEFAULT '{21,22}' ON CONVERSION ERROR) as cast2;
+\sv safecastview1
+CREATE OR REPLACE VIEW public.safecastview1 AS
+ SELECT CAST(ARRAY[ARRAY[1], ARRAY['three'::text], ARRAY['a'::text]] AS d_int_arr DEFAULT '{1,2}'::integer[]::d_int_arr ON CONVERSION ERROR) AS cast1,
+ CAST(ARRAY[ARRAY[1, 2], ARRAY['three'::text, 'a'::text]] AS d_int_arr DEFAULT '{21,22}'::integer[]::d_int_arr ON CONVERSION ERROR) AS cast2
+SELECT * FROM safecastview1;
+ cast1 | cast2
+-------+---------
+ {1,2} | {21,22}
+(1 row)
+
+CREATE INDEX cast_error_idx ON tcast((cast(c as int DEFAULT random(min=>1, max=>1) ON CONVERSION ERROR))); --error
+ERROR: functions in index expression must be marked IMMUTABLE
+RESET extra_float_digits;
diff --git a/src/test/regress/expected/create_cast.out b/src/test/regress/expected/create_cast.out
index 0e69644bca2..38eab453344 100644
--- a/src/test/regress/expected/create_cast.out
+++ b/src/test/regress/expected/create_cast.out
@@ -88,6 +88,11 @@ SELECT 1234::int4::casttesttype; -- Should work now
bar1234
(1 row)
+SELECT CAST(1234::int4 AS casttesttype DEFAULT NULL ON CONVERSION ERROR); -- error
+ERROR: cannot cast type integer to casttesttype when DEFAULT expression is specified in CAST ... ON CONVERSION ERROR
+LINE 1: SELECT CAST(1234::int4 AS casttesttype DEFAULT NULL ON CONVE...
+ ^
+HINT: Currently CAST ... DEFAULT ON CONVERSION ERROR does not support this cast
-- check dependencies generated for that
SELECT pg_describe_object(classid, objid, objsubid) as obj,
pg_describe_object(refclassid, refobjid, refobjsubid) as objref,
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index a357e1d0c0e..81ea244859f 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -943,8 +943,8 @@ SELECT *
FROM pg_cast c
WHERE castsource = 0 OR casttarget = 0 OR castcontext NOT IN ('e', 'a', 'i')
OR castmethod NOT IN ('f', 'b' ,'i');
- oid | castsource | casttarget | castfunc | castcontext | castmethod
------+------------+------------+----------+-------------+------------
+ oid | castsource | casttarget | castfunc | castcontext | castmethod | casterrorsafe
+-----+------------+------------+----------+-------------+------------+---------------
(0 rows)
-- Check that castfunc is nonzero only for cast methods that need a function,
@@ -953,8 +953,8 @@ SELECT *
FROM pg_cast c
WHERE (castmethod = 'f' AND castfunc = 0)
OR (castmethod IN ('b', 'i') AND castfunc <> 0);
- oid | castsource | casttarget | castfunc | castcontext | castmethod
------+------------+------------+----------+-------------+------------
+ oid | castsource | casttarget | castfunc | castcontext | castmethod | casterrorsafe
+-----+------------+------------+----------+-------------+------------+---------------
(0 rows)
-- Look for casts to/from the same type that aren't length coercion functions.
@@ -963,15 +963,15 @@ WHERE (castmethod = 'f' AND castfunc = 0)
SELECT *
FROM pg_cast c
WHERE castsource = casttarget AND castfunc = 0;
- oid | castsource | casttarget | castfunc | castcontext | castmethod
------+------------+------------+----------+-------------+------------
+ oid | castsource | casttarget | castfunc | castcontext | castmethod | casterrorsafe
+-----+------------+------------+----------+-------------+------------+---------------
(0 rows)
SELECT c.*
FROM pg_cast c, pg_proc p
WHERE c.castfunc = p.oid AND p.pronargs < 2 AND castsource = casttarget;
- oid | castsource | casttarget | castfunc | castcontext | castmethod
------+------------+------------+----------+-------------+------------
+ oid | castsource | casttarget | castfunc | castcontext | castmethod | casterrorsafe
+-----+------------+------------+----------+-------------+------------+---------------
(0 rows)
-- Look for cast functions that don't have the right signature. The
@@ -989,8 +989,8 @@ WHERE c.castfunc = p.oid AND
OR (c.castsource = 'character'::regtype AND
p.proargtypes[0] = 'text'::regtype))
OR NOT binary_coercible(p.prorettype, c.casttarget));
- oid | castsource | casttarget | castfunc | castcontext | castmethod
------+------------+------------+----------+-------------+------------
+ oid | castsource | casttarget | castfunc | castcontext | castmethod | casterrorsafe
+-----+------------+------------+----------+-------------+------------+---------------
(0 rows)
SELECT c.*
@@ -998,8 +998,8 @@ FROM pg_cast c, pg_proc p
WHERE c.castfunc = p.oid AND
((p.pronargs > 1 AND p.proargtypes[1] != 'int4'::regtype) OR
(p.pronargs > 2 AND p.proargtypes[2] != 'bool'::regtype));
- oid | castsource | casttarget | castfunc | castcontext | castmethod
------+------------+------------+----------+-------------+------------
+ oid | castsource | casttarget | castfunc | castcontext | castmethod | casterrorsafe
+-----+------------+------------+----------+-------------+------------+---------------
(0 rows)
-- Look for binary compatible casts that do not have the reverse
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index f9450cdc477..573a6699a63 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -81,7 +81,7 @@ test: brin_bloom brin_multi
test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.utf8 collate.icu.utf8 incremental_sort create_role without_overlaps generated_virtual
# collate.linux.utf8 and collate.icu.utf8 tests cannot be run in parallel with each other
-test: rules psql psql_crosstab psql_pipeline amutils stats_ext collate.linux.utf8 collate.windows.win1252
+test: rules psql psql_crosstab psql_pipeline amutils stats_ext collate.linux.utf8 collate.windows.win1252 cast
# ----------
# Run these alone so they don't run out of parallel workers
diff --git a/src/test/regress/sql/cast.sql b/src/test/regress/sql/cast.sql
new file mode 100644
index 00000000000..eb68ec21859
--- /dev/null
+++ b/src/test/regress/sql/cast.sql
@@ -0,0 +1,301 @@
+SET extra_float_digits = 0;
+
+-- CAST DEFAULT ON CONVERSION ERROR
+VALUES (CAST('error' AS integer ERROR ON CONVERSION ERROR)); --error
+VALUES (CAST('error' AS integer NULL ON CONVERSION ERROR));
+VALUES (CAST('error' AS integer DEFAULT 42 ON CONVERSION ERROR));
+SELECT CAST(1 AS date DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST(1::numeric AS money DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST(1111 AS "char" DEFAULT NULL ON CONVERSION ERROR);
+
+CREATE OR REPLACE FUNCTION ret_int8() RETURNS bigint AS
+$$
+BEGIN RETURN 2147483648; END;
+$$
+LANGUAGE plpgsql IMMUTABLE;
+
+SELECT CAST('a' as int DEFAULT ret_int8() ON CONVERSION ERROR); --error
+SELECT CAST('a' as date DEFAULT ret_int8() ON CONVERSION ERROR); --error
+
+-- test array coerce
+SELECT CAST(array[11.12] AS int[] DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST(array[11.12] AS date[] DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST(array[11111111111111111] AS int[] DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST(array[['abc'],[456]] AS int[] DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('{123,abc,456}' AS integer[] DEFAULT '{-789}' ON CONVERSION ERROR);
+SELECT CAST('{234,def,567}'::text[] AS integer[] DEFAULT '{-1011}' ON CONVERSION ERROR);
+SELECT CAST(ARRAY[['1'], ['three'],['a']] AS INTEGER[] DEFAULT '{1,2}' ON CONVERSION ERROR);
+SELECT CAST(ARRAY[['1', '2'], ['three', 'a']] AS text[] DEFAULT '{21,22}' ON CONVERSION ERROR);
+
+-- test valid DEFAULT expression for CAST = ON CONVERSION ERROR
+CREATE OR REPLACE FUNCTION ret_setint() RETURNS SETOF integer AS
+$$
+BEGIN RETURN QUERY EXECUTE 'select 1 union all select 1'; END;
+$$
+LANGUAGE plpgsql IMMUTABLE;
+
+CREATE TABLE tcast(a text[], b int GENERATED BY DEFAULT AS IDENTITY, c text default '1');
+INSERT INTO tcast VALUES ('{12}'), ('{1,a, b}'), ('{{1,2}, {c,d}}'), ('{13}');
+SELECT CAST('a' as int DEFAULT ret_setint() ON CONVERSION ERROR) FROM tcast; --error
+SELECT CAST('a' as int DEFAULT sum(1) ON CONVERSION ERROR); --error
+SELECT CAST('a' as int DEFAULT sum(1) over() ON CONVERSION ERROR); --error
+SELECT CAST('a' as int DEFAULT 'b' ON CONVERSION ERROR); --error
+
+SELECT CAST(t AS text[] DEFAULT '{21,22, ' || b || '}' ON CONVERSION ERROR) FROM tcast as t;
+SELECT CAST(t.a AS int[] DEFAULT '{21,22}'::int[] || b ON CONVERSION ERROR) FROM tcast as t;
+
+-- test with domain
+CREATE DOMAIN d_int42 as int check (value = 42) NOT NULL;
+CREATE DOMAIN d_char3_not_null as char(3) NOT NULL;
+CREATE DOMAIN d_varchar as varchar(3) NOT NULL;
+CREATE TYPE comp_domain_with_typmod AS (a d_char3_not_null, b int);
+CREATE TYPE comp2 AS (a d_varchar);
+SELECT CAST('(NULL)' AS comp2 DEFAULT '(1232)' ON CONVERSION ERROR); --error
+SELECT CAST('(NULL)' AS comp2 DEFAULT '(123)' ON CONVERSION ERROR); --ok
+SELECT CAST(11 AS d_int42 DEFAULT 41 ON CONVERSION ERROR); --error
+SELECT CAST(11 AS d_int42 DEFAULT 42 ON CONVERSION ERROR); --ok
+SELECT CAST(NULL AS d_int42 DEFAULT NULL ON CONVERSION ERROR); --error
+SELECT CAST(NULL AS d_int42 DEFAULT 42 ON CONVERSION ERROR); --ok
+SELECT CAST('(,42)' AS comp_domain_with_typmod DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('(NULL,42)' AS comp_domain_with_typmod DEFAULT '(1,2)' ON CONVERSION ERROR);
+SELECT CAST('(NULL,42)' AS comp_domain_with_typmod DEFAULT '(1234,2)' ON CONVERSION ERROR); --error
+
+-----safe cast with geometry data type
+SELECT CAST('(1,2)'::point AS box DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('[(NaN,1),(NaN,infinity)]'::lseg AS point DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('[(1e+300,Infinity),(1e+300,Infinity)]'::lseg AS point DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('[(1,2),(3,4)]'::path as polygon DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('(NaN,1.0,NaN,infinity)'::box AS point DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('(2.0,2.0,0.0,0.0)'::box AS lseg DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('(2.0,2.0,0.0,0.0)'::box AS polygon DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('(2.0,2.0,0.0,0.0)'::box AS path DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('(2.0,infinity,NaN,infinity)'::box AS circle DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('(NaN,0.0),(2.0,4.0),(0.0,infinity)'::polygon AS point DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('(2.0,0.0),(2.0,4.0),(0.0,0.0)'::polygon AS path DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('(2.0,0.0),(2.0,4.0),(0.0,0.0)'::polygon AS box DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('(NaN,infinity),(2.0,4.0),(0.0,infinity)'::polygon AS circle DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('<(5,1),3>'::circle AS point DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('<(3,5),0>'::circle as box DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('<(3,5),0>'::circle as polygon DEFAULT NULL ON CONVERSION ERROR);
+
+-----safe cast from/to money type is not supported, all the following would result error
+SELECT CAST(NULL::int8 AS money DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST(NULL::int4 AS money DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST(NULL::numeric AS money DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST(NULL::money AS numeric DEFAULT NULL ON CONVERSION ERROR);
+
+/*
+the following are safe cast test with type
+{
+ bytea, bit character, text, character-varying,
+ inet, macaddr8
+ int2, integer, bigint, numeric, float4, float8,
+ date, interval, timestamptz, timestamp
+ jsonb
+}
+*/
+-----safe cast from bytea type to other type
+SELECT CAST ('\x112233445566778899'::bytea AS int8 DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('\x123456789A'::bytea AS int4 DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('\x123456'::bytea AS int2 DEFAULT NULL ON CONVERSION ERROR);
+
+SELECT CAST('111111111100001'::bit(100) AS INT DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST ('111111111100001'::bit(100) AS INT8 DEFAULT NULL ON CONVERSION ERROR);
+
+select CAST('a.b.c.d'::text as regclass default NULL on conversion error);
+
+CREATE TABLE test_safecast(col0 text);
+INSERT INTO test_safecast(col0) VALUES ('<value>one</value');
+
+SELECT col0 as text,
+ CAST(col0 AS regclass DEFAULT NULL ON CONVERSION ERROR) as to_regclass,
+ CAST(col0 AS regclass DEFAULT NULL ON CONVERSION ERROR) IS NULL as expect_true,
+ CAST(col0 AS "char" DEFAULT NULL ON CONVERSION ERROR) as to_char,
+ CAST(col0 AS name DEFAULT NULL ON CONVERSION ERROR) as to_name,
+ CAST(col0 AS xml DEFAULT NULL ON CONVERSION ERROR) as to_xml
+FROM test_safecast;
+
+-----safe cast from other type to inet/cidr
+SELECT CAST('192.168.1.x' as inet DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('192.168.1.2/30' as cidr DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('22:00:5c:08:55:08:01:02'::macaddr8 as macaddr DEFAULT NULL ON CONVERSION ERROR);
+
+-----safe cast from range type to other type
+SELECT CAST('[1,2]'::int4range AS int4multirange DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('[1,2]'::int8range AS int8multirange DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('[1,2]'::numrange AS nummultirange DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('[-infinity,infinity]'::daterange AS datemultirange DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('[-infinity,infinity]'::tsrange AS tsmultirange DEFAULT NULL ON CONVERSION ERROR);
+SELECT CAST('[-infinity,infinity]'::tstzrange AS tstzmultirange DEFAULT NULL ON CONVERSION ERROR);
+
+--test cast numeric value with fraction to another numeric value
+CREATE TABLE safecast(col1 float4, col2 float8, col3 numeric, col4 numeric[],
+ col5 int2 default 32767,
+ col6 int4 default 32768,
+ col7 int8 default 4294967296);
+INSERT INTO safecast VALUES('11.1234', '11.1234', '9223372036854775808', '{11.1234}'::numeric[]);
+INSERT INTO safecast VALUES('inf', 'inf', 'inf', '{11.1234, 12, inf, NaN}'::numeric[]);
+INSERT INTO safecast VALUES('-inf', '-inf', '-inf', '{11.1234, 12, -inf, NaN}'::numeric[]);
+INSERT INTO safecast VALUES('NaN', 'NaN', 'NaN', '{11.1234, 12, -inf, NaN}'::numeric[]);
+
+SELECT col5 as int2,
+ CAST(col5 AS int2 DEFAULT NULL ON CONVERSION ERROR) as to_int2,
+ CAST(col5 AS int4 DEFAULT NULL ON CONVERSION ERROR) as to_int4,
+ CAST(col5 AS oid DEFAULT NULL ON CONVERSION ERROR) as to_oid,
+ CAST(col5 as int8 DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+ CAST(col5 as float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+ CAST(col5 as float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+ CAST(col5 as numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+ CAST(col5 as numeric(10,1) DEFAULT NULL ON CONVERSION ERROR) as to_numeric_scale
+FROM safecast;
+
+SELECT col6 as int4,
+ CAST(col6 AS int2 DEFAULT NULL ON CONVERSION ERROR) as to_int2,
+ CAST(col6 AS int4 DEFAULT NULL ON CONVERSION ERROR) as to_int4,
+ CAST(col6 AS oid DEFAULT NULL ON CONVERSION ERROR) as to_oid,
+ CAST(col6 as int8 DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+ CAST(col6 as float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+ CAST(col6 as float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+ CAST(col6 as numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+ CAST(col6 as numeric(10,1) DEFAULT NULL ON CONVERSION ERROR) as to_numeric_scale
+FROM safecast;
+
+SELECT col7 as int8,
+ CAST(col7 AS int2 DEFAULT NULL ON CONVERSION ERROR) as to_int2,
+ CAST(col7 AS int4 DEFAULT NULL ON CONVERSION ERROR) as to_int4,
+ CAST(col7 AS oid DEFAULT NULL ON CONVERSION ERROR) as to_oid,
+ CAST(col7 as int8 DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+ CAST(col7 as float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+ CAST(col7 as float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+ CAST(col7 as numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+ CAST(col7 as numeric(10,1) DEFAULT NULL ON CONVERSION ERROR) as to_numeric_scale
+FROM safecast;
+
+SELECT col3 as numeric,
+ CAST(col3 AS int2 DEFAULT NULL ON CONVERSION ERROR) as to_int2,
+ CAST(col3 AS int4 DEFAULT NULL ON CONVERSION ERROR) as to_int4,
+ CAST(col3 AS oid DEFAULT NULL ON CONVERSION ERROR) as to_oid,
+ CAST(col3 as int8 DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+ CAST(col3 as float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+ CAST(col3 as float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+ CAST(col3 as numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+ CAST(col3 as numeric(10,1) DEFAULT NULL ON CONVERSION ERROR) as to_numeric_scale
+FROM safecast;
+
+SELECT col4 as num_arr,
+ CAST(col4 AS int2[] DEFAULT NULL ON CONVERSION ERROR) as int2arr,
+ CAST(col4 AS int4[] DEFAULT NULL ON CONVERSION ERROR) as int4arr,
+ CAST(col4 as int8[] DEFAULT NULL ON CONVERSION ERROR) as int8arr,
+ CAST(col4 as float4[] DEFAULT NULL ON CONVERSION ERROR) as f4arr,
+ CAST(col4 as float8[] DEFAULT NULL ON CONVERSION ERROR) as f8arr,
+ CAST(col4 as numeric(10,1)[] DEFAULT NULL ON CONVERSION ERROR) as numarr
+FROM safecast;
+
+SELECT col1 as float4,
+ CAST(col1 AS int2 DEFAULT NULL ON CONVERSION ERROR) as to_int2,
+ CAST(col1 AS int4 DEFAULT NULL ON CONVERSION ERROR) as to_int4,
+ CAST(col1 AS oid DEFAULT NULL ON CONVERSION ERROR) as to_oid,
+ CAST(col1 as int8 DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+ CAST(col1 as float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+ CAST(col1 as float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+ CAST(col1 as numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+ CAST(col1 as numeric(10,1) DEFAULT NULL ON CONVERSION ERROR) as to_numeric_scale
+FROM safecast;
+
+SELECT col2 as float8,
+ CAST(col2 AS int2 DEFAULT NULL ON CONVERSION ERROR) as to_int2,
+ CAST(col2 AS int4 DEFAULT NULL ON CONVERSION ERROR) as to_int4,
+ CAST(col2 AS oid DEFAULT NULL ON CONVERSION ERROR) as to_oid,
+ CAST(col2 as int8 DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+ CAST(col2 as float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+ CAST(col2 as float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+ CAST(col2 as numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+ CAST(col2 as numeric(10,1) DEFAULT NULL ON CONVERSION ERROR) as to_numeric_scale
+FROM safecast;
+
+--test date/timestamp/interval realted cast
+CREATE TABLE safecast1(col0 date, col1 timestamp, col2 timestamptz, col3 interval, col4 time, col5 timetz);
+insert into safecast1 VALUES
+('-infinity', '-infinity', '-infinity', '-infinity',
+ '2003-03-07 15:36:39 America/New_York', '2003-07-07 15:36:39 America/New_York'),
+('-infinity', 'infinity', 'infinity', 'infinity',
+ '11:59:59.99 PM', '11:59:59.99 PM PDT');
+
+SELECT col0 as date,
+ CAST(col0 AS timestamptz DEFAULT NULL ON CONVERSION ERROR) as to_timestamptz,
+ CAST(col0 AS date DEFAULT NULL ON CONVERSION ERROR) as to_date,
+ CAST(col0 AS time DEFAULT NULL ON CONVERSION ERROR) as to_times,
+ CAST(col0 AS timetz DEFAULT NULL ON CONVERSION ERROR) as to_timetz,
+ CAST(col0 AS timestamp(2) DEFAULT NULL ON CONVERSION ERROR) as to_timestamp_scale
+FROM safecast1;
+
+SELECT col1 as timestamp,
+ CAST(col1 AS timestamptz DEFAULT NULL ON CONVERSION ERROR) as to_timestamptz,
+ CAST(col1 AS date DEFAULT NULL ON CONVERSION ERROR) as to_date,
+ CAST(col1 AS time DEFAULT NULL ON CONVERSION ERROR) as to_times,
+ CAST(col1 AS timetz DEFAULT NULL ON CONVERSION ERROR) as to_timetz,
+ CAST(col1 AS timestamp(2) DEFAULT NULL ON CONVERSION ERROR) as to_timestamp_scale
+FROM safecast1;
+
+SELECT col2 as timestamptz,
+ CAST(col2 AS timestamptz DEFAULT NULL ON CONVERSION ERROR) as to_timestamptz,
+ CAST(col2 AS date DEFAULT NULL ON CONVERSION ERROR) as to_date,
+ CAST(col2 AS time DEFAULT NULL ON CONVERSION ERROR) as to_times,
+ CAST(col2 AS timetz DEFAULT NULL ON CONVERSION ERROR) as to_timetz,
+ CAST(col2 AS timestamp(2) DEFAULT NULL ON CONVERSION ERROR) as to_timestamp_scale
+FROM safecast1;
+
+SELECT col3 as interval,
+ CAST(col3 AS timestamptz DEFAULT NULL ON CONVERSION ERROR) as to_timestamptz,
+ CAST(col3 AS date DEFAULT NULL ON CONVERSION ERROR) as to_date,
+ CAST(col3 AS time DEFAULT NULL ON CONVERSION ERROR) as to_times,
+ CAST(col3 AS timetz DEFAULT NULL ON CONVERSION ERROR) as to_timetz,
+ CAST(col3 AS timestamp(2) DEFAULT NULL ON CONVERSION ERROR) as to_timestamp_scale,
+ CAST(col3 AS interval(2) DEFAULT NULL ON CONVERSION ERROR) as to_interval_scale
+FROM safecast1;
+
+SELECT col4 as time,
+ CAST(col4 AS time(2) DEFAULT NULL ON CONVERSION ERROR) as to_times,
+ CAST(col4 AS timetz DEFAULT NULL ON CONVERSION ERROR) as to_timetz,
+ CAST(col4 AS interval(2) DEFAULT NULL ON CONVERSION ERROR) as to_interval,
+ CAST(col4 AS interval(2) DEFAULT NULL ON CONVERSION ERROR) as to_interval_scale
+FROM safecast1;
+
+SELECT col5 as timetz,
+ CAST(col5 AS time DEFAULT NULL ON CONVERSION ERROR) as to_time,
+ CAST(col5 AS time(2) DEFAULT NULL ON CONVERSION ERROR) as to_time_scale,
+ CAST(col5 AS timetz DEFAULT NULL ON CONVERSION ERROR) as to_timetz,
+ CAST(col5 AS timetz(6) DEFAULT NULL ON CONVERSION ERROR) as to_timetz_scale,
+ CAST(col5 AS interval DEFAULT NULL ON CONVERSION ERROR) as to_interval,
+ CAST(col5 AS interval(2) DEFAULT NULL ON CONVERSION ERROR) as to_interval_scale
+FROM safecast1;
+
+CREATE TEMP TABLE test_safecast1(col0 jsonb);
+INSERT INTO test_safecast1(col0) VALUES ('"test"');
+SELECT col0 as jsonb,
+ CAST(col0 AS integer DEFAULT NULL ON CONVERSION ERROR) as to_integer,
+ CAST(col0 AS numeric DEFAULT NULL ON CONVERSION ERROR) as to_numeric,
+ CAST(col0 AS bigint DEFAULT NULL ON CONVERSION ERROR) as to_int8,
+ CAST(col0 AS float4 DEFAULT NULL ON CONVERSION ERROR) as to_float4,
+ CAST(col0 AS float8 DEFAULT NULL ON CONVERSION ERROR) as to_float8,
+ CAST(col0 AS boolean DEFAULT NULL ON CONVERSION ERROR) as to_bool,
+ CAST(col0 AS smallint DEFAULT NULL ON CONVERSION ERROR) as to_smallint
+FROM test_safecast1;
+
+-- test deparse
+CREATE DOMAIN d_int_arr as int[];
+CREATE VIEW safecastview AS
+SELECT CAST('1234' as char(3) DEFAULT -1111 ON CONVERSION ERROR),
+ CAST(1 as date DEFAULT ((now()::date + random(min=>1, max=>1::int))) ON CONVERSION ERROR) as safecast,
+ CAST(ARRAY[['1'], ['three'],['a']] AS INTEGER[] DEFAULT '{1,2}' ON CONVERSION ERROR) as cast1,
+ CAST(ARRAY[['1', '2'], ['three', 'a']] AS text[] DEFAULT '{21,22}' ON CONVERSION ERROR) as cast2;
+\sv safecastview
+
+CREATE VIEW safecastview1 AS
+SELECT CAST(ARRAY[['1'], ['three'],['a']] AS d_int_arr DEFAULT '{1,2}' ON CONVERSION ERROR) as cast1,
+ CAST(ARRAY[['1', '2'], ['three', 'a']] AS d_int_arr DEFAULT '{21,22}' ON CONVERSION ERROR) as cast2;
+\sv safecastview1
+SELECT * FROM safecastview1;
+
+CREATE INDEX cast_error_idx ON tcast((cast(c as int DEFAULT random(min=>1, max=>1) ON CONVERSION ERROR))); --error
+RESET extra_float_digits;
diff --git a/src/test/regress/sql/create_cast.sql b/src/test/regress/sql/create_cast.sql
index 32187853cc7..0a15a795d87 100644
--- a/src/test/regress/sql/create_cast.sql
+++ b/src/test/regress/sql/create_cast.sql
@@ -62,6 +62,7 @@ $$ SELECT ('bar'::text || $1::text); $$;
CREATE CAST (int4 AS casttesttype) WITH FUNCTION bar_int4_text(int4) AS IMPLICIT;
SELECT 1234::int4::casttesttype; -- Should work now
+SELECT CAST(1234::int4 AS casttesttype DEFAULT NULL ON CONVERSION ERROR); -- error
-- check dependencies generated for that
SELECT pg_describe_object(classid, objid, objsubid) as obj,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 5290b91e83e..7996854b3a5 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2660,6 +2660,9 @@ STRLEN
SV
SYNCHRONIZATION_BARRIER
SYSTEM_INFO
+SafeTypeCast
+SafeTypeCastExpr
+SafeTypeCastState
SampleScan
SampleScanGetSampleSize_function
SampleScanState
--
2.34.1