0003-CAST-ON-DEFAULT-work-in-progress.patch
text/x-patch
Filename: 0003-CAST-ON-DEFAULT-work-in-progress.patch
Type: text/x-patch
Part: 2
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 0003
Subject: CAST ON DEFAULT work in progress
| File | + | − |
|---|---|---|
| src/backend/executor/execExpr.c | 107 | 66 |
| src/backend/executor/execExprInterp.c | 33 | 2 |
| src/backend/jit/llvm/llvmjit_expr.c | 15 | 0 |
| src/backend/nodes/makefuncs.c | 3 | 1 |
| src/backend/nodes/nodeFuncs.c | 9 | 6 |
| src/backend/optimizer/util/clauses.c | 3 | 1 |
| src/backend/parser/gram.y | 39 | 7 |
| src/backend/parser/parse_agg.c | 10 | 5 |
| src/backend/parser/parse_coerce.c | 169 | 46 |
| src/backend/parser/parse_expr.c | 69 | 18 |
| src/backend/partitioning/partbounds.c | 2 | 1 |
| src/backend/rewrite/rewriteSearchCycle.c | 2 | 2 |
| src/include/executor/execExpr.h | 4 | 0 |
| src/include/nodes/execnodes.h | 4 | 0 |
| src/include/nodes/makefuncs.h | 2 | 1 |
| src/include/nodes/parsenodes.h | 1 | 0 |
| src/include/nodes/primnodes.h | 11 | 1 |
| src/include/parser/kwlist.h | 1 | 0 |
| src/include/parser/parse_coerce.h | 15 | 0 |
| src/test/regress/expected/cast.out | 40 | 0 |
| src/test/regress/parallel_schedule | 1 | 1 |
| src/test/regress/sql/cast.sql | 11 | 0 |
From 74aaf068a2d071f6fefab27309d7a0252f28ccee Mon Sep 17 00:00:00 2001
From: coreyhuinker <corey.huinker@gmail.com>
Date: Mon, 19 Dec 2022 17:11:49 -0500
Subject: [PATCH 3/3] CAST ON DEFAULT work in progress
---
src/backend/executor/execExpr.c | 173 +++++++++++-------
src/backend/executor/execExprInterp.c | 35 +++-
src/backend/jit/llvm/llvmjit_expr.c | 15 ++
src/backend/nodes/makefuncs.c | 4 +-
src/backend/nodes/nodeFuncs.c | 15 +-
src/backend/optimizer/util/clauses.c | 4 +-
src/backend/parser/gram.y | 46 ++++-
src/backend/parser/parse_agg.c | 15 +-
src/backend/parser/parse_coerce.c | 215 ++++++++++++++++++-----
src/backend/parser/parse_expr.c | 87 +++++++--
src/backend/partitioning/partbounds.c | 3 +-
src/backend/rewrite/rewriteSearchCycle.c | 4 +-
src/include/executor/execExpr.h | 4 +
src/include/nodes/execnodes.h | 4 +
src/include/nodes/makefuncs.h | 3 +-
src/include/nodes/parsenodes.h | 1 +
src/include/nodes/primnodes.h | 12 +-
src/include/parser/kwlist.h | 1 +
src/include/parser/parse_coerce.h | 15 ++
src/test/regress/expected/cast.out | 40 +++++
src/test/regress/parallel_schedule | 2 +-
src/test/regress/sql/cast.sql | 11 ++
22 files changed, 551 insertions(+), 158 deletions(-)
create mode 100644 src/test/regress/expected/cast.out
create mode 100644 src/test/regress/sql/cast.sql
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 81429b9f05..9aaa53b67e 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -40,6 +40,7 @@
#include "jit/jit.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
+#include "nodes/miscnodes.h"
#include "nodes/nodeFuncs.h"
#include "nodes/subscripting.h"
#include "optimizer/optimizer.h"
@@ -61,10 +62,10 @@ typedef struct LastAttnumInfo
static void ExecReadyExpr(ExprState *state);
static void ExecInitExprRec(Expr *node, ExprState *state,
- Datum *resv, bool *resnull);
+ Datum *resv, bool *resnull, bool *reserror);
static void ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args,
Oid funcid, Oid inputcollid,
- ExprState *state);
+ ExprState *state, ErrorSaveContext *escontext);
static void ExecInitExprSlots(ExprState *state, Node *node);
static void ExecPushExprSlots(ExprState *state, LastAttnumInfo *info);
static bool get_last_attnums_walker(Node *node, LastAttnumInfo *info);
@@ -74,11 +75,11 @@ static void ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable,
static void ExecInitSubscriptingRef(ExprEvalStep *scratch,
SubscriptingRef *sbsref,
ExprState *state,
- Datum *resv, bool *resnull);
+ Datum *resv, bool *resnull, bool *reserror);
static bool isAssignmentIndirectionExpr(Expr *expr);
static void ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
ExprState *state,
- Datum *resv, bool *resnull);
+ Datum *resv, bool *resnull, bool *reserror);
static void ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
ExprEvalStep *scratch,
FunctionCallInfo fcinfo, AggStatePerTrans pertrans,
@@ -140,7 +141,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
ExecInitExprSlots(state, (Node *) node);
/* Compile the expression proper */
- ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
+ ExecInitExprRec(node, state, &state->resvalue, &state->resnull, &state->reserror);
/* Finally, append a DONE step */
scratch.opcode = EEOP_DONE;
@@ -177,7 +178,7 @@ ExecInitExprWithParams(Expr *node, ParamListInfo ext_params)
ExecInitExprSlots(state, (Node *) node);
/* Compile the expression proper */
- ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
+ ExecInitExprRec(node, state, &state->resvalue, &state->resnull, &state->reserror);
/* Finally, append a DONE step */
scratch.opcode = EEOP_DONE;
@@ -251,7 +252,7 @@ ExecInitQual(List *qual, PlanState *parent)
Expr *node = (Expr *) lfirst(lc);
/* first evaluate expression */
- ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
+ ExecInitExprRec(node, state, &state->resvalue, &state->resnull, &state->reserror);
/* then emit EEOP_QUAL to detect if it's false (or null) */
scratch.d.qualexpr.jumpdone = -1;
@@ -455,7 +456,7 @@ ExecBuildProjectionInfo(List *targetList,
* into the ExprState's resvalue/resnull and then move.
*/
ExecInitExprRec(tle->expr, state,
- &state->resvalue, &state->resnull);
+ &state->resvalue, &state->resnull, &state->reserror);
/*
* Column might be referenced multiple times in upper nodes, so
@@ -659,7 +660,7 @@ ExecBuildUpdateProjection(List *targetList,
* path and it doesn't seem worth expending code for that.
*/
ExecInitExprRec(tle->expr, state,
- &state->resvalue, &state->resnull);
+ &state->resvalue, &state->resnull, &state->reserror);
/* Needn't worry about read-only-ness here, either. */
scratch.opcode = EEOP_ASSIGN_TMP;
scratch.d.assign_tmp.resultnum = targetattnum - 1;
@@ -688,7 +689,7 @@ ExecBuildUpdateProjection(List *targetList,
Assert(tle->resjunk);
ExecInitExprRec(tle->expr, state,
- &state->resvalue, &state->resnull);
+ &state->resvalue, &state->resnull, &state->reserror);
}
}
@@ -899,7 +900,7 @@ ExecReadyExpr(ExprState *state)
*/
static void
ExecInitExprRec(Expr *node, ExprState *state,
- Datum *resv, bool *resnull)
+ Datum *resv, bool *resnull, bool *reserror)
{
ExprEvalStep scratch = {0};
@@ -910,6 +911,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
Assert(resv != NULL && resnull != NULL);
scratch.resvalue = resv;
scratch.resnull = resnull;
+ scratch.reserror = reserror;
/* cases should be ordered as they are in enum NodeTag */
switch (nodeTag(node))
@@ -1126,17 +1128,24 @@ ExecInitExprRec(Expr *node, ExprState *state,
{
SubscriptingRef *sbsref = (SubscriptingRef *) node;
- ExecInitSubscriptingRef(&scratch, sbsref, state, resv, resnull);
+ ExecInitSubscriptingRef(&scratch, sbsref, state, resv, resnull, NULL);
break;
}
case T_FuncExpr:
{
- FuncExpr *func = (FuncExpr *) node;
+ FuncExpr *func = (FuncExpr *) node;
+ ErrorSaveContext *escontext = NULL;
+
+ if (unlikely(func->safe_mode))
+ {
+ escontext = palloc0(sizeof(ErrorSaveContext));
+ escontext->type = T_ErrorSaveContext;
+ }
ExecInitFunc(&scratch, node,
func->args, func->funcid, func->inputcollid,
- state);
+ state, escontext);
ExprEvalPushStep(state, &scratch);
break;
}
@@ -1147,7 +1156,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
ExecInitFunc(&scratch, node,
op->args, op->opfuncid, op->inputcollid,
- state);
+ state, NULL);
ExprEvalPushStep(state, &scratch);
break;
}
@@ -1158,7 +1167,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
ExecInitFunc(&scratch, node,
op->args, op->opfuncid, op->inputcollid,
- state);
+ state, NULL);
/*
* Change opcode of call instruction to EEOP_DISTINCT.
@@ -1180,7 +1189,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
ExecInitFunc(&scratch, node,
op->args, op->opfuncid, op->inputcollid,
- state);
+ state, NULL);
/*
* Change opcode of call instruction to EEOP_NULLIF.
@@ -1263,7 +1272,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
{
/* Evaluate scalar directly into left function argument */
ExecInitExprRec(scalararg, state,
- &fcinfo->args[0].value, &fcinfo->args[0].isnull);
+ &fcinfo->args[0].value, &fcinfo->args[0].isnull, NULL);
/*
* Evaluate array argument into our return value. There's
@@ -1272,7 +1281,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
* EEOP_HASHED_SCALARARRAYOP, and will not be passed to
* any other expression.
*/
- ExecInitExprRec(arrayarg, state, resv, resnull);
+ ExecInitExprRec(arrayarg, state, resv, resnull, NULL);
/* And perform the operation */
scratch.opcode = EEOP_HASHED_SCALARARRAYOP;
@@ -1289,7 +1298,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
/* Evaluate scalar directly into left function argument */
ExecInitExprRec(scalararg, state,
&fcinfo->args[0].value,
- &fcinfo->args[0].isnull);
+ &fcinfo->args[0].isnull, NULL);
/*
* Evaluate array argument into our return value. There's
@@ -1297,7 +1306,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
* guaranteed to be overwritten by EEOP_SCALARARRAYOP, and
* will not be passed to any other expression.
*/
- ExecInitExprRec(arrayarg, state, resv, resnull);
+ ExecInitExprRec(arrayarg, state, resv, resnull, NULL);
/* And perform the operation */
scratch.opcode = EEOP_SCALARARRAYOP;
@@ -1342,7 +1351,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
Expr *arg = (Expr *) lfirst(lc);
/* Evaluate argument into our output variable */
- ExecInitExprRec(arg, state, resv, resnull);
+ ExecInitExprRec(arg, state, resv, resnull, NULL);
/* Perform the appropriate step type */
switch (boolexpr->boolop)
@@ -1423,7 +1432,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
FieldSelect *fselect = (FieldSelect *) node;
/* evaluate row/record argument into result area */
- ExecInitExprRec(fselect->arg, state, resv, resnull);
+ ExecInitExprRec(fselect->arg, state, resv, resnull, NULL);
/* and extract field */
scratch.opcode = EEOP_FIELDSELECT;
@@ -1460,7 +1469,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
rowcachep->cacheptr = NULL;
/* emit code to evaluate the composite input value */
- ExecInitExprRec(fstore->arg, state, resv, resnull);
+ ExecInitExprRec(fstore->arg, state, resv, resnull, NULL);
/* next, deform the input tuple into our workspace */
scratch.opcode = EEOP_FIELDSTORE_DEFORM;
@@ -1514,7 +1523,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
ExecInitExprRec(e, state,
&values[fieldnum - 1],
- &nulls[fieldnum - 1]);
+ &nulls[fieldnum - 1], NULL);
state->innermost_caseval = save_innermost_caseval;
state->innermost_casenull = save_innermost_casenull;
@@ -1536,7 +1545,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
/* relabel doesn't need to do anything at runtime */
RelabelType *relabel = (RelabelType *) node;
- ExecInitExprRec(relabel->arg, state, resv, resnull);
+ ExecInitExprRec(relabel->arg, state, resv, resnull, NULL);
break;
}
@@ -1547,9 +1556,10 @@ ExecInitExprRec(Expr *node, ExprState *state,
bool typisvarlena;
Oid typioparam;
FunctionCallInfo fcinfo_in;
+ ErrorSaveContext *escontext = NULL;
/* evaluate argument into step's result area */
- ExecInitExprRec(iocoerce->arg, state, resv, resnull);
+ ExecInitExprRec(iocoerce->arg, state, resv, resnull, reserror);
/*
* Prepare both output and input function calls, to be
@@ -1581,9 +1591,17 @@ ExecInitExprRec(Expr *node, ExprState *state,
&iofunc, &typioparam);
fmgr_info(iofunc, scratch.d.iocoerce.finfo_in);
fmgr_info_set_expr((Node *) node, scratch.d.iocoerce.finfo_in);
+
+ /* if this is a safe-mode coerce */
+ if (unlikely(iocoerce->safe_mode))
+ {
+ escontext = palloc0(sizeof(ErrorSaveContext));
+ escontext->type = T_ErrorSaveContext;
+ }
+
InitFunctionCallInfoData(*scratch.d.iocoerce.fcinfo_data_in,
scratch.d.iocoerce.finfo_in,
- 3, InvalidOid, NULL, NULL);
+ 3, InvalidOid, (fmNodePtr) escontext, NULL);
/*
* We can preload the second and third arguments for the input
@@ -1606,7 +1624,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
ExprState *elemstate;
/* evaluate argument into step's result area */
- ExecInitExprRec(acoerce->arg, state, resv, resnull);
+ ExecInitExprRec(acoerce->arg, state, resv, resnull, reserror);
resultelemtype = get_element_type(acoerce->resulttype);
if (!OidIsValid(resultelemtype))
@@ -1627,9 +1645,11 @@ ExecInitExprRec(Expr *node, ExprState *state,
elemstate->innermost_caseval = (Datum *) palloc(sizeof(Datum));
elemstate->innermost_casenull = (bool *) palloc(sizeof(bool));
+ elemstate->innermost_caseerror = (bool *) palloc(sizeof(bool));
ExecInitExprRec(acoerce->elemexpr, elemstate,
- &elemstate->resvalue, &elemstate->resnull);
+ &elemstate->resvalue, &elemstate->resnull,
+ &elemstate->reserror);
if (elemstate->steps_len == 1 &&
elemstate->steps[0].opcode == EEOP_CASE_TESTVAL)
@@ -1677,7 +1697,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
rowcachep[1].cacheptr = NULL;
/* evaluate argument into step's result area */
- ExecInitExprRec(convert->arg, state, resv, resnull);
+ ExecInitExprRec(convert->arg, state, resv, resnull, NULL);
/* and push conversion step */
scratch.opcode = EEOP_CONVERT_ROWTYPE;
@@ -1699,6 +1719,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
List *adjust_jumps = NIL;
Datum *caseval = NULL;
bool *casenull = NULL;
+ bool *caseerror = NULL;
ListCell *lc;
/*
@@ -1711,9 +1732,10 @@ ExecInitExprRec(Expr *node, ExprState *state,
/* Evaluate testexpr into caseval/casenull workspace */
caseval = palloc(sizeof(Datum));
casenull = palloc(sizeof(bool));
+ caseerror = palloc(sizeof(bool));
ExecInitExprRec(caseExpr->arg, state,
- caseval, casenull);
+ caseval, casenull, caseerror);
/*
* Since value might be read multiple times, force to R/O
@@ -1725,8 +1747,10 @@ ExecInitExprRec(Expr *node, ExprState *state,
scratch.opcode = EEOP_MAKE_READONLY;
scratch.resvalue = caseval;
scratch.resnull = casenull;
+ scratch.reserror = caseerror;
scratch.d.make_readonly.value = caseval;
scratch.d.make_readonly.isnull = casenull;
+ scratch.d.make_readonly.iserror = caseerror;
ExprEvalPushStep(state, &scratch);
/* restore normal settings of scratch fields */
scratch.resvalue = resv;
@@ -1745,6 +1769,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
CaseWhen *when = (CaseWhen *) lfirst(lc);
Datum *save_innermost_caseval;
bool *save_innermost_casenull;
+ bool *save_innermost_caseerror;
int whenstep;
/*
@@ -1759,14 +1784,17 @@ ExecInitExprRec(Expr *node, ExprState *state,
*/
save_innermost_caseval = state->innermost_caseval;
save_innermost_casenull = state->innermost_casenull;
+ save_innermost_caseerror = state->innermost_caseerror;
state->innermost_caseval = caseval;
state->innermost_casenull = casenull;
+ state->innermost_caseerror = caseerror;
/* evaluate condition into CASE's result variables */
- ExecInitExprRec(when->expr, state, resv, resnull);
+ ExecInitExprRec(when->expr, state, resv, resnull, NULL);
state->innermost_caseval = save_innermost_caseval;
state->innermost_casenull = save_innermost_casenull;
+ state->innermost_caseerror = save_innermost_caseerror;
/* If WHEN result isn't true, jump to next CASE arm */
scratch.opcode = EEOP_JUMP_IF_NOT_TRUE;
@@ -1778,7 +1806,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
* If WHEN result is true, evaluate THEN result, storing
* it into the CASE's result variables.
*/
- ExecInitExprRec(when->result, state, resv, resnull);
+ ExecInitExprRec(when->result, state, resv, resnull, reserror);
/* Emit JUMP step to jump to end of CASE's code */
scratch.opcode = EEOP_JUMP;
@@ -1804,7 +1832,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
/* evaluate ELSE expr into CASE's result variables */
ExecInitExprRec(caseExpr->defresult, state,
- resv, resnull);
+ resv, resnull, reserror);
/* adjust jump targets */
foreach(lc, adjust_jumps)
@@ -1833,6 +1861,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
scratch.opcode = EEOP_CASE_TESTVAL;
scratch.d.casetest.value = state->innermost_caseval;
scratch.d.casetest.isnull = state->innermost_casenull;
+ scratch.d.casetest.iserror = state->innermost_caseerror;
ExprEvalPushStep(state, &scratch);
break;
@@ -1875,7 +1904,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
ExecInitExprRec(e, state,
&scratch.d.arrayexpr.elemvalues[elemoff],
- &scratch.d.arrayexpr.elemnulls[elemoff]);
+ &scratch.d.arrayexpr.elemnulls[elemoff], NULL);
elemoff++;
}
@@ -1969,7 +1998,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
/* Evaluate column expr into appropriate workspace slot */
ExecInitExprRec(e, state,
&scratch.d.row.elemvalues[i],
- &scratch.d.row.elemnulls[i]);
+ &scratch.d.row.elemnulls[i], NULL);
i++;
}
@@ -2047,9 +2076,9 @@ ExecInitExprRec(Expr *node, ExprState *state,
/* evaluate left and right args directly into fcinfo */
ExecInitExprRec(left_expr, state,
- &fcinfo->args[0].value, &fcinfo->args[0].isnull);
+ &fcinfo->args[0].value, &fcinfo->args[0].isnull, NULL);
ExecInitExprRec(right_expr, state,
- &fcinfo->args[1].value, &fcinfo->args[1].isnull);
+ &fcinfo->args[1].value, &fcinfo->args[1].isnull, NULL);
scratch.opcode = EEOP_ROWCOMPARE_STEP;
scratch.d.rowcompare_step.finfo = finfo;
@@ -2104,6 +2133,13 @@ ExecInitExprRec(Expr *node, ExprState *state,
CoalesceExpr *coalesce = (CoalesceExpr *) node;
List *adjust_jumps = NIL;
ListCell *lc;
+ ExprEvalOp jump_test_opcode;
+
+ /* Coalesce can handle resnull and reserror tests */
+ if (likely(coalesce->op == NULL_TEST))
+ jump_test_opcode = EEOP_JUMP_IF_NOT_NULL;
+ else
+ jump_test_opcode = EEOP_JUMP_IF_NOT_ERROR;
/* We assume there's at least one arg */
Assert(coalesce->args != NIL);
@@ -2117,10 +2153,10 @@ ExecInitExprRec(Expr *node, ExprState *state,
Expr *e = (Expr *) lfirst(lc);
/* evaluate argument, directly into result datum */
- ExecInitExprRec(e, state, resv, resnull);
+ ExecInitExprRec(e, state, resv, resnull, reserror);
- /* if it's not null, skip to end of COALESCE expr */
- scratch.opcode = EEOP_JUMP_IF_NOT_NULL;
+ /* if it's not null/error, skip to end of COALESCE expr */
+ scratch.opcode = jump_test_opcode;
scratch.d.jump.jumpdone = -1; /* adjust later */
ExprEvalPushStep(state, &scratch);
@@ -2139,7 +2175,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
{
ExprEvalStep *as = &state->steps[lfirst_int(lc)];
- Assert(as->opcode == EEOP_JUMP_IF_NOT_NULL);
+ Assert(as->opcode == jump_test_opcode);
Assert(as->d.jump.jumpdone == -1);
as->d.jump.jumpdone = state->steps_len;
}
@@ -2201,7 +2237,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
ExecInitExprRec(e, state,
&scratch.d.minmax.values[off],
- &scratch.d.minmax.nulls[off]);
+ &scratch.d.minmax.nulls[off], NULL);
off++;
}
@@ -2256,7 +2292,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
ExecInitExprRec(e, state,
&scratch.d.xmlexpr.named_argvalue[off],
- &scratch.d.xmlexpr.named_argnull[off]);
+ &scratch.d.xmlexpr.named_argnull[off], NULL);
off++;
}
@@ -2267,7 +2303,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
ExecInitExprRec(e, state,
&scratch.d.xmlexpr.argvalue[off],
- &scratch.d.xmlexpr.argnull[off]);
+ &scratch.d.xmlexpr.argnull[off], NULL);
off++;
}
@@ -2304,7 +2340,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
/* first evaluate argument into result variable */
ExecInitExprRec(ntest->arg, state,
- resv, resnull);
+ resv, resnull, NULL);
/* then push the test of that argument */
ExprEvalPushStep(state, &scratch);
@@ -2321,7 +2357,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
* and will get overwritten by the below EEOP_BOOLTEST_IS_*
* step.
*/
- ExecInitExprRec(btest->arg, state, resv, resnull);
+ ExecInitExprRec(btest->arg, state, resv, resnull, NULL);
switch (btest->booltesttype)
{
@@ -2359,7 +2395,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
CoerceToDomain *ctest = (CoerceToDomain *) node;
ExecInitCoerceToDomain(&scratch, ctest, state,
- resv, resnull);
+ resv, resnull, NULL);
break;
}
@@ -2442,7 +2478,7 @@ ExprEvalPushStep(ExprState *es, const ExprEvalStep *s)
*/
static void
ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
- Oid inputcollid, ExprState *state)
+ Oid inputcollid, ExprState *state, ErrorSaveContext *escontext)
{
int nargs = list_length(args);
AclResult aclresult;
@@ -2483,7 +2519,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, (fmNodePtr) escontext, NULL);
/* Keep extra copies of this info to save an indirection at runtime */
scratch->d.func.fn_addr = flinfo->fn_addr;
@@ -2519,7 +2555,7 @@ ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
{
ExecInitExprRec(arg, state,
&fcinfo->args[argno].value,
- &fcinfo->args[argno].isnull);
+ &fcinfo->args[argno].isnull, NULL);
}
argno++;
}
@@ -2570,6 +2606,7 @@ ExecPushExprSlots(ExprState *state, LastAttnumInfo *info)
scratch.resvalue = NULL;
scratch.resnull = NULL;
+ scratch.reserror = NULL;
/* Emit steps as needed */
if (info->last_inner > 0)
@@ -2834,7 +2871,7 @@ ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable, ExprState *state)
*/
static void
ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
- ExprState *state, Datum *resv, bool *resnull)
+ ExprState *state, Datum *resv, bool *resnull, bool *reserror)
{
bool isAssignment = (sbsref->refassgnexpr != NULL);
int nupper = list_length(sbsref->refupperindexpr);
@@ -2897,7 +2934,7 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
* be overwritten by the final EEOP_SBSREF_FETCH/ASSIGN step, which is
* pushed last.
*/
- ExecInitExprRec(sbsref->refexpr, state, resv, resnull);
+ ExecInitExprRec(sbsref->refexpr, state, resv, resnull, NULL);
/*
* If refexpr yields NULL, and the operation should be strict, then result
@@ -2931,7 +2968,7 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
/* Each subscript is evaluated into appropriate array entry */
ExecInitExprRec(e, state,
&sbsrefstate->upperindex[i],
- &sbsrefstate->upperindexnull[i]);
+ &sbsrefstate->upperindexnull[i], NULL);
}
i++;
}
@@ -2954,7 +2991,7 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
/* Each subscript is evaluated into appropriate array entry */
ExecInitExprRec(e, state,
&sbsrefstate->lowerindex[i],
- &sbsrefstate->lowerindexnull[i]);
+ &sbsrefstate->lowerindexnull[i], NULL);
}
i++;
}
@@ -3018,7 +3055,7 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
/* evaluate replacement value into replacevalue/replacenull */
ExecInitExprRec(sbsref->refassgnexpr, state,
- &sbsrefstate->replacevalue, &sbsrefstate->replacenull);
+ &sbsrefstate->replacevalue, &sbsrefstate->replacenull, NULL);
state->innermost_caseval = save_innermost_caseval;
state->innermost_casenull = save_innermost_casenull;
@@ -3107,11 +3144,12 @@ isAssignmentIndirectionExpr(Expr *expr)
*/
static void
ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
- ExprState *state, Datum *resv, bool *resnull)
+ ExprState *state, Datum *resv, bool *resnull, bool *reserror)
{
DomainConstraintRef *constraint_ref;
Datum *domainval = NULL;
bool *domainnull = NULL;
+ bool *domainerror = NULL;
ListCell *l;
scratch->d.domaincheck.resulttype = ctest->resulttype;
@@ -3124,7 +3162,7 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
* if there's constraint failures there'll be errors, otherwise it's what
* needs to be returned.
*/
- ExecInitExprRec(ctest->arg, state, resv, resnull);
+ ExecInitExprRec(ctest->arg, state, resv, resnull, NULL);
/*
* Note: if the argument is of varlena type, it could be a R/W expanded
@@ -3196,11 +3234,13 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
/* Yes, so make output workspace for MAKE_READONLY */
domainval = (Datum *) palloc(sizeof(Datum));
domainnull = (bool *) palloc(sizeof(bool));
+ domainerror = (bool *) palloc(sizeof(bool));
/* Emit MAKE_READONLY */
scratch2.opcode = EEOP_MAKE_READONLY;
scratch2.resvalue = domainval;
scratch2.resnull = domainnull;
+ scratch2.reserror = domainerror;
scratch2.d.make_readonly.value = resv;
scratch2.d.make_readonly.isnull = resnull;
ExprEvalPushStep(state, &scratch2);
@@ -3227,7 +3267,7 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
/* evaluate check expression value */
ExecInitExprRec(con->check_expr, state,
scratch->d.domaincheck.checkvalue,
- scratch->d.domaincheck.checknull);
+ scratch->d.domaincheck.checknull, NULL);
state->innermost_domainval = save_innermost_domainval;
state->innermost_domainnull = save_innermost_domainnull;
@@ -3319,7 +3359,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
{
/* evaluate filter expression */
ExecInitExprRec(pertrans->aggref->aggfilter, state,
- &state->resvalue, &state->resnull);
+ &state->resvalue, &state->resnull, NULL);
/* and jump out if false */
scratch.opcode = EEOP_JUMP_IF_NOT_TRUE;
scratch.d.jump.jumpdone = -1; /* adjust later */
@@ -3359,7 +3399,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
*/
ExecInitExprRec(source_tle->expr, state,
&trans_fcinfo->args[argno + 1].value,
- &trans_fcinfo->args[argno + 1].isnull);
+ &trans_fcinfo->args[argno + 1].isnull, NULL);
}
else
{
@@ -3368,7 +3408,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
/* evaluate argument */
ExecInitExprRec(source_tle->expr, state,
&ds_fcinfo->args[0].value,
- &ds_fcinfo->args[0].isnull);
+ &ds_fcinfo->args[0].isnull, NULL);
/* Dummy second argument for type-safety reasons */
ds_fcinfo->args[1].value = PointerGetDatum(NULL);
@@ -3430,7 +3470,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
*/
ExecInitExprRec(source_tle->expr, state,
&trans_fcinfo->args[argno + 1].value,
- &trans_fcinfo->args[argno + 1].isnull);
+ &trans_fcinfo->args[argno + 1].isnull, NULL);
argno++;
}
Assert(pertrans->numTransInputs == argno);
@@ -3448,7 +3488,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
ExecInitExprRec(source_tle->expr, state,
&state->resvalue,
- &state->resnull);
+ &state->resnull, NULL);
strictnulls = &state->resnull;
argno++;
@@ -3471,7 +3511,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
TargetEntry *source_tle = (TargetEntry *) lfirst(arg);
ExecInitExprRec(source_tle->expr, state,
- &values[argno], &nulls[argno]);
+ &values[argno], &nulls[argno], NULL);
argno++;
}
Assert(pertrans->numInputs == argno);
@@ -3585,6 +3625,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
scratch.resvalue = NULL;
scratch.resnull = NULL;
+ scratch.reserror = NULL;
scratch.opcode = EEOP_DONE;
ExprEvalPushStep(state, &scratch);
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 1dab2787b7..6cc6fa1717 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -64,6 +64,7 @@
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
+#include "nodes/miscnodes.h"
#include "parser/parsetree.h"
#include "pgstat.h"
#include "utils/array.h"
@@ -434,6 +435,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_JUMP,
&&CASE_EEOP_JUMP_IF_NULL,
&&CASE_EEOP_JUMP_IF_NOT_NULL,
+ &&CASE_EEOP_JUMP_IF_NOT_ERROR,
&&CASE_EEOP_JUMP_IF_NOT_TRUE,
&&CASE_EEOP_NULLTEST_ISNULL,
&&CASE_EEOP_NULLTEST_ISNOTNULL,
@@ -729,6 +731,13 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
*op->resvalue = d;
*op->resnull = fcinfo->isnull;
+ if (SOFT_ERROR_OCCURRED(fcinfo->context))
+ {
+ ErrorSaveContext *escontext = (ErrorSaveContext *) fcinfo->context;
+ escontext->error_occurred = false;
+ *op->reserror = true;
+ }
+
EEO_NEXT();
}
@@ -966,6 +975,21 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
+ EEO_CASE(EEOP_JUMP_IF_NOT_ERROR)
+ {
+ /* Transfer control if current result is non-error */
+ if (!*op->reserror)
+ {
+ *op->reserror = false;
+ EEO_JUMP(op->d.jump.jumpdone);
+ }
+
+ /* reset error flag */
+ *op->reserror = false;
+
+ EEO_NEXT();
+ }
+
EEO_CASE(EEOP_JUMP_IF_NOT_TRUE)
{
/* Transfer control if current result is null or false */
@@ -1181,10 +1205,17 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
fcinfo_in->isnull = false;
d = FunctionCallInvoke(fcinfo_in);
+
*op->resvalue = d;
- /* Should get null result if and only if str is NULL */
- if (str == NULL)
+ if (SOFT_ERROR_OCCURRED(fcinfo_in->context))
+ {
+ ErrorSaveContext *escontext = (ErrorSaveContext *) fcinfo_in->context;
+ escontext->error_occurred = false;
+ *op->reserror = true;
+ }
+ /* If no error, should get null result if and only if str is NULL */
+ else if (str == NULL)
{
Assert(*op->resnull);
Assert(fcinfo_in->isnull);
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index f114337f8e..f99b108ceb 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -919,6 +919,21 @@ llvm_compile_expr(ExprState *state)
break;
}
+ case EEOP_JUMP_IF_NOT_ERROR:
+ {
+ LLVMValueRef v_reserror;
+
+ /* Transfer control if current result is non-error */
+
+ v_resnull = LLVMBuildLoad(b, v_reserrorp, "");
+
+ LLVMBuildCondBr(b,
+ LLVMBuildICmp(b, LLVMIntEQ, v_reserror,
+ l_sbool_const(0), ""),
+ opblocks[op->d.jump.jumpdone],
+ opblocks[opno + 1]);
+ break;
+ }
case EEOP_JUMP_IF_NOT_TRUE:
{
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index c85d8fe975..229d1e84c8 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -517,7 +517,8 @@ makeColumnDef(const char *colname, Oid typeOid, int32 typmod, Oid collOid)
*/
FuncExpr *
makeFuncExpr(Oid funcid, Oid rettype, List *args,
- Oid funccollid, Oid inputcollid, CoercionForm fformat)
+ Oid funccollid, Oid inputcollid, CoercionForm fformat,
+ bool safe_mode)
{
FuncExpr *funcexpr;
@@ -530,6 +531,7 @@ makeFuncExpr(Oid funcid, Oid rettype, List *args,
funcexpr->funccollid = funccollid;
funcexpr->inputcollid = inputcollid;
funcexpr->args = args;
+ funcexpr->safe_mode = safe_mode;
funcexpr->location = -1;
return funcexpr;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index af8620ceb7..7bc3e13208 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -1522,13 +1522,16 @@ exprLocation(const Node *expr)
{
const TypeCast *tc = (const TypeCast *) expr;
- /*
- * This could represent CAST(), ::, or TypeName 'literal', so
- * any of the components might be leftmost.
- */
loc = exprLocation(tc->arg);
- loc = leftmostLoc(loc, tc->typeName->location);
- loc = leftmostLoc(loc, tc->location);
+ if (likely(!tc->safe_mode))
+ {
+ /*
+ * This could represent CAST(), ::, or TypeName 'literal',
+ * so any of the components might be leftmost.
+ */
+ loc = leftmostLoc(loc, tc->typeName->location);
+ loc = leftmostLoc(loc, tc->location);
+ }
}
break;
case T_CollateClause:
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index bffc8112aa..e0b0ffb9a6 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -2894,6 +2894,7 @@ eval_const_expressions_mutator(Node *node,
newexpr->resulttype = expr->resulttype;
newexpr->resultcollid = expr->resultcollid;
newexpr->coerceformat = expr->coerceformat;
+ newexpr->safe_mode = expr->safe_mode;
newexpr->location = expr->location;
return (Node *) newexpr;
}
@@ -3158,7 +3159,7 @@ eval_const_expressions_mutator(Node *node,
* drop following arguments since they will never be
* reached.
*/
- if (IsA(e, Const))
+ if ((coalesceexpr->op == NULL_TEST) && IsA(e, Const))
{
if (((Const *) e)->constisnull)
continue; /* drop null constant */
@@ -3183,6 +3184,7 @@ eval_const_expressions_mutator(Node *node,
newcoalesce->coalescetype = coalesceexpr->coalescetype;
newcoalesce->coalescecollid = coalesceexpr->coalescecollid;
newcoalesce->args = newargs;
+ newcoalesce->op = coalesceexpr->op;
newcoalesce->location = coalesceexpr->location;
return (Node *) newcoalesce;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index adc3f8ced3..3e24fe5f70 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -642,6 +642,8 @@ 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
+%type <node> cast_on_error_action
/*
@@ -690,7 +692,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DETACH DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P
DOUBLE_P DROP
- EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
+ EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ERROR_P ESCAPE EVENT EXCEPT
EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXPRESSION
EXTENSION EXTERNAL EXTRACT
@@ -14399,8 +14401,7 @@ interval_second:
* you expect! So we use %prec annotations freely to set precedences.
*/
a_expr: c_expr { $$ = $1; }
- | a_expr TYPECAST Typename
- { $$ = makeTypeCast($1, $3, @2); }
+ | a_expr TYPECAST Typename { $$ = makeTypeCast($1, $3, @2); }
| a_expr COLLATE any_name
{
CollateClause *n = makeNode(CollateClause);
@@ -15330,8 +15331,26 @@ func_expr_common_subexpr:
COERCE_SQL_SYNTAX,
@1);
}
- | CAST '(' a_expr AS Typename ')'
- { $$ = makeTypeCast($3, $5, @1); }
+ | CAST '(' a_expr AS Typename cast_on_error_clause ')'
+ {
+ TypeCast *cast = (TypeCast *) makeTypeCast($3, $5, @1);
+ if ($6 == NULL)
+ $$ = (Node *) cast;
+ else
+ {
+ /*
+ * On-error actions must themselves be typecast to the
+ * same type as the original expression.
+ */
+ TypeCast *on_err = (TypeCast *) makeTypeCast($6, $5, @6);
+ CoalesceExpr *c = makeNode(CoalesceExpr);
+ cast->safe_mode = true;
+ c->args = list_make2(cast, on_err);
+ c->op = ERROR_TEST;
+ c->location = @1;
+ $$ = (Node *) c;
+ }
+ }
| EXTRACT '(' extract_list ')'
{
$$ = (Node *) makeFuncCall(SystemFuncName("extract"),
@@ -15462,6 +15481,7 @@ func_expr_common_subexpr:
CoalesceExpr *c = makeNode(CoalesceExpr);
c->args = $3;
+ c->op = NULL_TEST;
c->location = @1;
$$ = (Node *) c;
}
@@ -15551,6 +15571,15 @@ func_expr_common_subexpr:
}
;
+cast_on_error_clause: cast_on_error_action ON ERROR_P { $$ = $1; }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
+cast_on_error_action: ERROR_P { $$ = NULL; }
+ | NULL_P { $$ = makeNullAConst(-1); }
+ | DEFAULT a_expr { $$ = $2; }
+ ;
+
/*
* SQL/XML support
*/
@@ -16138,8 +16167,8 @@ substr_list:
* is unknown or doesn't have an implicit cast to int4.
*/
$$ = list_make3($1, makeIntConst(1, -1),
- makeTypeCast($3,
- SystemTypeName("int4"), -1));
+ makeTypeCast($3, SystemTypeName("int4"),
+ -1));
}
| a_expr SIMILAR a_expr ESCAPE a_expr
{
@@ -16799,6 +16828,7 @@ unreserved_keyword:
| ENCODING
| ENCRYPTED
| ENUM_P
+ | ERROR_P
| ESCAPE
| EVENT
| EXCLUDE
@@ -17346,6 +17376,7 @@ bare_label_keyword:
| ENCRYPTED
| END_P
| ENUM_P
+ | ERROR_P
| ESCAPE
| EVENT
| EXCLUDE
@@ -17749,6 +17780,7 @@ makeTypeCast(Node *arg, TypeName *typename, int location)
n->arg = arg;
n->typeName = typename;
+ n->safe_mode = false;
n->location = location;
return (Node *) n;
}
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 3ef9e8ee5e..b08422297d 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -2004,7 +2004,8 @@ build_aggregate_transfn_expr(Oid *agg_input_types,
args,
InvalidOid,
agg_input_collation,
- COERCE_EXPLICIT_CALL);
+ COERCE_EXPLICIT_CALL,
+ NULL);
fexpr->funcvariadic = agg_variadic;
*transfnexpr = (Expr *) fexpr;
@@ -2020,7 +2021,8 @@ build_aggregate_transfn_expr(Oid *agg_input_types,
args,
InvalidOid,
agg_input_collation,
- COERCE_EXPLICIT_CALL);
+ COERCE_EXPLICIT_CALL,
+ NULL);
fexpr->funcvariadic = agg_variadic;
*invtransfnexpr = (Expr *) fexpr;
}
@@ -2048,7 +2050,8 @@ build_aggregate_serialfn_expr(Oid serialfn_oid,
args,
InvalidOid,
InvalidOid,
- COERCE_EXPLICIT_CALL);
+ COERCE_EXPLICIT_CALL,
+ NULL);
*serialfnexpr = (Expr *) fexpr;
}
@@ -2072,7 +2075,8 @@ build_aggregate_deserialfn_expr(Oid deserialfn_oid,
args,
InvalidOid,
InvalidOid,
- COERCE_EXPLICIT_CALL);
+ COERCE_EXPLICIT_CALL,
+ NULL);
*deserialfnexpr = (Expr *) fexpr;
}
@@ -2109,7 +2113,8 @@ build_aggregate_finalfn_expr(Oid *agg_input_types,
args,
InvalidOid,
agg_input_collation,
- COERCE_EXPLICIT_CALL);
+ COERCE_EXPLICIT_CALL,
+ NULL);
/* finalfn is currently never treated as variadic */
}
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 60908111c8..c00ef1bd70 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -36,13 +36,15 @@ static Node *coerce_type_typmod(Node *node,
Oid targetTypeId, int32 targetTypMod,
CoercionContext ccontext, CoercionForm cformat,
int location,
- bool hideInputCoercion);
+ bool hideInputCoercion,
+ bool *cast_error_found);
static void hide_coercion_node(Node *node);
static Node *build_coercion_expression(Node *node,
CoercionPathType pathtype,
Oid funcId,
Oid targetTypeId, int32 targetTypMod,
CoercionContext ccontext, CoercionForm cformat,
+ bool *cast_error_found,
int location);
static Node *coerce_record_to_complex(ParseState *pstate, Node *node,
Oid targetTypeId,
@@ -80,6 +82,19 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype,
CoercionContext ccontext,
CoercionForm cformat,
int location)
+{
+ return coerce_to_target_type_safe(pstate, expr, exprtype, targettype,
+ targettypmod, ccontext, cformat,
+ NULL, location);
+}
+
+Node *
+coerce_to_target_type_safe(ParseState *pstate, Node *expr, Oid exprtype,
+ Oid targettype, int32 targettypmod,
+ CoercionContext ccontext,
+ CoercionForm cformat,
+ bool *cast_error_found,
+ int location)
{
Node *result;
Node *origexpr;
@@ -101,19 +116,30 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype,
while (expr && IsA(expr, CollateExpr))
expr = (Node *) ((CollateExpr *) expr)->arg;
- result = coerce_type(pstate, expr, exprtype,
- targettype, targettypmod,
- ccontext, cformat, location);
+ result = coerce_type_safe(pstate, expr, exprtype,
+ targettype, targettypmod,
+ ccontext, cformat,
+ cast_error_found, location);
+
+ /*
+ * If this coercion failed on bad input, we want to report that to the
+ * caller.
+ */
+ if (cast_error_found != NULL && *cast_error_found)
+ return NULL;
/*
* If the target is a fixed-length type, it may need a length coercion as
* well as a type coercion. If we find ourselves adding both, force the
* inner coercion node to implicit display form.
*/
- result = coerce_type_typmod(result,
- targettype, targettypmod,
+ result = coerce_type_typmod(result, targettype, targettypmod,
ccontext, cformat, location,
- (result != expr && !IsA(result, Const)));
+ (result != expr && !IsA(result, Const)),
+ cast_error_found);
+
+ if (cast_error_found != NULL && *cast_error_found)
+ return NULL;
if (expr != origexpr && type_is_collatable(targettype))
{
@@ -157,6 +183,18 @@ Node *
coerce_type(ParseState *pstate, Node *node,
Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod,
CoercionContext ccontext, CoercionForm cformat, int location)
+{
+ return coerce_type_safe(pstate, node, inputTypeId, targetTypeId,
+ targetTypeMod, ccontext, cformat,
+ NULL, location);
+}
+
+Node *
+coerce_type_safe(ParseState *pstate, Node *node,
+ Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod,
+ CoercionContext ccontext, CoercionForm cformat,
+ bool *cast_error_found,
+ int location)
{
Node *result;
CoercionPathType pathtype;
@@ -255,6 +293,7 @@ coerce_type(ParseState *pstate, Node *node,
int32 inputTypeMod;
Type baseType;
ParseCallbackState pcbstate;
+ bool coerce_failed = false;
/*
* If the target type is a domain, we want to call its base type's
@@ -307,21 +346,47 @@ coerce_type(ParseState *pstate, Node *node,
* We assume here that UNKNOWN's internal representation is the same
* as CSTRING.
*/
- if (!con->constisnull)
- newcon->constvalue = stringTypeDatum(baseType,
- DatumGetCString(con->constvalue),
- inputTypeMod);
+ if (cast_error_found == NULL)
+ {
+ if (!con->constisnull)
+ newcon->constvalue = stringTypeDatum(baseType,
+ DatumGetCString(con->constvalue),
+ inputTypeMod);
+ else
+ newcon->constvalue = stringTypeDatum(baseType,
+ NULL,
+ inputTypeMod);
+ }
else
- newcon->constvalue = stringTypeDatum(baseType,
- NULL,
- inputTypeMod);
+ {
+ /* If we find an error, just carry it up to the caller */
+ Datum val2;
+ bool success;
+
+ if (!con->constisnull)
+ success = stringTypeDatumSafe(baseType,
+ DatumGetCString(con->constvalue),
+ inputTypeMod, &val2);
+ else
+ success = stringTypeDatumSafe(baseType, NULL, inputTypeMod,
+ &val2);
+
+ if (success)
+ newcon->constvalue = val2;
+ else
+ {
+ *cast_error_found = true;
+ coerce_failed = true;
+ result = NULL;
+ }
+ }
/*
* If it's a varlena value, force it to be in non-expanded
* (non-toasted) format; this avoids any possible dependency on
* external values and improves consistency of representation.
*/
- if (!con->constisnull && newcon->constlen == -1)
+ if (!coerce_failed && !con->constisnull && newcon->constlen == -1)
newcon->constvalue =
PointerGetDatum(PG_DETOAST_DATUM(newcon->constvalue));
@@ -338,32 +403,53 @@ coerce_type(ParseState *pstate, Node *node,
* identical may not get recognized as such. See pgsql-hackers
* discussion of 2008-04-04.
*/
- if (!con->constisnull && !newcon->constbyval)
+ if (!coerce_failed && !con->constisnull && !newcon->constbyval)
{
Datum val2;
- val2 = stringTypeDatum(baseType,
- DatumGetCString(con->constvalue),
- inputTypeMod);
- if (newcon->constlen == -1)
- val2 = PointerGetDatum(PG_DETOAST_DATUM(val2));
- if (!datumIsEqual(newcon->constvalue, val2, false, newcon->constlen))
- elog(WARNING, "type %s has unstable input conversion for \"%s\"",
- typeTypeName(baseType), DatumGetCString(con->constvalue));
+ if (cast_error_found != NULL)
+ {
+ if (!stringTypeDatumSafe(baseType,
+ DatumGetCString(con->constvalue),
+ inputTypeMod, &val2))
+ {
+ coerce_failed = true;
+ *cast_error_found = true;
+ result = NULL;
+ }
+ }
+ else
+ val2 = stringTypeDatum(baseType,
+ DatumGetCString(con->constvalue),
+ inputTypeMod);
+ if (!coerce_failed)
+ {
+ if (newcon->constlen == -1)
+ val2 = PointerGetDatum(PG_DETOAST_DATUM(val2));
+ if (!datumIsEqual(newcon->constvalue, val2,
+ false, newcon->constlen))
+ elog(WARNING,
+ "type %s has unstable input conversion for \"%s\"",
+ typeTypeName(baseType),
+ DatumGetCString(con->constvalue));
+ }
}
#endif
cancel_parser_errposition_callback(&pcbstate);
- result = (Node *) newcon;
+ if (!coerce_failed)
+ {
+ result = (Node *) newcon;
- /* If target is a domain, apply constraints. */
- if (baseTypeId != targetTypeId)
- result = coerce_to_domain(result,
- baseTypeId, baseTypeMod,
- targetTypeId,
- ccontext, cformat, location,
- false);
+ /* If target is a domain, apply constraints. */
+ if (baseTypeId != targetTypeId)
+ result = coerce_to_domain_safe(result,
+ baseTypeId, baseTypeMod,
+ targetTypeId,
+ ccontext, cformat, location,
+ false, cast_error_found);
+ }
ReleaseSysCache(baseType);
@@ -396,9 +482,15 @@ coerce_type(ParseState *pstate, Node *node,
*/
CollateExpr *coll = (CollateExpr *) node;
- result = coerce_type(pstate, (Node *) coll->arg,
- inputTypeId, targetTypeId, targetTypeMod,
- ccontext, cformat, location);
+ result = coerce_type_safe(pstate, (Node *) coll->arg,
+ inputTypeId, targetTypeId, targetTypeMod,
+ ccontext, cformat,
+ cast_error_found,
+ location);
+
+ if (cast_error_found != NULL && *cast_error_found)
+ return NULL;
+
if (type_is_collatable(targetTypeId))
{
CollateExpr *newcoll = makeNode(CollateExpr);
@@ -431,17 +523,23 @@ coerce_type(ParseState *pstate, Node *node,
result = build_coercion_expression(node, pathtype, funcId,
baseTypeId, baseTypeMod,
- ccontext, cformat, location);
+ ccontext, cformat,
+ cast_error_found,
+ location);
/*
* If domain, coerce to the domain type and relabel with domain
* type ID, hiding the previous coercion node.
*/
if (targetTypeId != baseTypeId)
- result = coerce_to_domain(result, baseTypeId, baseTypeMod,
+ {
+ result = coerce_to_domain_safe(result, baseTypeId, baseTypeMod,
targetTypeId,
ccontext, cformat, location,
- true);
+ true, cast_error_found);
+ if (cast_error_found != NULL && *cast_error_found)
+ return NULL;
+ }
}
else
{
@@ -454,9 +552,11 @@ coerce_type(ParseState *pstate, Node *node,
* that must be accounted for. If the destination is a domain
* then we won't need a RelabelType node.
*/
- result = coerce_to_domain(node, InvalidOid, -1, targetTypeId,
+ result = coerce_to_domain_safe(node, InvalidOid, -1, targetTypeId,
ccontext, cformat, location,
- false);
+ false, cast_error_found);
+ if (cast_error_found != NULL && *cast_error_found)
+ return NULL;
if (result == node)
{
/*
@@ -676,6 +776,17 @@ Node *
coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod, Oid typeId,
CoercionContext ccontext, CoercionForm cformat, int location,
bool hideInputCoercion)
+{
+ return coerce_to_domain_safe(arg, baseTypeId, baseTypeMod, typeId,
+ ccontext, cformat, location,
+ hideInputCoercion, NULL);
+}
+
+Node *
+coerce_to_domain_safe(Node *arg, Oid baseTypeId, int32 baseTypeMod, Oid typeId,
+ CoercionContext ccontext, CoercionForm cformat,
+ int location, bool hideInputCoercion,
+ bool *cast_error_found)
{
CoerceToDomain *result;
@@ -706,7 +817,7 @@ coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod, Oid typeId,
*/
arg = coerce_type_typmod(arg, baseTypeId, baseTypeMod,
ccontext, COERCE_IMPLICIT_CAST, location,
- false);
+ false, cast_error_found);
/*
* Now build the domain coercion node. This represents run-time checking
@@ -753,7 +864,7 @@ static Node *
coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod,
CoercionContext ccontext, CoercionForm cformat,
int location,
- bool hideInputCoercion)
+ bool hideInputCoercion, bool *cast_error_found)
{
CoercionPathType pathtype;
Oid funcId;
@@ -780,7 +891,9 @@ coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod,
{
node = build_coercion_expression(node, pathtype, funcId,
targetTypeId, targetTypMod,
- ccontext, cformat, location);
+ ccontext, cformat,
+ cast_error_found,
+ location);
}
else
{
@@ -841,9 +954,10 @@ build_coercion_expression(Node *node,
Oid funcId,
Oid targetTypeId, int32 targetTypMod,
CoercionContext ccontext, CoercionForm cformat,
- int location)
+ bool *cast_error_found, int location)
{
int nargs = 0;
+ bool safe_mode = (cast_error_found != NULL);
if (OidIsValid(funcId))
{
@@ -913,13 +1027,14 @@ build_coercion_expression(Node *node,
}
fexpr = makeFuncExpr(funcId, targetTypeId, args,
- InvalidOid, InvalidOid, cformat);
+ InvalidOid, InvalidOid, cformat, safe_mode);
fexpr->location = location;
return (Node *) fexpr;
}
else if (pathtype == COERCION_PATH_ARRAYCOERCE)
{
/* We need to build an ArrayCoerceExpr */
+ /* TODO pass in Safe param */
ArrayCoerceExpr *acoerce = makeNode(ArrayCoerceExpr);
CaseTestExpr *ctest = makeNode(CaseTestExpr);
Oid sourceBaseTypeId;
@@ -951,14 +1066,20 @@ build_coercion_expression(Node *node,
targetElementType = get_element_type(targetTypeId);
Assert(OidIsValid(targetElementType));
- elemexpr = coerce_to_target_type(NULL,
+ /*
+ * No node gets resolved here, so this cast cannot fail
+ * at parse time.
+ */
+ elemexpr = coerce_to_target_type_safe(NULL,
(Node *) ctest,
ctest->typeId,
targetElementType,
targetTypMod,
ccontext,
cformat,
+ cast_error_found,
location);
+
if (elemexpr == NULL) /* shouldn't happen */
elog(ERROR, "failed to coerce array element type as expected");
@@ -973,6 +1094,7 @@ build_coercion_expression(Node *node,
acoerce->resulttypmod = exprTypmod(elemexpr);
/* resultcollid will be set by parse_collate.c */
acoerce->coerceformat = cformat;
+ acoerce->safe_mode = safe_mode;
acoerce->location = location;
return (Node *) acoerce;
@@ -988,6 +1110,7 @@ build_coercion_expression(Node *node,
iocoerce->resulttype = targetTypeId;
/* resultcollid will be set by parse_collate.c */
iocoerce->coerceformat = cformat;
+ iocoerce->safe_mode = safe_mode;
iocoerce->location = location;
return (Node *) iocoerce;
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 150a8099c2..54224b9f06 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -57,7 +57,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 *cast_error_found);
static Node *transformRowExpr(ParseState *pstate, RowExpr *r, bool allowDefault);
static Node *transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c);
static Node *transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m);
@@ -137,7 +138,7 @@ 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:
@@ -1907,7 +1908,8 @@ 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 *cast_error_found)
{
ArrayExpr *newa = makeNode(ArrayExpr);
List *newelems = NIL;
@@ -1938,7 +1940,12 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
(A_ArrayExpr *) e,
array_type,
element_type,
- typmod);
+ typmod,
+ cast_error_found);
+
+ if (cast_error_found != NULL && *cast_error_found)
+ return NULL;
+
/* we certainly have an array here */
Assert(array_type == InvalidOid || array_type == exprType(newe));
newa->multidims = true;
@@ -2027,13 +2034,18 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
if (coerce_hard)
{
- newe = coerce_to_target_type(pstate, e,
+ newe = coerce_to_target_type_safe(pstate, e,
exprType(e),
coerce_type,
typmod,
COERCION_EXPLICIT,
COERCE_EXPLICIT_CAST,
+ cast_error_found,
-1);
+
+ if (cast_error_found != NULL && *cast_error_found)
+ return NULL;
+
if (newe == NULL)
ereport(ERROR,
(errcode(ERRCODE_CANNOT_COERCE),
@@ -2105,13 +2117,19 @@ transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c)
List *newcoercedargs = NIL;
ListCell *args;
+
foreach(args, c->args)
{
Node *e = (Node *) lfirst(args);
Node *newe;
newe = transformExprRecurse(pstate, e);
- newargs = lappend(newargs, newe);
+ /*
+ * Some child nodes can return NULL if they would result in a
+ * safe_mode error. Filter those out here
+ */
+ if (newe != NULL)
+ newargs = lappend(newargs, newe);
}
newc->coalescetype = select_common_type(pstate, newargs, "COALESCE", NULL);
@@ -2141,6 +2159,7 @@ transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c)
exprLocation(pstate->p_last_srf))));
newc->args = newcoercedargs;
+ newc->op = c->op;
newc->location = c->location;
return (Node *) newc;
}
@@ -2345,8 +2364,7 @@ transformXmlSerialize(ParseState *pstate, XmlSerialize *xs)
result = coerce_to_target_type(pstate, (Node *) xexpr,
TEXTOID, targetType, targetTypmod,
COERCION_IMPLICIT,
- COERCE_IMPLICIT_CAST,
- -1);
+ COERCE_IMPLICIT_CAST, -1);
if (result == NULL)
ereport(ERROR,
(errcode(ERRCODE_CANNOT_COERCE),
@@ -2524,10 +2542,25 @@ transformTypeCast(ParseState *pstate, TypeCast *tc)
Oid inputType;
Oid targetType;
int32 targetTypmod;
- int location;
+ int location = tc->location;
+ TypeName *typeName = tc->typeName;
+ bool cast_error;
+ bool *cast_error_found;
+
+
+ if (tc->safe_mode)
+ {
+ cast_error = false;
+ cast_error_found = &cast_error;
+ }
+ else
+ {
+ cast_error_found = NULL;
+ }
+
/* Look up the type name first */
- typenameTypeIdAndMod(pstate, tc->typeName, &targetType, &targetTypmod);
+ typenameTypeIdAndMod(pstate, typeName, &targetType, &targetTypmod);
/*
* If the subject of the typecast is an ARRAY[] construct and the target
@@ -2557,7 +2590,16 @@ transformTypeCast(ParseState *pstate, TypeCast *tc)
(A_ArrayExpr *) arg,
targetBaseType,
elementType,
- targetBaseTypmod);
+ targetBaseTypmod,
+ cast_error_found);
+
+ /*
+ * if any element of the tranformation failed, then that means that the
+ * larger cast has failed. Returning NULL here is safe when the
+ * parent node (CoalesceExpr) knows to look for it (and filter it out).
+ */
+ if (cast_error_found != NULL && *cast_error_found)
+ return NULL;
}
else
expr = transformExprRecurse(pstate, arg);
@@ -2574,15 +2616,24 @@ transformTypeCast(ParseState *pstate, TypeCast *tc)
* 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 = tc->location;
if (location < 0)
- location = tc->typeName->location;
+ location = typeName->location;
+
+ result = coerce_to_target_type_safe(pstate, expr, inputType,
+ targetType, targetTypmod,
+ COERCION_EXPLICIT,
+ COERCE_EXPLICIT_CAST,
+ cast_error_found,
+ location);
+
+ /*
+ * If this typecast reported a cast error, and the cast failed outright,
+ * then we want to replace this node entirely with the default,
+ * and we signal the parent node to do that by returning NULL.
+ */
+ if (cast_error_found != NULL && *cast_error_found)
+ return NULL;
- result = coerce_to_target_type(pstate, expr, inputType,
- targetType, targetTypmod,
- COERCION_EXPLICIT,
- COERCE_EXPLICIT_CAST,
- location);
if (result == NULL)
ereport(ERROR,
(errcode(ERRCODE_CANNOT_COERCE),
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index 29643fb4ab..92a3c60955 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -4049,7 +4049,8 @@ get_qual_for_hash(Relation parent, PartitionBoundSpec *spec)
args,
InvalidOid,
InvalidOid,
- COERCE_EXPLICIT_CALL);
+ COERCE_EXPLICIT_CALL,
+ NULL);
return list_make1(fexpr);
}
diff --git a/src/backend/rewrite/rewriteSearchCycle.c b/src/backend/rewrite/rewriteSearchCycle.c
index 58f684cd52..ae93c06702 100644
--- a/src/backend/rewrite/rewriteSearchCycle.c
+++ b/src/backend/rewrite/rewriteSearchCycle.c
@@ -191,7 +191,7 @@ make_path_cat_expr(RowExpr *rowexpr, AttrNumber path_varattno)
fexpr = makeFuncExpr(F_ARRAY_CAT, RECORDARRAYOID,
list_make2(makeVar(1, path_varattno, RECORDARRAYOID, -1, 0, 0),
arr),
- InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL);
+ InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL, NULL);
return (Expr *) fexpr;
}
@@ -521,7 +521,7 @@ rewriteSearchAndCycle(CommonTableExpr *cte)
fs->resulttype = INT8OID;
fs->resulttypmod = -1;
- fexpr = makeFuncExpr(F_INT8INC, INT8OID, list_make1(fs), InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL);
+ fexpr = makeFuncExpr(F_INT8INC, INT8OID, list_make1(fs), InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL, NULL);
lfirst(list_head(search_col_rowexpr->args)) = fexpr;
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 0557302b92..3ab5f8b000 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -138,6 +138,7 @@ typedef enum ExprEvalOp
/* conditional jumps based on current result value */
EEOP_JUMP_IF_NULL,
EEOP_JUMP_IF_NOT_NULL,
+ EEOP_JUMP_IF_NOT_ERROR,
EEOP_JUMP_IF_NOT_TRUE,
/* perform NULL tests for scalar values */
@@ -273,6 +274,7 @@ typedef struct ExprEvalStep
/* where to store the result of this step */
Datum *resvalue;
bool *resnull;
+ bool *reserror;
/*
* Inline data for the operation. Inline data is faster to access, but
@@ -395,6 +397,7 @@ typedef struct ExprEvalStep
{
Datum *value; /* value to return */
bool *isnull;
+ bool *iserror;
} casetest;
/* for EEOP_MAKE_READONLY */
@@ -402,6 +405,7 @@ typedef struct ExprEvalStep
{
Datum *value; /* value to coerce to read-only */
bool *isnull;
+ bool *iserror;
} make_readonly;
/* for EEOP_IOCOERCE */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 9a64a830a2..fb9b2f7963 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -95,6 +95,9 @@ typedef struct ExprState
#define FIELDNO_EXPRSTATE_RESULTSLOT 4
TupleTableSlot *resultslot;
+#define FIELDNO_EXPRSTATE_RESERROR 5
+ bool reserror;
+
/*
* Instructions to compute expression's return value.
*/
@@ -126,6 +129,7 @@ typedef struct ExprState
Datum *innermost_caseval;
bool *innermost_casenull;
+ bool *innermost_caseerror;
Datum *innermost_domainval;
bool *innermost_domainnull;
diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h
index 50de4c62af..50a9bdde59 100644
--- a/src/include/nodes/makefuncs.h
+++ b/src/include/nodes/makefuncs.h
@@ -77,7 +77,8 @@ extern ColumnDef *makeColumnDef(const char *colname,
Oid typeOid, int32 typmod, Oid collOid);
extern FuncExpr *makeFuncExpr(Oid funcid, Oid rettype, List *args,
- Oid funccollid, Oid inputcollid, CoercionForm fformat);
+ Oid funccollid, Oid inputcollid,
+ CoercionForm fformat, bool safe_mode);
extern FuncCall *makeFuncCall(List *name, List *args,
CoercionForm funcformat, int location);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 8fe9b2fcfe..c4ee9c46d0 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -340,6 +340,7 @@ typedef struct TypeCast
NodeTag type;
Node *arg; /* the expression being casted */
TypeName *typeName; /* the target type */
+ bool safe_mode; /* cast can ereturn error vs ereport */
int location; /* token location, or -1 if unknown */
} TypeCast;
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 74f228d959..2ea23914b1 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -604,6 +604,7 @@ typedef struct FuncExpr
Oid funccollid; /* OID of collation of result */
Oid inputcollid; /* OID of collation that function should use */
List *args; /* arguments to the function */
+ bool safe_mode; /* use safe mode */
int location; /* token location, or -1 if unknown */
} FuncExpr;
@@ -1023,6 +1024,7 @@ typedef struct CoerceViaIO
/* output typmod is not stored, but is presumed -1 */
Oid resultcollid; /* OID of collation, or InvalidOid if none */
CoercionForm coerceformat; /* how to display this node */
+ bool safe_mode; /* use safe mode */
int location; /* token location, or -1 if unknown */
} CoerceViaIO;
@@ -1048,6 +1050,7 @@ typedef struct ArrayCoerceExpr
int32 resulttypmod; /* output typmod (also element typmod) */
Oid resultcollid; /* OID of collation, or InvalidOid if none */
CoercionForm coerceformat; /* how to display this node */
+ bool safe_mode; /* use safe mode */
int location; /* token location, or -1 if unknown */
} ArrayCoerceExpr;
@@ -1260,8 +1263,14 @@ typedef struct RowCompareExpr
List *rargs; /* the right-hand input arguments */
} RowCompareExpr;
+typedef enum CoalesceOp
+{
+ NULL_TEST, /* Test for NULL, like SQL COALECE() */
+ ERROR_TEST /* Test for error flag */
+} CoalesceOp;
+
/*
- * CoalesceExpr - a COALESCE expression
+ * CoalesceExpr - a COALESCE expression or OnError expression
*/
typedef struct CoalesceExpr
{
@@ -1269,6 +1278,7 @@ typedef struct CoalesceExpr
Oid coalescetype; /* type of expression result */
Oid coalescecollid; /* OID of collation, or InvalidOid if none */
List *args; /* the arguments */
+ CoalesceOp op; /* test for NULL or test for ERROR */
int location; /* token location, or -1 if unknown */
} CoalesceExpr;
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 957ee18d84..ed7d670819 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -151,6 +151,7 @@ PG_KEYWORD("encoding", ENCODING, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("encrypted", ENCRYPTED, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("end", END_P, RESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("enum", ENUM_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("error", ERROR_P, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("escape", ESCAPE, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("event", EVENT, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("except", EXCEPT, RESERVED_KEYWORD, AS_LABEL)
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index ddbc995077..db8e15f225 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -43,15 +43,30 @@ extern Node *coerce_to_target_type(ParseState *pstate,
CoercionContext ccontext,
CoercionForm cformat,
int location);
+extern Node *coerce_to_target_type_safe(ParseState *pstate,
+ Node *expr, Oid exprtype,
+ Oid targettype, int32 targettypmod,
+ CoercionContext ccontext,
+ CoercionForm cformat,
+ bool *cast_error_found,
+ int location);
extern bool can_coerce_type(int nargs, const Oid *input_typeids, const Oid *target_typeids,
CoercionContext ccontext);
extern Node *coerce_type(ParseState *pstate, Node *node,
Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod,
CoercionContext ccontext, CoercionForm cformat, int location);
+extern Node *coerce_type_safe(ParseState *pstate, Node *node,
+ Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod,
+ CoercionContext ccontext, CoercionForm cformat,
+ bool *cast_error_found, int location);
extern Node *coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod,
Oid typeId,
CoercionContext ccontext, CoercionForm cformat, int location,
bool hideInputCoercion);
+extern Node *coerce_to_domain_safe(Node *arg, Oid baseTypeId, int32 baseTypeMod,
+ Oid typeId,
+ CoercionContext ccontext, CoercionForm cformat, int location,
+ bool hideInputCoercion, bool *cast_error_found);
extern Node *coerce_to_boolean(ParseState *pstate, Node *node,
const char *constructName);
diff --git a/src/test/regress/expected/cast.out b/src/test/regress/expected/cast.out
new file mode 100644
index 0000000000..87cd1c101b
--- /dev/null
+++ b/src/test/regress/expected/cast.out
@@ -0,0 +1,40 @@
+/* ON ERROR behavior */
+VALUES (CAST('error' AS integer));
+ERROR: invalid input syntax for type integer: "error"
+LINE 2: VALUES (CAST('error' AS integer));
+ ^
+VALUES (CAST('error' AS integer ERROR ON ERROR));
+ERROR: invalid input syntax for type integer: "error"
+LINE 1: VALUES (CAST('error' AS integer ERROR ON ERROR));
+ ^
+VALUES (CAST('error' AS integer NULL ON ERROR));
+ column1
+---------
+
+(1 row)
+
+VALUES (CAST('error' AS integer DEFAULT 42 ON ERROR));
+ column1
+---------
+ 42
+(1 row)
+
+SELECT CAST('{123,abc,456}' AS integer[] DEFAULT '{-789}' ON ERROR) as array_test1;
+ array_test1
+-------------
+ {-789}
+(1 row)
+
+SELECT CAST('{234,def,567}'::text[] AS integer[] DEFAULT '{-1011}' ON ERROR) as array_test2;
+ array_test2
+-------------
+ {-1011}
+(1 row)
+
+SELECT CAST(u.arg AS integer DEFAULT -1 ON ERROR) AS unnest_test1 FROM unnest('{345,ghi,678}'::text[]) AS u(arg);
+ unnest_test1
+--------------
+ 345
+ -1
+ 678
+(3 rows)
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 9a139f1e24..1d49b9b95f 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -41,7 +41,7 @@ test: geometry horology tstypes regex type_sanity opr_sanity misc_sanity comment
# execute two copy tests in parallel, to check that copy itself
# is concurrent safe.
# ----------
-test: copy copyselect copydml insert insert_conflict
+test: copy copyselect copydml insert insert_conflict cast
# ----------
# More groups of parallel tests
diff --git a/src/test/regress/sql/cast.sql b/src/test/regress/sql/cast.sql
new file mode 100644
index 0000000000..fab8cb7d33
--- /dev/null
+++ b/src/test/regress/sql/cast.sql
@@ -0,0 +1,11 @@
+/* ON ERROR behavior */
+
+VALUES (CAST('error' AS integer));
+VALUES (CAST('error' AS integer ERROR ON ERROR));
+VALUES (CAST('error' AS integer NULL ON ERROR));
+VALUES (CAST('error' AS integer DEFAULT 42 ON ERROR));
+
+SELECT CAST('{123,abc,456}' AS integer[] DEFAULT '{-789}' ON ERROR) as array_test1;
+SELECT CAST('{234,def,567}'::text[] AS integer[] DEFAULT '{-1011}' ON ERROR) as array_test2;
+
+SELECT CAST(u.arg AS integer DEFAULT -1 ON ERROR) AS unnest_test1 FROM unnest('{345,ghi,678}'::text[]) AS u(arg);
--
2.38.1