v1-0001-handle-key-words-empty-array-and-empty-object.nocfbot

application/octet-stream

Filename: v1-0001-handle-key-words-empty-array-and-empty-object.nocfbot
Type: application/octet-stream
Part: 0
Message: Re: remaining sql/json patches
From d32400963e34b8ab334f72903de74e00bc7f8c64 Mon Sep 17 00:00:00 2001
From: pgaddict <jian.universality@gmail.com>
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