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)
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)
{
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;
}
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;
}
*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;
}
(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)
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);
/*
* JsonBehavior expression.
*/
jsestate->escontext.error_occurred = false;
+ jsestate->escontext.error_occurred = false;
+ jsestate->escontext.details_wanted = true;
}
}
-- Test NULL checks execution in domain types
CREATE DOMAIN sqljsonb_int_not_null AS int NOT NULL;
SELECT JSON_VALUE(jsonb 'null', '$' RETURNING sqljsonb_int_not_null);
- json_value
-------------
-
-(1 row)
-
+ERROR: could not coerce ON ERROR expression (NULL) to the RETURNING type
+DETAIL: domain sqljsonb_int_not_null does not allow null values
SELECT JSON_VALUE(jsonb 'null', '$' RETURNING sqljsonb_int_not_null ERROR ON ERROR);
ERROR: domain sqljsonb_int_not_null does not allow null values
SELECT JSON_VALUE(jsonb 'null', '$' RETURNING sqljsonb_int_not_null DEFAULT 2 ON EMPTY ERROR ON ERROR);
(1 row)
SELECT JSON_QUERY(jsonb '{"a": 1}', '$.b' RETURNING sqljsonb_int_not_null);
- json_query
-------------
-
-(1 row)
-
+ERROR: could not coerce ON EMPTY expression (NULL) to the RETURNING type
+DETAIL: domain sqljsonb_int_not_null does not allow null values
SELECT JSON_QUERY(jsonb '{"a": 1}', '$.b' RETURNING sqljsonb_int_not_null ERROR ON EMPTY ERROR ON ERROR);
ERROR: no SQL/JSON item found for specified path
-- Test timestamptz passing and output
DROP FUNCTION ret_setint;
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);
- json_value
-------------
-
-(1 row)
-
+ERROR: could not coerce ON EMPTY expression (DEFAULT) to the RETURNING type
+DETAIL: value for domain queryfuncs_test_domain violates check constraint "queryfuncs_test_domain_check"
SELECT JSON_VALUE(jsonb '{"d1": "H"}', '$.a2' RETURNING queryfuncs_test_domain DEFAULT 'foo1'::queryfuncs_test_domain ON EMPTY);
json_value
------------