mirror of
https://github.com/postgres/postgres.git
synced 2025-05-03 22:24:49 +03:00
SQL/JSON: Fix error-handling of some JsonBehavior expressions
To ensure that the errors of executing a JsonBehavior expression that is coerced in the parser are caught instead of being thrown directly, pass ErrorSaveContext to ExecInitExprRec() when initializing it. Also, add a EEOP_JSONEXPR_COERCION_FINISH step to handle the errors that are caught that way. Discussion: https://postgr.es/m/CACJufxEo4sUjKCYtda0_qt9tazqqKPmF1cqhW9KBOUeJFqQd2g@mail.gmail.com Backpatch-through: 17
This commit is contained in:
parent
c7301c3b6f
commit
63e6c5f4a2
@ -4400,6 +4400,8 @@ ExecInitJsonExpr(JsonExpr *jsexpr, ExprState *state,
|
|||||||
if (jsexpr->on_error &&
|
if (jsexpr->on_error &&
|
||||||
jsexpr->on_error->btype != JSON_BEHAVIOR_ERROR)
|
jsexpr->on_error->btype != JSON_BEHAVIOR_ERROR)
|
||||||
{
|
{
|
||||||
|
ErrorSaveContext *saved_escontext;
|
||||||
|
|
||||||
jsestate->jump_error = state->steps_len;
|
jsestate->jump_error = state->steps_len;
|
||||||
|
|
||||||
/* JUMP to end if false, that is, skip the ON ERROR expression. */
|
/* JUMP to end if false, that is, skip the ON ERROR expression. */
|
||||||
@ -4410,15 +4412,36 @@ ExecInitJsonExpr(JsonExpr *jsexpr, ExprState *state,
|
|||||||
scratch->d.jump.jumpdone = -1; /* set below */
|
scratch->d.jump.jumpdone = -1; /* set below */
|
||||||
ExprEvalPushStep(state, scratch);
|
ExprEvalPushStep(state, scratch);
|
||||||
|
|
||||||
/* Steps to evaluate the ON ERROR expression */
|
/*
|
||||||
|
* Steps to evaluate the ON ERROR expression; handle errors softly to
|
||||||
|
* rethrow them in COERCION_FINISH step that will be added later.
|
||||||
|
*/
|
||||||
|
saved_escontext = state->escontext;
|
||||||
|
state->escontext = escontext;
|
||||||
ExecInitExprRec((Expr *) jsexpr->on_error->expr,
|
ExecInitExprRec((Expr *) jsexpr->on_error->expr,
|
||||||
state, resv, resnull);
|
state, resv, resnull);
|
||||||
|
state->escontext = saved_escontext;
|
||||||
|
|
||||||
/* Step to coerce the ON ERROR expression if needed */
|
/* Step to coerce the ON ERROR expression if needed */
|
||||||
if (jsexpr->on_error->coerce)
|
if (jsexpr->on_error->coerce)
|
||||||
ExecInitJsonCoercion(state, jsexpr->returning, escontext,
|
ExecInitJsonCoercion(state, jsexpr->returning, escontext,
|
||||||
jsexpr->omit_quotes, resv, resnull);
|
jsexpr->omit_quotes, resv, resnull);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a COERCION_FINISH step to check for errors that may occur when
|
||||||
|
* coercing and rethrow them.
|
||||||
|
*/
|
||||||
|
if (jsexpr->on_error->coerce ||
|
||||||
|
IsA(jsexpr->on_error->expr, CoerceViaIO) ||
|
||||||
|
IsA(jsexpr->on_error->expr, CoerceToDomain))
|
||||||
|
{
|
||||||
|
scratch->opcode = EEOP_JSONEXPR_COERCION_FINISH;
|
||||||
|
scratch->resvalue = resv;
|
||||||
|
scratch->resnull = resnull;
|
||||||
|
scratch->d.jsonexpr.jsestate = jsestate;
|
||||||
|
ExprEvalPushStep(state, scratch);
|
||||||
|
}
|
||||||
|
|
||||||
/* JUMP to end to skip the ON EMPTY steps added below. */
|
/* JUMP to end to skip the ON EMPTY steps added below. */
|
||||||
jumps_to_end = lappend_int(jumps_to_end, state->steps_len);
|
jumps_to_end = lappend_int(jumps_to_end, state->steps_len);
|
||||||
scratch->opcode = EEOP_JUMP;
|
scratch->opcode = EEOP_JUMP;
|
||||||
@ -4433,6 +4456,8 @@ ExecInitJsonExpr(JsonExpr *jsexpr, ExprState *state,
|
|||||||
if (jsexpr->on_empty != NULL &&
|
if (jsexpr->on_empty != NULL &&
|
||||||
jsexpr->on_empty->btype != JSON_BEHAVIOR_ERROR)
|
jsexpr->on_empty->btype != JSON_BEHAVIOR_ERROR)
|
||||||
{
|
{
|
||||||
|
ErrorSaveContext *saved_escontext;
|
||||||
|
|
||||||
jsestate->jump_empty = state->steps_len;
|
jsestate->jump_empty = state->steps_len;
|
||||||
|
|
||||||
/* JUMP to end if false, that is, skip the ON EMPTY expression. */
|
/* JUMP to end if false, that is, skip the ON EMPTY expression. */
|
||||||
@ -4443,14 +4468,36 @@ ExecInitJsonExpr(JsonExpr *jsexpr, ExprState *state,
|
|||||||
scratch->d.jump.jumpdone = -1; /* set below */
|
scratch->d.jump.jumpdone = -1; /* set below */
|
||||||
ExprEvalPushStep(state, scratch);
|
ExprEvalPushStep(state, scratch);
|
||||||
|
|
||||||
/* Steps to evaluate the ON EMPTY expression */
|
/*
|
||||||
|
* Steps to evaluate the ON EMPTY expression; handle errors softly to
|
||||||
|
* rethrow them in COERCION_FINISH step that will be added later.
|
||||||
|
*/
|
||||||
|
saved_escontext = state->escontext;
|
||||||
|
state->escontext = escontext;
|
||||||
ExecInitExprRec((Expr *) jsexpr->on_empty->expr,
|
ExecInitExprRec((Expr *) jsexpr->on_empty->expr,
|
||||||
state, resv, resnull);
|
state, resv, resnull);
|
||||||
|
state->escontext = saved_escontext;
|
||||||
|
|
||||||
/* Step to coerce the ON EMPTY expression if needed */
|
/* Step to coerce the ON EMPTY expression if needed */
|
||||||
if (jsexpr->on_empty->coerce)
|
if (jsexpr->on_empty->coerce)
|
||||||
ExecInitJsonCoercion(state, jsexpr->returning, escontext,
|
ExecInitJsonCoercion(state, jsexpr->returning, escontext,
|
||||||
jsexpr->omit_quotes, resv, resnull);
|
jsexpr->omit_quotes, resv, resnull);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a COERCION_FINISH step to check for errors that may occur when
|
||||||
|
* coercing and rethrow them.
|
||||||
|
*/
|
||||||
|
if (jsexpr->on_empty->coerce ||
|
||||||
|
IsA(jsexpr->on_empty->expr, CoerceViaIO) ||
|
||||||
|
IsA(jsexpr->on_empty->expr, CoerceToDomain))
|
||||||
|
{
|
||||||
|
|
||||||
|
scratch->opcode = EEOP_JSONEXPR_COERCION_FINISH;
|
||||||
|
scratch->resvalue = resv;
|
||||||
|
scratch->resnull = resnull;
|
||||||
|
scratch->d.jsonexpr.jsestate = jsestate;
|
||||||
|
ExprEvalPushStep(state, scratch);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(lc, jumps_to_end)
|
foreach(lc, jumps_to_end)
|
||||||
|
@ -4558,6 +4558,12 @@ ExecEvalJsonCoercionFinish(ExprState *state, ExprEvalStep *op)
|
|||||||
*op->resvalue = (Datum) 0;
|
*op->resvalue = (Datum) 0;
|
||||||
*op->resnull = true;
|
*op->resnull = true;
|
||||||
jsestate->error.value = BoolGetDatum(true);
|
jsestate->error.value = BoolGetDatum(true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset for next use such as for catching errors when coercing a
|
||||||
|
* JsonBehavior expression.
|
||||||
|
*/
|
||||||
|
jsestate->escontext.error_occurred = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +227,11 @@ SELECT * FROM JSON_TABLE(jsonb '{"d1": "H"}', '$'
|
|||||||
|
|
||||||
SELECT * FROM JSON_TABLE(jsonb '{"d1": "H"}', '$'
|
SELECT * FROM JSON_TABLE(jsonb '{"d1": "H"}', '$'
|
||||||
COLUMNS (js1 jsonb_test_domain PATH '$.a2' DEFAULT 'foo'::jsonb_test_domain ON EMPTY));
|
COLUMNS (js1 jsonb_test_domain PATH '$.a2' DEFAULT 'foo'::jsonb_test_domain ON EMPTY));
|
||||||
ERROR: value for domain jsonb_test_domain violates check constraint "jsonb_test_domain_check"
|
js1
|
||||||
|
-----
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
SELECT * FROM JSON_TABLE(jsonb '{"d1": "H"}', '$'
|
SELECT * FROM JSON_TABLE(jsonb '{"d1": "H"}', '$'
|
||||||
COLUMNS (js1 jsonb_test_domain PATH '$.a2' DEFAULT 'foo1'::jsonb_test_domain ON EMPTY));
|
COLUMNS (js1 jsonb_test_domain PATH '$.a2' DEFAULT 'foo1'::jsonb_test_domain ON EMPTY));
|
||||||
js1
|
js1
|
||||||
|
@ -1232,7 +1232,11 @@ DROP TABLE test_jsonb_mutability;
|
|||||||
DROP FUNCTION ret_setint;
|
DROP FUNCTION ret_setint;
|
||||||
CREATE DOMAIN queryfuncs_test_domain AS text CHECK (value <> 'foo');
|
CREATE DOMAIN queryfuncs_test_domain AS text CHECK (value <> 'foo');
|
||||||
SELECT JSON_VALUE(jsonb '{"d1": "H"}', '$.a2' RETURNING queryfuncs_test_domain DEFAULT 'foo'::queryfuncs_test_domain ON EMPTY);
|
SELECT JSON_VALUE(jsonb '{"d1": "H"}', '$.a2' RETURNING queryfuncs_test_domain DEFAULT 'foo'::queryfuncs_test_domain ON EMPTY);
|
||||||
ERROR: value for domain queryfuncs_test_domain violates check constraint "queryfuncs_test_domain_check"
|
json_value
|
||||||
|
------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
SELECT JSON_VALUE(jsonb '{"d1": "H"}', '$.a2' RETURNING queryfuncs_test_domain DEFAULT 'foo1'::queryfuncs_test_domain ON EMPTY);
|
SELECT JSON_VALUE(jsonb '{"d1": "H"}', '$.a2' RETURNING queryfuncs_test_domain DEFAULT 'foo1'::queryfuncs_test_domain ON EMPTY);
|
||||||
json_value
|
json_value
|
||||||
------------
|
------------
|
||||||
|
Loading…
x
Reference in New Issue
Block a user