Skip to content

Commit 63e6c5f

Browse files
committed
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
1 parent c7301c3 commit 63e6c5f

File tree

4 files changed

+65
-4
lines changed

4 files changed

+65
-4
lines changed

src/backend/executor/execExpr.c

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4400,6 +4400,8 @@ ExecInitJsonExpr(JsonExpr *jsexpr, ExprState *state,
44004400
if (jsexpr->on_error &&
44014401
jsexpr->on_error->btype != JSON_BEHAVIOR_ERROR)
44024402
{
4403+
ErrorSaveContext *saved_escontext;
4404+
44034405
jsestate->jump_error = state->steps_len;
44044406

44054407
/* JUMP to end if false, that is, skip the ON ERROR expression. */
@@ -4410,15 +4412,36 @@ ExecInitJsonExpr(JsonExpr *jsexpr, ExprState *state,
44104412
scratch->d.jump.jumpdone = -1; /* set below */
44114413
ExprEvalPushStep(state, scratch);
44124414

4413-
/* Steps to evaluate the ON ERROR expression */
4415+
/*
4416+
* Steps to evaluate the ON ERROR expression; handle errors softly to
4417+
* rethrow them in COERCION_FINISH step that will be added later.
4418+
*/
4419+
saved_escontext = state->escontext;
4420+
state->escontext = escontext;
44144421
ExecInitExprRec((Expr *) jsexpr->on_error->expr,
44154422
state, resv, resnull);
4423+
state->escontext = saved_escontext;
44164424

44174425
/* Step to coerce the ON ERROR expression if needed */
44184426
if (jsexpr->on_error->coerce)
44194427
ExecInitJsonCoercion(state, jsexpr->returning, escontext,
44204428
jsexpr->omit_quotes, resv, resnull);
44214429

4430+
/*
4431+
* Add a COERCION_FINISH step to check for errors that may occur when
4432+
* coercing and rethrow them.
4433+
*/
4434+
if (jsexpr->on_error->coerce ||
4435+
IsA(jsexpr->on_error->expr, CoerceViaIO) ||
4436+
IsA(jsexpr->on_error->expr, CoerceToDomain))
4437+
{
4438+
scratch->opcode = EEOP_JSONEXPR_COERCION_FINISH;
4439+
scratch->resvalue = resv;
4440+
scratch->resnull = resnull;
4441+
scratch->d.jsonexpr.jsestate = jsestate;
4442+
ExprEvalPushStep(state, scratch);
4443+
}
4444+
44224445
/* JUMP to end to skip the ON EMPTY steps added below. */
44234446
jumps_to_end = lappend_int(jumps_to_end, state->steps_len);
44244447
scratch->opcode = EEOP_JUMP;
@@ -4433,6 +4456,8 @@ ExecInitJsonExpr(JsonExpr *jsexpr, ExprState *state,
44334456
if (jsexpr->on_empty != NULL &&
44344457
jsexpr->on_empty->btype != JSON_BEHAVIOR_ERROR)
44354458
{
4459+
ErrorSaveContext *saved_escontext;
4460+
44364461
jsestate->jump_empty = state->steps_len;
44374462

44384463
/* JUMP to end if false, that is, skip the ON EMPTY expression. */
@@ -4443,14 +4468,36 @@ ExecInitJsonExpr(JsonExpr *jsexpr, ExprState *state,
44434468
scratch->d.jump.jumpdone = -1; /* set below */
44444469
ExprEvalPushStep(state, scratch);
44454470

4446-
/* Steps to evaluate the ON EMPTY expression */
4471+
/*
4472+
* Steps to evaluate the ON EMPTY expression; handle errors softly to
4473+
* rethrow them in COERCION_FINISH step that will be added later.
4474+
*/
4475+
saved_escontext = state->escontext;
4476+
state->escontext = escontext;
44474477
ExecInitExprRec((Expr *) jsexpr->on_empty->expr,
44484478
state, resv, resnull);
4479+
state->escontext = saved_escontext;
44494480

44504481
/* Step to coerce the ON EMPTY expression if needed */
44514482
if (jsexpr->on_empty->coerce)
44524483
ExecInitJsonCoercion(state, jsexpr->returning, escontext,
44534484
jsexpr->omit_quotes, resv, resnull);
4485+
4486+
/*
4487+
* Add a COERCION_FINISH step to check for errors that may occur when
4488+
* coercing and rethrow them.
4489+
*/
4490+
if (jsexpr->on_empty->coerce ||
4491+
IsA(jsexpr->on_empty->expr, CoerceViaIO) ||
4492+
IsA(jsexpr->on_empty->expr, CoerceToDomain))
4493+
{
4494+
4495+
scratch->opcode = EEOP_JSONEXPR_COERCION_FINISH;
4496+
scratch->resvalue = resv;
4497+
scratch->resnull = resnull;
4498+
scratch->d.jsonexpr.jsestate = jsestate;
4499+
ExprEvalPushStep(state, scratch);
4500+
}
44544501
}
44554502

44564503
foreach(lc, jumps_to_end)

src/backend/executor/execExprInterp.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4558,6 +4558,12 @@ ExecEvalJsonCoercionFinish(ExprState *state, ExprEvalStep *op)
45584558
*op->resvalue = (Datum) 0;
45594559
*op->resnull = true;
45604560
jsestate->error.value = BoolGetDatum(true);
4561+
4562+
/*
4563+
* Reset for next use such as for catching errors when coercing a
4564+
* JsonBehavior expression.
4565+
*/
4566+
jsestate->escontext.error_occurred = false;
45614567
}
45624568
}
45634569

src/test/regress/expected/sqljson_jsontable.out

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,11 @@ SELECT * FROM JSON_TABLE(jsonb '{"d1": "H"}', '$'
227227

228228
SELECT * FROM JSON_TABLE(jsonb '{"d1": "H"}', '$'
229229
COLUMNS (js1 jsonb_test_domain PATH '$.a2' DEFAULT 'foo'::jsonb_test_domain ON EMPTY));
230-
ERROR: value for domain jsonb_test_domain violates check constraint "jsonb_test_domain_check"
230+
js1
231+
-----
232+
233+
(1 row)
234+
231235
SELECT * FROM JSON_TABLE(jsonb '{"d1": "H"}', '$'
232236
COLUMNS (js1 jsonb_test_domain PATH '$.a2' DEFAULT 'foo1'::jsonb_test_domain ON EMPTY));
233237
js1

src/test/regress/expected/sqljson_queryfuncs.out

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1232,7 +1232,11 @@ DROP TABLE test_jsonb_mutability;
12321232
DROP FUNCTION ret_setint;
12331233
CREATE DOMAIN queryfuncs_test_domain AS text CHECK (value <> 'foo');
12341234
SELECT JSON_VALUE(jsonb '{"d1": "H"}', '$.a2' RETURNING queryfuncs_test_domain DEFAULT 'foo'::queryfuncs_test_domain ON EMPTY);
1235-
ERROR: value for domain queryfuncs_test_domain violates check constraint "queryfuncs_test_domain_check"
1235+
json_value
1236+
------------
1237+
1238+
(1 row)
1239+
12361240
SELECT JSON_VALUE(jsonb '{"d1": "H"}', '$.a2' RETURNING queryfuncs_test_domain DEFAULT 'foo1'::queryfuncs_test_domain ON EMPTY);
12371241
json_value
12381242
------------

0 commit comments

Comments
 (0)