From c13ad4db322c57e1fb6c040ff2f85e957b9d0e79 Mon Sep 17 00:00:00 2001
From: Amit Langote <amitlan@postgresql.org>
Date: Tue, 9 Apr 2024 20:31:30 +0900
Subject: [PATCH v1] JSON_TABLE(): mention column name in the ON EMPTY error
 message

---
 src/backend/executor/execExprInterp.c         | 36 ++++++++++---------
 src/backend/parser/parse_expr.c               |  1 +
 src/backend/parser/parse_jsontable.c          |  1 +
 src/include/nodes/parsenodes.h                |  2 ++
 src/include/nodes/primnodes.h                 |  3 ++
 .../regress/expected/sqljson_jsontable.out    |  6 ++--
 6 files changed, 30 insertions(+), 19 deletions(-)

diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 41af28cb1e..0becee67a8 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -4409,28 +4409,32 @@ ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op,
 	{
 		if (jsexpr->on_empty)
 		{
-			if (jsexpr->on_empty->btype == JSON_BEHAVIOR_ERROR)
-				ereport(ERROR,
-						errcode(ERRCODE_NO_SQL_JSON_ITEM),
-						errmsg("no SQL/JSON item"));
-			else
+			if (jsexpr->on_empty->btype != JSON_BEHAVIOR_ERROR)
+			{
 				jsestate->empty.value = BoolGetDatum(true);
+				Assert(jsestate->jump_empty >= 0);
+				return jsestate->jump_empty;
+			}
+		}
+		else if (jsexpr->on_error->btype != JSON_BEHAVIOR_ERROR)
+		{
+			jsestate->error.value = BoolGetDatum(true);
 
-			Assert(jsestate->jump_empty >= 0);
-			return jsestate->jump_empty;
+			*op->resvalue = (Datum) 0;
+			*op->resnull = true;
+			Assert(!throw_error && jsestate->jump_error >= 0);
+			return jsestate->jump_error;
 		}
-		else if (jsexpr->on_error->btype == JSON_BEHAVIOR_ERROR)
+
+		if (jsexpr->column_name)
 			ereport(ERROR,
 					errcode(ERRCODE_NO_SQL_JSON_ITEM),
-					errmsg("no SQL/JSON item"));
+					errmsg("no SQL/JSON item found for column \"%s\"",
+						   jsexpr->column_name));
 		else
-			jsestate->error.value = BoolGetDatum(true);
-
-		*op->resvalue = (Datum) 0;
-		*op->resnull = true;
-
-		Assert(!throw_error && jsestate->jump_error >= 0);
-		return jsestate->jump_error;
+			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 4c98d7a046..34ac17868b 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -4311,6 +4311,7 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
 	jsexpr = makeNode(JsonExpr);
 	jsexpr->location = func->location;
 	jsexpr->op = func->op;
+	jsexpr->column_name = func->column_name;
 
 	/*
 	 * jsonpath machinery can only handle jsonb documents, so coerce the input
diff --git a/src/backend/parser/parse_jsontable.c b/src/backend/parser/parse_jsontable.c
index 99d3101f6b..b3920f0531 100644
--- a/src/backend/parser/parse_jsontable.c
+++ b/src/backend/parser/parse_jsontable.c
@@ -327,6 +327,7 @@ transformJsonTableColumns(JsonTableParseContext *cxt, List *columns,
 
 					jfe = transformJsonTableColumn(rawc, (Node *) param,
 												   passingArgs);
+					jfe->column_name = pstrdup(rawc->name);
 
 					colexpr = transformExpr(pstate, (Node *) jfe,
 											EXPR_KIND_FROM_FUNCTION);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index f763f790b1..c0ff75e643 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1791,6 +1791,8 @@ typedef struct JsonFuncExpr
 {
 	NodeTag		type;
 	JsonExprOp	op;				/* expression type */
+	char	   *column_name;	/* JSON_TABLE() column name or NULL if this is
+								 * not for a JSON_TABLE() */
 	JsonValueExpr *context_item;	/* context item expression */
 	Node	   *pathspec;		/* JSON path specification expression */
 	List	   *passing;		/* list of PASSING clause arguments, if any */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index dafe93a4c9..9b662b8dd2 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1782,6 +1782,9 @@ typedef struct JsonExpr
 
 	JsonExprOp	op;
 
+	char	   *column_name;	/* JSON_TABLE() column name or NULL if this is
+								 * not for a JSON_TABLE() */
+
 	/* jsonb-valued expression to query */
 	Node	   *formatted_expr;
 
diff --git a/src/test/regress/expected/sqljson_jsontable.out b/src/test/regress/expected/sqljson_jsontable.out
index a00eec8a6f..849a02d9c0 100644
--- a/src/test/regress/expected/sqljson_jsontable.out
+++ b/src/test/regress/expected/sqljson_jsontable.out
@@ -492,11 +492,11 @@ FROM
 		ON true;
 ERROR:  invalid input syntax for type integer: "err"
 SELECT * FROM JSON_TABLE(jsonb '1', '$' COLUMNS (a int PATH '$.a' ERROR ON EMPTY)) jt;
-ERROR:  no SQL/JSON item
+ERROR:  no SQL/JSON item found for column "a"
 SELECT * FROM JSON_TABLE(jsonb '1', '$' COLUMNS (a int PATH 'strict $.a' ERROR ON ERROR) ERROR ON ERROR) jt;
 ERROR:  jsonpath member accessor can only be applied to an object
 SELECT * FROM JSON_TABLE(jsonb '1', '$' COLUMNS (a int PATH 'lax $.a' ERROR ON EMPTY) ERROR ON ERROR) jt;
-ERROR:  no SQL/JSON item
+ERROR:  no SQL/JSON item found for column "a"
 SELECT * FROM JSON_TABLE(jsonb '"a"', '$' COLUMNS (a int PATH '$'   DEFAULT 1 ON EMPTY DEFAULT 2 ON ERROR)) jt;
  a 
 ---
@@ -849,7 +849,7 @@ SELECT sub.* FROM s,
 		xx int path '$.c',
 		NESTED PATH '$.a.za[1]' columns (NESTED PATH '$.z21[*]' COLUMNS (z21 int path '$?(@ >= $"x")' ERROR ON ERROR))
 	)) sub;
-ERROR:  no SQL/JSON item
+ERROR:  no SQL/JSON item found for column "z21"
 -- Parent columns xx1, xx appear before NESTED ones
 SELECT sub.* FROM s,
 	(VALUES (23)) x(x), generate_series(13, 13) y,
-- 
2.43.0

