From d32400963e34b8ab334f72903de74e00bc7f8c64 Mon Sep 17 00:00:00 2001 From: pgaddict Date: Tue, 28 Nov 2023 09:32:28 +0800 Subject: [PATCH v1 1/1] handle key words "empty array" and "empty object". transform empty array to jsonb '[]', empty object to jsonb '{}'. current implementation for "error on empty" will tranformed to "error on empty null on empty". Add a bool element to JsonBehavior, so we can disambiguate "error on empty" and "error on empty null on error". --- src/backend/executor/execExpr.c | 12 ++++--- src/backend/executor/execExprInterp.c | 2 +- src/backend/parser/parse_expr.c | 38 +++++++++++++++++++-- src/include/nodes/primnodes.h | 1 + src/test/regress/expected/jsonb_sqljson.out | 22 +++--------- 5 files changed, 50 insertions(+), 25 deletions(-) diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index cfbd9272..5138c66c 100644 --- a/src/backend/executor/execExpr.c +++ b/src/backend/executor/execExpr.c @@ -4223,7 +4223,9 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state, List *jumps_to_end = NIL; ListCell *lc; ExprEvalStep *as; - + bool throw_errors; + throw_errors = (jexpr->on_error && + jexpr->on_error->btype == JSON_BEHAVIOR_ERROR); jsestate->jsexpr = jexpr; /* @@ -4312,7 +4314,7 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state, jsestate->jump_eval_result_coercion = ExecInitJsonCoercion(scratch, state, jsestate, jexpr->result_coercion, - jexpr->on_error->btype == JSON_BEHAVIOR_ERROR, + throw_errors, resv, resnull); /* @@ -4358,7 +4360,7 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state, jsestate->eval_item_coercion_jumps[item_coercion->item_type] = ExecInitJsonCoercion(scratch, state, jsestate, coercion, - jexpr->on_error->btype == JSON_BEHAVIOR_ERROR, + throw_errors, resv, resnull); /* Emit JUMP step to skip past other coercions' steps. */ @@ -4550,8 +4552,8 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state, * Adjust remaining jump target addresses now that we have the necessary * steps in place. */ - Assert(on_error_step_off >= 0 || jexpr == NULL || - jexpr->on_error->btype == JSON_BEHAVIOR_ERROR); + Assert(on_error_step_off >= 0 || jexpr == NULL || throw_errors); + jsestate->jump_error = on_error_step_off; /* Adjust EEOP_JUMP steps */ diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index 74217fb5..d00207a1 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -4385,7 +4385,7 @@ ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op, if (jexpr->on_empty && jexpr->on_empty->btype == JSON_BEHAVIOR_ERROR) { - if (jexpr->on_error->btype == JSON_BEHAVIOR_ERROR) + if (jexpr->on_error->btype == JSON_BEHAVIOR_ERROR || jexpr->on_error->absent) ereport(ERROR, (errcode(ERRCODE_NO_SQL_JSON_ITEM), errmsg("no SQL/JSON item"))); diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 6c8918c6..0d5d32e3 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -4644,9 +4644,43 @@ transformJsonBehavior(ParseState *pstate, JsonBehavior *behavior, location = behavior->location; if (behavior_type == JSON_BEHAVIOR_DEFAULT) default_expr = transformExprRecurse(pstate, behavior->default_expr); - } - behavior = makeJsonBehavior(behavior_type, default_expr, location); + if (behavior_type == JSON_BEHAVIOR_EMPTY_OBJECT) + { + Datum d = DirectFunctionCall1(jsonb_in, CStringGetDatum("{}")); + + /* make a jsonb {} const for coercing it to returning typeid */ + default_expr = (Node *) makeConst(JSONBOID, + -1, + InvalidOid, + -1, + d, + false, /* isnull */ + false /* byval */ ); + } + + if (behavior_type == JSON_BEHAVIOR_EMPTY_ARRAY) + { + Datum d = DirectFunctionCall1(jsonb_in, CStringGetDatum("[]")); + + /* make a jsonb [] const for coercing it to returning typeid */ + default_expr = (Node *) makeConst(JSONBOID, + -1, + InvalidOid, + -1, + d, + false, /* isnull */ + false /* byval */ ); + } + + behavior = makeJsonBehavior(behavior_type, default_expr, location); + behavior->absent = false; + } + else + { + behavior = makeJsonBehavior(behavior_type, default_expr, location); + behavior->absent = true; + } /* * Also coerce the DEFAULT expression, if any, to match the returning diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index e48249f8..0a658936 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -1723,6 +1723,7 @@ typedef struct JsonBehavior Node *default_expr; /* default expression when btype is * JSON_BEHAVIOR_DEFAULT */ int location; /* token location, or -1 if unknown */ + bool absent; /* if not explicitly specified then true else false.*/ } JsonBehavior; /* diff --git a/src/test/regress/expected/jsonb_sqljson.out b/src/test/regress/expected/jsonb_sqljson.out index 8b835817..61c538f5 100644 --- a/src/test/regress/expected/jsonb_sqljson.out +++ b/src/test/regress/expected/jsonb_sqljson.out @@ -657,11 +657,7 @@ SELECT JSON_QUERY(jsonb '[]', '$[*]' EMPTY OBJECT ON EMPTY); (1 row) SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON EMPTY); - json_query ------------- - -(1 row) - +ERROR: no SQL/JSON item SELECT JSON_QUERY(jsonb '[]', '$[*]' DEFAULT '"empty"' ON EMPTY); json_query ------------ @@ -764,17 +760,9 @@ SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING bytea FORMAT JSON); (1 row) SELECT JSON_QUERY(jsonb '[1,2]', '$[*]' RETURNING bytea EMPTY OBJECT ON ERROR); - json_query ------------- - \x7b7d -(1 row) - +ERROR: cannot cast DEFAULT expression of type jsonb to bytea SELECT JSON_QUERY(jsonb '[1,2]', '$[*]' RETURNING bytea FORMAT JSON EMPTY OBJECT ON ERROR); - json_query ------------- - \x7b7d -(1 row) - +ERROR: cannot cast DEFAULT expression of type jsonb to bytea SELECT JSON_QUERY(jsonb '[1,2]', '$[*]' RETURNING json EMPTY OBJECT ON ERROR); json_query ------------ @@ -788,9 +776,9 @@ SELECT JSON_QUERY(jsonb '[1,2]', '$[*]' RETURNING jsonb EMPTY OBJECT ON ERROR); (1 row) SELECT JSON_QUERY(jsonb '[3,4]', '$[*]' RETURNING bigint[] EMPTY OBJECT ON ERROR); -ERROR: expected JSON array +ERROR: cannot cast DEFAULT expression of type jsonb to bigint[] SELECT JSON_QUERY(jsonb '"[3,4]"', '$[*]' RETURNING bigint[] EMPTY OBJECT ON ERROR); -ERROR: expected JSON array +ERROR: cannot cast DEFAULT expression of type jsonb to bigint[] -- RETUGNING pseudo-types not allowed SELECT JSON_QUERY(jsonb '[3,4]', '$[*]' RETURNING anyarray EMPTY OBJECT ON ERROR); ERROR: returning pseudo-types is not supported in SQL/JSON functions -- 2.34.1