diff options
author | Amit Langote <amitlan@postgresql.org> | 2024-07-26 16:00:16 +0900 |
---|---|---|
committer | Amit Langote <amitlan@postgresql.org> | 2024-07-26 16:00:56 +0900 |
commit | 231b7d670b218d6a5cde0574cf160c8157ab91fb (patch) | |
tree | 4d901820246068b4a1559fce75c6214a817034e9 /src/backend/executor/execExprInterp.c | |
parent | 63e6c5f4a2eeb22e0dd446a62c2b4b417d2b51f0 (diff) | |
download | postgresql-231b7d670b218d6a5cde0574cf160c8157ab91fb.tar.gz postgresql-231b7d670b218d6a5cde0574cf160c8157ab91fb.zip |
SQL/JSON: Improve error-handling of JsonBehavior expressions
Instead of returning a NULL when the JsonBehavior expression value
could not be coerced to the RETURNING type, throw the error message
informing the user that it is the JsonBehavior expression that caused
the error with the actual coercion error message shown in its DETAIL
line.
Discussion: https://postgr.es/m/CACJufxEo4sUjKCYtda0_qt9tazqqKPmF1cqhW9KBOUeJFqQd2g@mail.gmail.com
Backpatch-through: 17
Diffstat (limited to 'src/backend/executor/execExprInterp.c')
-rw-r--r-- | src/backend/executor/execExprInterp.c | 78 |
1 files changed, 70 insertions, 8 deletions
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index 4c9b2a8c178..430438f668e 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -4284,13 +4284,12 @@ ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op, memset(&jsestate->error, 0, sizeof(NullableDatum)); memset(&jsestate->empty, 0, sizeof(NullableDatum)); - /* - * Also reset ErrorSaveContext contents for the next row. Since we don't - * set details_wanted, we don't need to also reset error_data, which would - * be NULL anyway. - */ - Assert(!jsestate->escontext.details_wanted && - jsestate->escontext.error_data == NULL); + /* Also reset ErrorSaveContext contents for the next row. */ + if (jsestate->escontext.details_wanted) + { + jsestate->escontext.error_data = NULL; + jsestate->escontext.details_wanted = false; + } jsestate->escontext.error_occurred = false; switch (jsexpr->op) @@ -4400,6 +4399,14 @@ ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op, error = true; } + /* + * When setting up the ErrorSaveContext (if needed) for capturing the + * errors that occur when coercing the JsonBehavior expression, set + * details_wanted to be able to show the actual error message as the + * DETAIL of the error message that tells that it is the JsonBehavior + * expression that caused the error; see ExecEvalJsonCoercionFinish(). + */ + /* Handle ON EMPTY. */ if (empty) { @@ -4410,6 +4417,9 @@ ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op, if (jsexpr->on_empty->btype != JSON_BEHAVIOR_ERROR) { jsestate->empty.value = BoolGetDatum(true); + /* Set up to catch coercion errors of the ON EMPTY value. */ + jsestate->escontext.error_occurred = false; + jsestate->escontext.details_wanted = true; Assert(jsestate->jump_empty >= 0); return jsestate->jump_empty; } @@ -4417,6 +4427,9 @@ ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op, else if (jsexpr->on_error->btype != JSON_BEHAVIOR_ERROR) { jsestate->error.value = BoolGetDatum(true); + /* Set up to catch coercion errors of the ON ERROR value. */ + jsestate->escontext.error_occurred = false; + jsestate->escontext.details_wanted = true; Assert(!throw_error && jsestate->jump_error >= 0); return jsestate->jump_error; } @@ -4442,6 +4455,9 @@ ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op, *op->resvalue = (Datum) 0; *op->resnull = true; jsestate->error.value = BoolGetDatum(true); + /* Set up to catch coercion errors of the ON ERROR value. */ + jsestate->escontext.error_occurred = false; + jsestate->escontext.details_wanted = true; return jsestate->jump_error; } @@ -4544,9 +4560,33 @@ ExecEvalJsonCoercion(ExprState *state, ExprEvalStep *op, (Node *) escontext); } +static char * +GetJsonBehaviorValueString(JsonBehavior *behavior) +{ + /* + * The order of array elements must correspond to the order of + * JsonBehaviorType members. + */ + const char *behavior_names[] = + { + "NULL", + "ERROR", + "EMPTY", + "TRUE", + "FALSE", + "UNKNOWN", + "EMPTY ARRAY", + "EMPTY OBJECT", + "DEFAULT" + }; + + return pstrdup(behavior_names[behavior->btype]); +} + /* * Checks if an error occurred in ExecEvalJsonCoercion(). If so, this sets - * JsonExprState.error to trigger the ON ERROR handling steps. + * JsonExprState.error to trigger the ON ERROR handling steps, unless the + * error is thrown when coercing a JsonBehavior value. */ void ExecEvalJsonCoercionFinish(ExprState *state, ExprEvalStep *op) @@ -4555,8 +4595,28 @@ ExecEvalJsonCoercionFinish(ExprState *state, ExprEvalStep *op) if (SOFT_ERROR_OCCURRED(&jsestate->escontext)) { + /* + * jsestate->error or jsetate->empty being set means that the error + * occurred when coercing the JsonBehavior value. Throw the error in + * that case with the actual coercion error message shown in the + * DETAIL part. + */ + if (DatumGetBool(jsestate->error.value)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("could not coerce ON ERROR expression (%s) to the RETURNING type", + GetJsonBehaviorValueString(jsestate->jsexpr->on_error)), + errdetail("%s", jsestate->escontext.error_data->message))); + else if (DatumGetBool(jsestate->empty.value)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("could not coerce ON EMPTY expression (%s) to the RETURNING type", + GetJsonBehaviorValueString(jsestate->jsexpr->on_empty)), + errdetail("%s", jsestate->escontext.error_data->message))); + *op->resvalue = (Datum) 0; *op->resnull = true; + jsestate->error.value = BoolGetDatum(true); /* @@ -4564,6 +4624,8 @@ ExecEvalJsonCoercionFinish(ExprState *state, ExprEvalStep *op) * JsonBehavior expression. */ jsestate->escontext.error_occurred = false; + jsestate->escontext.error_occurred = false; + jsestate->escontext.details_wanted = true; } } |