From 93a0c1e15bf4956c76e8ba1ef20cb79f71073dc2 Mon Sep 17 00:00:00 2001
From: Amit Langote <amitlan@postgresql.org>
Date: Tue, 12 Mar 2024 13:20:34 +0900
Subject: [PATCH v43 2/3] Many fixes and refactoring

---
 doc/src/sgml/func.sgml                        |  14 +-
 src/backend/executor/execExpr.c               | 259 +++++-------
 src/backend/executor/execExprInterp.c         | 255 +++++-------
 src/backend/jit/llvm/llvmjit_expr.c           |  82 +---
 src/backend/nodes/makefuncs.c                 |   6 +-
 src/backend/nodes/nodeFuncs.c                 | 119 +-----
 src/backend/parser/gram.y                     |   4 +-
 src/backend/parser/parse_expr.c               | 394 +++++++-----------
 src/backend/parser/parse_target.c             |   3 +
 src/backend/utils/adt/ruleutils.c             |   3 +
 src/include/executor/execExpr.h               |   5 +-
 src/include/nodes/execnodes.h                 |  22 +-
 src/include/nodes/makefuncs.h                 |   4 +-
 src/include/nodes/primnodes.h                 |  91 +---
 .../regress/expected/sqljson_queryfuncs.out   |  62 +--
 src/test/regress/sql/sqljson_queryfuncs.sql   |  23 +-
 src/tools/pgindent/typedefs.list              |   2 -
 17 files changed, 481 insertions(+), 867 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 70fa1f6c69..754a52fb48 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -18605,16 +18605,6 @@ $.* ? (@ like_regex "^\\d+$")
    <replaceable>path_expression</replaceable> can contain.
   </para>
 
-  <note>
-   <para>
-    SQL/JSON query functions currently only accept values of the
-    <type>jsonb</type> type, because the SQL/JSON path language only
-    supports those, so it might be necessary to cast the
-    <replaceable>context_item</replaceable> argument of these functions to
-    <type>jsonb</type>.
-   </para>
-  </note>
-
   <table id="functions-sqljson-querying">
    <title>SQL/JSON Query Functions</title>
    <tgroup cols="1">
@@ -18684,7 +18674,7 @@ ERROR:  jsonpath array subscript is out of bounds
         <optional> { <literal>ERROR</literal> | <literal>NULL</literal> | <literal>EMPTY</literal> { <optional> <literal>ARRAY</literal> </optional> | <literal>OBJECT</literal> } | <literal>DEFAULT</literal> <replaceable>expression</replaceable> } <literal>ON ERROR</literal> </optional>)
       </para>
        <para>
-        Returns the result of applying the
+        Returns the result of applying the SQL/JSON
         <replaceable>path_expression</replaceable> to the
         <replaceable>context_item</replaceable> using the
         <literal>PASSING</literal> <replaceable>value</replaceable>s.
@@ -18762,7 +18752,7 @@ DETAIL:  Missing "]" after array dimensions.
         <optional> { <literal>ERROR</literal> | <literal>NULL</literal> | <literal>DEFAULT</literal> <replaceable>expression</replaceable> } <literal>ON ERROR</literal> </optional>)
        </para>
        <para>
-        Returns the result of applying the
+        Returns the result of applying the SQL/JSON
         <replaceable>path_expression</replaceable> to the
         <replaceable>context_item</replaceable> using the
         <literal>PASSING</literal> <replaceable>value</replaceable>s.
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index e5c92efd61..2269642934 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -88,12 +88,12 @@ static void ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
 								  FunctionCallInfo fcinfo, AggStatePerTrans pertrans,
 								  int transno, int setno, int setoff, bool ishash,
 								  bool nullcheck);
-static void ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state,
+static void ExecInitJsonExpr(JsonExpr *jsexpr, ExprState *state,
 							 Datum *resv, bool *resnull,
 							 ExprEvalStep *scratch);
-static int	ExecInitJsonExprCoercion(ExprState *state, Node *coercion,
-									 ErrorSaveContext *escontext,
-									 Datum *resv, bool *resnull);
+static void ExecInitJsonCoercion(ExprState *state, JsonReturning *returning,
+					 ErrorSaveContext *escontext,
+					 Datum *resv, bool *resnull);
 
 
 /*
@@ -2434,9 +2434,9 @@ ExecInitExprRec(Expr *node, ExprState *state,
 
 		case T_JsonExpr:
 			{
-				JsonExpr   *jexpr = castNode(JsonExpr, node);
+				JsonExpr   *jsexpr = castNode(JsonExpr, node);
 
-				ExecInitJsonExpr(jexpr, state, resv, resnull, &scratch);
+				ExecInitJsonExpr(jsexpr, state, resv, resnull, &scratch);
 				break;
 			}
 
@@ -4213,26 +4213,28 @@ ExecBuildParamSetEqual(TupleDesc desc,
  * Push steps to evaluate a JsonExpr and its various subsidiary expressions.
  */
 static void
-ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state,
+ExecInitJsonExpr(JsonExpr *jsexpr, ExprState *state,
 				 Datum *resv, bool *resnull,
 				 ExprEvalStep *scratch)
 {
 	JsonExprState *jsestate = palloc0(sizeof(JsonExprState));
+	ErrorSaveContext *escontext =
+		jsexpr->on_error->btype != JSON_BEHAVIOR_ERROR ?
+		&jsestate->escontext : NULL;
 	ListCell   *argexprlc;
 	ListCell   *argnamelc;
 	List	   *jumps_if_skip = NIL;
-	List	   *jumps_to_coerce_finish = NIL;
 	List	   *jumps_to_end = NIL;
 	ListCell   *lc;
 	ExprEvalStep *as;
 
-	jsestate->jsexpr = jexpr;
+	jsestate->jsexpr = jsexpr;
 
 	/*
 	 * Evaluate formatted_expr storing the result into
 	 * jsestate->formatted_expr.
 	 */
-	ExecInitExprRec((Expr *) jexpr->formatted_expr, state,
+	ExecInitExprRec((Expr *) jsexpr->formatted_expr, state,
 					&jsestate->formatted_expr.value,
 					&jsestate->formatted_expr.isnull);
 
@@ -4247,7 +4249,7 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state,
 	 * Evaluate pathspec expression storing the result into
 	 * jsestate->pathspec.
 	 */
-	ExecInitExprRec((Expr *) jexpr->path_spec, state,
+	ExecInitExprRec((Expr *) jsexpr->path_spec, state,
 					&jsestate->pathspec.value,
 					&jsestate->pathspec.isnull);
 
@@ -4260,8 +4262,8 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state,
 
 	/* Steps to compute PASSING args. */
 	jsestate->args = NIL;
-	forboth(argexprlc, jexpr->passing_values,
-			argnamelc, jexpr->passing_names)
+	forboth(argexprlc, jsexpr->passing_values,
+			argnamelc, jsexpr->passing_names)
 	{
 		Expr	   *argexpr = (Expr *) lfirst(argexprlc);
 		String	   *argname = lfirst_node(String, argnamelc);
@@ -4300,8 +4302,8 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state,
 	scratch->d.constval.isnull = true;
 	ExprEvalPushStep(state, scratch);
 
-	/* Jump to coerce the NULL using result_coercion if present. */
-	if (jexpr->result_coercion)
+	/* Jump to coerce the NULL using coercion_expr if present. */
+	if (jsexpr->coercion_expr)
 	{
 		scratch->opcode = EEOP_JUMP;
 		scratch->d.jump.jumpdone = state->steps_len + 1;
@@ -4309,81 +4311,80 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state,
 	}
 
 	/*
-	 * Steps to coerce the result value computed by EEOP_JSONEXPR_PATH. To
-	 * handle coercion errors softly, use the following ErrorSaveContext when
-	 * initializing the coercion expressions, including any JsonCoercion
-	 * nodes.
+	 * To handle coercion errors softly, use the following ErrorSaveContext
+	 * to pass to ExecInitExprRec() when initializing the coercion expressions
+	 * and in the EEOP_JSONEXPR_COERCION step.
 	 */
 	jsestate->escontext.type = T_ErrorSaveContext;
-	if (jexpr->result_coercion)
+
+	/* Steps to coerce the result value computed by EEOP_JSONEXPR_PATH. */
+	jsestate->jump_eval_coercion_expr = -1;
+	if (jsexpr->coercion_expr)
 	{
-		jsestate->jump_eval_result_coercion =
-			ExecInitJsonExprCoercion(state, jexpr->result_coercion,
-									 jexpr->on_error->btype != JSON_BEHAVIOR_ERROR ?
-									 &jsestate->escontext : NULL,
-									 resv, resnull);
-		/* Jump to COERCION_FINISH. */
-		scratch->opcode = EEOP_JUMP;
-		scratch->d.jump.jumpdone = -1;	/* set below */
-		jumps_to_coerce_finish = lappend_int(jumps_to_coerce_finish,
-											 state->steps_len);
-		ExprEvalPushStep(state, scratch);
-	}
-	else
-		jsestate->jump_eval_result_coercion = -1;
+		Datum	   *save_innermost_caseval;
+		bool	   *save_innermost_casenull;
+		ErrorSaveContext *save_escontext;
 
-	/* Steps for coercing JsonItemType values returned by JsonPathValue(). */
-	if (jexpr->item_coercions)
+		jsestate->jump_eval_coercion_expr = state->steps_len;
+		save_innermost_caseval = state->innermost_caseval;
+		save_innermost_casenull = state->innermost_casenull;
+		save_escontext = state->escontext;
+
+		state->innermost_caseval = resv;
+		state->innermost_casenull = resnull;
+		state->escontext = escontext;
+
+		ExecInitExprRec((Expr *) jsexpr->coercion_expr, state, resv, resnull);
+
+		state->innermost_caseval = save_innermost_caseval;
+		state->innermost_casenull = save_innermost_casenull;
+		state->escontext = save_escontext;
+	}
+	else if (jsexpr->use_json_coercion)
+	{
+		jsestate->jump_eval_coercion_expr = state->steps_len;
+		ExecInitJsonCoercion(state, jsexpr->returning, escontext, resv, resnull);
+	}
+	else if (jsexpr->use_io_coercion)
 	{
 		/*
-		 * Here we create the steps for each JsonItemType type's coercion
-		 * expression and also store a flag whether the coercion is a cast
-		 * expression.  ExecPrepareJsonItemCoercion() called by
-		 * ExecEvalJsonExprPath() will map a given JsonbValue returned by
-		 * JsonPathValue() to its  JsonItemType's expression's step address
-		 * and the flag by indexing the following arrays with JsonItemType
-		 * enum value.
+		 * Only initialize the FunctionCallInfo for the target type's input
+		 * function, which is called by ExecEvalJsonExprPath() itself, so no
+		 * additional step is necessary.
 		 */
-		jsestate->num_item_coercions = list_length(jexpr->item_coercions);
-		jsestate->eval_item_coercion_jumps = (int *)
-			palloc(jsestate->num_item_coercions * sizeof(int));
-		jsestate->item_coercion_is_cast = (bool *)
-			palloc0(jsestate->num_item_coercions * sizeof(bool));
-		foreach(lc, jexpr->item_coercions)
-		{
-			JsonItemCoercion *item_coercion = lfirst(lc);
-			Node	   *coercion = item_coercion->coercion;
-
-			jsestate->item_coercion_is_cast[item_coercion->item_type] =
-				(coercion != NULL && !IsA(coercion, JsonCoercion));
-			jsestate->eval_item_coercion_jumps[item_coercion->item_type] =
-				ExecInitJsonExprCoercion(state, coercion,
-										 jexpr->on_error->btype != JSON_BEHAVIOR_ERROR ?
-										 &jsestate->escontext : NULL,
-										 resv, resnull);
-
-			/* Jump to COERCION_FINISH. */
-			scratch->opcode = EEOP_JUMP;
-			scratch->d.jump.jumpdone = -1;	/* set below */
-			jumps_to_coerce_finish = lappend_int(jumps_to_coerce_finish,
-												 state->steps_len);
-			ExprEvalPushStep(state, scratch);
-		}
+		Oid			typinput;
+		Oid			typioparam;
+		FmgrInfo   *finfo;
+		FunctionCallInfo fcinfo;
+
+		getTypeInputInfo(jsexpr->returning->typid, &typinput, &typioparam);
+		finfo = palloc0(sizeof(FmgrInfo));
+		fcinfo = palloc0(SizeForFunctionCallInfo(3));
+		fmgr_info(typinput, finfo);
+		fmgr_info_set_expr((Node *) jsexpr->returning, finfo);
+		InitFunctionCallInfoData(*fcinfo, finfo, 3, InvalidOid, NULL, NULL);
+
+		/*
+		 * We can preload the second and third arguments for the input
+		 * function, since they're constants.
+		 */
+		fcinfo->args[1].value = ObjectIdGetDatum(typioparam);
+		fcinfo->args[1].isnull = false;
+		fcinfo->args[2].value = Int32GetDatum(jsexpr->returning->typmod);
+		fcinfo->args[2].isnull = false;
+		fcinfo->context = (Node *) escontext;
+
+		jsestate->input_finfo = finfo;
+		jsestate->input_fcinfo = fcinfo;
 	}
 
 	/*
-	 * Add step to reset the ErrorSaveContext and set JsonExprState.error if
-	 * the coercion evaluation ran into an error but was not thrown because of
-	 * the ON ERROR behavior.
+	 * Add a special step to check if the coercion evaluation ran into an
+	 * error but was not thrown because of the ON ERROR behavior and set
+	 * JsonExprState.error if so.
 	 */
-	if (jexpr->result_coercion || jexpr->item_coercions)
+	if (jsestate->jump_eval_coercion_expr >= 0)
 	{
-		foreach(lc, jumps_to_coerce_finish)
-		{
-			as = &state->steps[lfirst_int(lc)];
-			as->d.jump.jumpdone = state->steps_len;
-		}
-
 		scratch->opcode = EEOP_JSONEXPR_COERCION_FINISH;
 		scratch->d.jsonexpr.jsestate = jsestate;
 		ExprEvalPushStep(state, scratch);
@@ -4396,8 +4397,8 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state,
 	 * occur during EEOP_JSONEXPR_PATH evaluation and subsequent coercion
 	 * evaluation.
 	 */
-	if (jexpr->on_error &&
-		jexpr->on_error->btype != JSON_BEHAVIOR_ERROR)
+	if (jsexpr->on_error &&
+		jsexpr->on_error->btype != JSON_BEHAVIOR_ERROR)
 	{
 		jsestate->jump_error = state->steps_len;
 		scratch->opcode = EEOP_JUMP_IF_NOT_TRUE;
@@ -4413,14 +4414,13 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state,
 		ExprEvalPushStep(state, scratch);
 
 		/* Steps to evaluate the ON ERROR expression */
-		ExecInitExprRec((Expr *) jexpr->on_error->expr,
+		ExecInitExprRec((Expr *) jsexpr->on_error->expr,
 						state, resv, resnull);
 
 		/* Steps to coerce the ON ERROR expression if needed */
-		(void) ExecInitJsonExprCoercion(state,
-										(Node *) jexpr->on_error->coercion,
-										&jsestate->escontext,
-										resv, resnull);
+		if (jsexpr->on_error->coerce)
+			ExecInitJsonCoercion(state, jsexpr->returning, escontext, resv,
+								 resnull);
 
 		jumps_to_end = lappend_int(jumps_to_end, state->steps_len);
 		scratch->opcode = EEOP_JUMP;
@@ -4429,8 +4429,8 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state,
 	}
 
 	/* Step to handle ON EMPTY behaviors. */
-	if (jexpr->on_empty != NULL &&
-		jexpr->on_empty->btype != JSON_BEHAVIOR_ERROR)
+	if (jsexpr->on_empty != NULL &&
+		jsexpr->on_empty->btype != JSON_BEHAVIOR_ERROR)
 	{
 		jsestate->jump_empty = state->steps_len;
 
@@ -4442,14 +4442,13 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state,
 		ExprEvalPushStep(state, scratch);
 
 		/* Steps to evaluate the ON EMPTY expression */
-		ExecInitExprRec((Expr *) jexpr->on_empty->expr,
+		ExecInitExprRec((Expr *) jsexpr->on_empty->expr,
 						state, resv, resnull);
 
 		/* Steps to coerce the ON EMPTY expression if needed */
-		(void) ExecInitJsonExprCoercion(state,
-										(Node *) jexpr->on_empty->coercion,
-										&jsestate->escontext,
-										resv, resnull);
+		if (jsexpr->on_empty->coerce)
+			ExecInitJsonCoercion(state, jsexpr->returning, escontext, resv,
+								 resnull);
 
 		scratch->opcode = EEOP_JUMP;
 		scratch->d.jump.jumpdone = -1;	/* set below */
@@ -4466,64 +4465,24 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state,
 	jsestate->jump_end = state->steps_len;
 }
 
-/* Initialize one JsonCoercion for execution. */
-static int
-ExecInitJsonExprCoercion(ExprState *state, Node *coercion_expr,
-						 ErrorSaveContext *escontext,
-						 Datum *resv, bool *resnull)
+/*
+ * Initialize a EEOP_JSONEXPR_COERCION step to coerce the value given in resv
+ * to the given RETURNING type.
+ */
+static void
+ExecInitJsonCoercion(ExprState *state, JsonReturning *returning,
+					 ErrorSaveContext *escontext,
+					 Datum *resv, bool *resnull)
 {
-	int			jump_eval_coercion;
-	Datum	   *save_innermost_caseval;
-	bool	   *save_innermost_casenull;
-	ErrorSaveContext *save_escontext;
-
-	if (coercion_expr == NULL)
-		return -1;
-
-	jump_eval_coercion = state->steps_len;
-	if (IsA(coercion_expr, JsonCoercion))
-	{
-		JsonCoercion *coercion = (JsonCoercion *) coercion_expr;
-		ExprEvalStep scratch = {0};
-		Oid			typinput;
-		FmgrInfo   *finfo;
-		Oid			typioparam;
-
-		getTypeInputInfo(((JsonCoercion *) coercion)->targettype,
-						 &typinput, &typioparam);
-		finfo = palloc0(sizeof(FmgrInfo));
-		fmgr_info(typinput, finfo);
-
-		scratch.opcode = EEOP_JSONEXPR_COERCION;
-		scratch.resvalue = resv;
-		scratch.resnull = resnull;
-		scratch.d.jsonexpr_coercion.coercion = coercion;
-		scratch.d.jsonexpr_coercion.input_finfo = finfo;
-		scratch.d.jsonexpr_coercion.typioparam = typioparam;
-		scratch.d.jsonexpr_coercion.json_populate_type_cache = NULL;
-		scratch.d.jsonexpr_coercion.escontext = escontext;
-		ExprEvalPushStep(state, &scratch);
-		/* Initialize the cast expression below, if any. */
-		if (coercion->cast_expr != NULL)
-			coercion_expr = coercion->cast_expr;
-		else
-			return jump_eval_coercion;
-	}
-
-	/* Push step(s) to compute cstate->coercion. */
-	save_innermost_caseval = state->innermost_caseval;
-	save_innermost_casenull = state->innermost_casenull;
-	save_escontext = state->escontext;
-
-	state->innermost_caseval = resv;
-	state->innermost_casenull = resnull;
-	state->escontext = escontext;
-
-	ExecInitExprRec((Expr *) coercion_expr, state, resv, resnull);
-
-	state->innermost_caseval = save_innermost_caseval;
-	state->innermost_casenull = save_innermost_casenull;
-	state->escontext = save_escontext;
+	ExprEvalStep scratch = {0};
 
-	return jump_eval_coercion;
+	/* For json_populate_type() */
+	scratch.opcode = EEOP_JSONEXPR_COERCION;
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	scratch.d.jsonexpr_coercion.targettype = returning->typid;
+	scratch.d.jsonexpr_coercion.targettypmod = returning->typmod;
+	scratch.d.jsonexpr_coercion.json_populate_type_cache = NULL;
+	scratch.d.jsonexpr_coercion.escontext = escontext;
+	ExprEvalPushStep(state, &scratch);
 }
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 2d6b11bb34..dea3006dec 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -180,10 +180,7 @@ static pg_attribute_always_inline void ExecAggPlainTransByRef(AggState *aggstate
 															  AggStatePerGroup pergroup,
 															  ExprContext *aggcontext,
 															  int setno);
-static void ExecPrepareJsonItemCoercion(JsonbValue *item, JsonExprState *jsestate,
-										bool throw_error,
-										int *jump_eval_item_coercion,
-										Datum *resvalue, bool *resnull);
+static char *ExecGetJsonValueItemString(JsonbValue *item, bool *resnull);
 
 /*
  * ScalarArrayOpExprHashEntry
@@ -4269,15 +4266,14 @@ ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op,
 					 ExprContext *econtext)
 {
 	JsonExprState *jsestate = op->d.jsonexpr.jsestate;
-	JsonExpr   *jexpr = jsestate->jsexpr;
+	JsonExpr   *jsexpr = jsestate->jsexpr;
 	Datum		item;
 	JsonPath   *path;
-	bool		throw_error = jexpr->on_error->btype == JSON_BEHAVIOR_ERROR;
+	bool		throw_error = jsexpr->on_error->btype == JSON_BEHAVIOR_ERROR;
 	bool	    error = false,
 				empty = false;
-
-	/* Might get overridden for JSON_VALUE_OP by an per-item coercion. */
-	int			jump_eval_coercion = jsestate->jump_eval_result_coercion;
+	int			jump_eval_coercion = jsestate->jump_eval_coercion_expr;
+	char	   *val_string = NULL;
 
 	item = jsestate->formatted_expr.value;
 	path = DatumGetJsonPathP(jsestate->pathspec.value);
@@ -4285,7 +4281,8 @@ ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op,
 	/* Set error/empty to false. */
 	memset(&jsestate->error, 0, sizeof(NullableDatum));
 	memset(&jsestate->empty, 0, sizeof(NullableDatum));
-	switch (jexpr->op)
+	jsestate->escontext.error_occurred = false;
+	switch (jsexpr->op)
 	{
 		case JSON_EXISTS_OP:
 			{
@@ -4302,12 +4299,27 @@ ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op,
 			break;
 
 		case JSON_QUERY_OP:
-			*op->resvalue = JsonPathQuery(item, path, jexpr->wrapper, &empty,
+			*op->resvalue = JsonPathQuery(item, path, jsexpr->wrapper, &empty,
 										  !throw_error ? &error : NULL,
 										  jsestate->args);
 
-			if (!error && !empty)
-				*op->resnull = (DatumGetPointer(*op->resvalue) == NULL);
+			*op->resnull = (DatumGetPointer(*op->resvalue) == NULL);
+
+			/* Handle OMIT QUOTES. */
+			if (!*op->resnull && jsexpr->omit_quotes)
+			{
+				val_string = JsonbUnquote(DatumGetJsonbP(*op->resvalue));
+
+				/*
+				 * Pass the string as a text value to the cast expression
+				 * if any.  If not, use the input function call below to
+				 * do the coercion.
+				 */
+				if (jump_eval_coercion >= 0)
+					*op->resvalue =
+						DirectFunctionCall1(textin,
+											PointerGetDatum(val_string));
+			}
 			break;
 
 		case JSON_VALUE_OP:
@@ -4318,47 +4330,69 @@ ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op,
 
 				if (jbv == NULL)
 				{
-					/* Will be coerced with result_coercion. */
+					/* Will be coerced with coercion_expr. */
 					*op->resvalue = (Datum) 0;
 					*op->resnull = true;
 				}
 				else if (!error && !empty)
 				{
-					/*
-					 * If the requested output type is json(b), use
-					 * result_coercion to do the coercion.
-					 */
-					if (jexpr->returning->typid == JSONOID ||
-						jexpr->returning->typid == JSONBOID)
+					if (jsexpr->returning->typid == JSONOID ||
+						jsexpr->returning->typid == JSONBOID)
 					{
-						*op->resvalue = JsonbPGetDatum(JsonbValueToJsonb(jbv));
-						*op->resnull = false;
+						val_string = DatumGetCString(DirectFunctionCall1(jsonb_out,
+														JsonbPGetDatum(JsonbValueToJsonb(jbv))));
 					}
 					else
 					{
+						val_string = ExecGetJsonValueItemString(jbv, op->resnull);
+
 						/*
-						 * Else, use one of the item_coercions.
-						 *
-						 * Error out if no cast expression exists.
+						 * Pass the string as a text value to the cast expression
+						 * if any.  If not, use the input function call below to
+						 * do the coercion.
 						 */
-						ExecPrepareJsonItemCoercion(jbv, jsestate, throw_error,
-													&jump_eval_coercion,
-													op->resvalue, op->resnull);
+						*op->resvalue = PointerGetDatum(val_string);
+						if (jump_eval_coercion >= 0)
+							*op->resvalue = DirectFunctionCall1(textin, *op->resvalue);
 					}
 				}
 				break;
 			}
 
 		default:
-			elog(ERROR, "unrecognized SQL/JSON expression op %d", jexpr->op);
+			elog(ERROR, "unrecognized SQL/JSON expression op %d",
+				 (int) jsexpr->op);
 			return false;
 	}
 
+	/*
+	 * Coerce the result value by calling the input function coercion.
+	 * *op->resvalue must point to C string in this case.
+	 */
+	if (!*op->resnull && jsexpr->use_io_coercion)
+	{
+		FunctionCallInfo fcinfo;
+
+		fcinfo = jsestate->input_fcinfo;
+		Assert(fcinfo != NULL);
+		Assert(val_string != NULL);
+		fcinfo->args[0].value = PointerGetDatum(val_string);
+		fcinfo->args[0].isnull = *op->resnull;
+		/* second and third arguments are already set up */
+
+		fcinfo->isnull = false;
+		*op->resvalue = FunctionCallInvoke(fcinfo);
+		if (SOFT_ERROR_OCCURRED(&jsestate->escontext))
+			error = true;
+
+		jump_eval_coercion = -1;
+	}
+
 	if (empty)
 	{
-		if (jexpr->on_empty)
+		if (jsexpr->on_empty)
 		{
-			if (jexpr->on_empty->btype == JSON_BEHAVIOR_ERROR)
+			if (jsexpr->on_empty->btype == JSON_BEHAVIOR_ERROR)
 				ereport(ERROR,
 						errcode(ERRCODE_NO_SQL_JSON_ITEM),
 						errmsg("no SQL/JSON item"));
@@ -4368,7 +4402,7 @@ ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op,
 			Assert(jsestate->jump_empty >= 0);
 			return jsestate->jump_empty;
 		}
-		else if (jexpr->on_error->btype == JSON_BEHAVIOR_ERROR)
+		else if (jsexpr->on_error->btype == JSON_BEHAVIOR_ERROR)
 			ereport(ERROR,
 					errcode(ERRCODE_NO_SQL_JSON_ITEM),
 					errmsg("no SQL/JSON item"));
@@ -4396,31 +4430,18 @@ ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op,
 }
 
 /*
- * Selects a coercion for a given JsonbValue based on its type.
- *
- * On return, *resvalue and *resnull are set to the value extracted from the
- * JsonbValue and *jump_eval_item_coercion is set to the step address of the
- * coercion expression.
+ * Convert the a given JsonbValue to its C string representation
  *
- * If the found expression is a JsonCoercion node that means the parser
- * didnt' find a cast to do the coercion, so throw an error if the
- * ON ERROR behavior says to do so.
+ * Returns the string as a Datum setting *resnull if the JsonbValue is a
+ * a jbvNull.
  */
-static void
-ExecPrepareJsonItemCoercion(JsonbValue *item, JsonExprState *jsestate,
-							bool throw_error,
-							int *jump_eval_item_coercion,
-							Datum *resvalue, bool *resnull)
+static char *
+ExecGetJsonValueItemString(JsonbValue *item, bool *resnull)
 {
-	int		   *eval_item_coercion_jumps = jsestate->eval_item_coercion_jumps;
-	bool	   *item_coercion_is_cast = jsestate->item_coercion_is_cast;
-	bool		is_cast;
-	int			jump_to;
-	JsonbValue	buf;
-
 	if (item->type == jbvBinary && JsonContainerIsScalar(item->val.binary.data))
 	{
 		bool		is_scalar PG_USED_FOR_ASSERTS_ONLY;
+		JsonbValue	buf;
 
 		is_scalar = JsonbExtractScalar(item->val.binary.data, &buf);
 		item = &buf;
@@ -4433,56 +4454,44 @@ ExecPrepareJsonItemCoercion(JsonbValue *item, JsonExprState *jsestate,
 	switch (item->type)
 	{
 		case jbvNull:
-			is_cast = item_coercion_is_cast[JsonItemTypeNull];
-			jump_to = eval_item_coercion_jumps[JsonItemTypeNull];
-			*resvalue = (Datum) 0;
 			*resnull = true;
-			break;
+			return NULL;
 
 		case jbvString:
-			is_cast = item_coercion_is_cast[JsonItemTypeString];
-			jump_to = eval_item_coercion_jumps[JsonItemTypeString];
-			*resvalue =
-				PointerGetDatum(cstring_to_text_with_len(item->val.string.val,
-														 item->val.string.len));
-			break;
+			{
+				char *str = palloc(item->val.string.len + 1);
+
+				memcpy(str, item->val.string.val, item->val.string.len);
+				str[item->val.string.len] = '\0';
+				return str;
+			}
 
 		case jbvNumeric:
-			is_cast = item_coercion_is_cast[JsonItemTypeNumeric];
-			jump_to = eval_item_coercion_jumps[JsonItemTypeNumeric];
-			*resvalue = NumericGetDatum(item->val.numeric);
-			break;
+			return DatumGetCString(DirectFunctionCall1(numeric_out,
+												   NumericGetDatum(item->val.numeric)));
 
 		case jbvBool:
-			is_cast = item_coercion_is_cast[JsonItemTypeBoolean];
-			jump_to = eval_item_coercion_jumps[JsonItemTypeBoolean];
-			*resvalue = BoolGetDatum(item->val.boolean);
-			break;
+			return DatumGetCString(DirectFunctionCall1(boolout,
+									   BoolGetDatum(item->val.boolean)));
 
 		case jbvDatetime:
-			*resvalue = item->val.datetime.value;
 			switch (item->val.datetime.typid)
 			{
 				case DATEOID:
-					is_cast = item_coercion_is_cast[JsonItemTypeDate];
-					jump_to = eval_item_coercion_jumps[JsonItemTypeDate];
-					break;
+					return DatumGetCString(DirectFunctionCall1(date_out,
+											   item->val.datetime.value));
 				case TIMEOID:
-					is_cast = item_coercion_is_cast[JsonItemTypeTime];
-					jump_to = eval_item_coercion_jumps[JsonItemTypeTime];
-					break;
+					return DatumGetCString(DirectFunctionCall1(time_out,
+											   item->val.datetime.value));
 				case TIMETZOID:
-					is_cast = item_coercion_is_cast[JsonItemTypeTimetz];
-					jump_to = eval_item_coercion_jumps[JsonItemTypeTimetz];
-					break;
+					return DatumGetCString(DirectFunctionCall1(timetz_out,
+											   item->val.datetime.value));
 				case TIMESTAMPOID:
-					is_cast = item_coercion_is_cast[JsonItemTypeTimestamp];
-					jump_to = eval_item_coercion_jumps[JsonItemTypeTimestamp];
-					break;
+					return DatumGetCString(DirectFunctionCall1(timestamp_out,
+											   item->val.datetime.value));
 				case TIMESTAMPTZOID:
-					is_cast = item_coercion_is_cast[JsonItemTypeTimestamptz];
-					jump_to = eval_item_coercion_jumps[JsonItemTypeTimestamptz];
-					break;
+					return DatumGetCString(DirectFunctionCall1(timestamptz_out,
+											   item->val.datetime.value));
 				default:
 					elog(ERROR, "unexpected jsonb datetime type oid %u",
 						 item->val.datetime.typid);
@@ -4492,37 +4501,21 @@ ExecPrepareJsonItemCoercion(JsonbValue *item, JsonExprState *jsestate,
 		case jbvArray:
 		case jbvObject:
 		case jbvBinary:
-			is_cast = item_coercion_is_cast[JsonItemTypeComposite];
-			jump_to = eval_item_coercion_jumps[JsonItemTypeComposite];
-			*resvalue = JsonbPGetDatum(JsonbValueToJsonb(item));
-			break;
+			return DatumGetCString(DirectFunctionCall1(jsonb_out,
+									   JsonbPGetDatum(JsonbValueToJsonb(item))));
 
 		default:
 			elog(ERROR, "unexpected jsonb value type %d", item->type);
 	}
 
-	/* If the expression is not a cast expression, throw an error. */
-	if (jump_to >= 0 && !is_cast)
-	{
-		if (throw_error)
-			ereport(ERROR,
-					errcode(ERRCODE_SQL_JSON_ITEM_CANNOT_BE_CAST_TO_TARGET_TYPE),
-					errmsg("SQL/JSON item cannot be cast to target type"));
-
-		*resvalue = (Datum) 0;
-		*resnull = true;
-	}
-
-	*jump_eval_item_coercion = jump_to;
+	Assert(false);
+	*resnull = true;
+	return NULL;
 }
 
 /*
- * Coerce a jsonb or a scalar string value produced by ExecEvalJsonExprPath()
- * or an ON ERROR / EMPTY behavior expression to the target type.
- *
- * This is also responsible for removing any quotes present in the source
- * value if JsonCoercion.omit_quotes is true before coercing to the target
- * type.
+ * Coerce a jsonb value produced by ExecEvalJsonExprPath() or an ON ERROR /
+ * ON EMPTY behavior expression to the target type.
  *
  * Any soft errors that occur here will be checked by
  * EEOP_JSONEXPR_COERCION_FINISH that will run after this.
@@ -4531,48 +4524,14 @@ void
 ExecEvalJsonCoercion(ExprState *state, ExprEvalStep *op,
 					 ExprContext *econtext)
 {
-	JsonCoercion *coercion = op->d.jsonexpr_coercion.coercion;
 	ErrorSaveContext *escontext = op->d.jsonexpr_coercion.escontext;
-	Datum		res = *op->resvalue;
 
-	/*
-	 * Handle OMIT QUOTES.
-	 *
-	 * Normally, json_populate_type() is the place to coerce jsonb values to
-	 * the requested target type, including those that are scalar strings, but
-	 * it doesn't have the support for removing quotes to implement the
-	 * OMIT QUOTES clause, so we handle it here.
-	 */
-	if (coercion->omit_quotes)
-	{
-		FmgrInfo   *input_finfo = op->d.jsonexpr_coercion.input_finfo;
-		Oid			typioparam = op->d.jsonexpr_coercion.typioparam;
-		char	   *val = !*op->resnull ?
-			JsonbUnquote(DatumGetJsonbP(res)) : NULL;
-
-		/*
-		 * If the coercion must be done using a cast expression, pass to it
-		 * the text version of the quote-stripped string.  If not, finish the
-		 * coercion by calling the input function.
-		 */
-		if (coercion->cast_expr)
-			*op->resvalue = DirectFunctionCall1(textin,
-												CStringGetDatum(val));
-		else
-			(void) InputFunctionCallSafe(input_finfo, val, typioparam,
-										 coercion->targettypmod,
-										 (Node *) escontext,
-										 op->resvalue);
-	}
-	else
-	{
-		*op->resvalue = json_populate_type(res, JSONBOID,
-										   coercion->targettype,
-										   coercion->targettypmod,
-										   &op->d.jsonexpr_coercion.json_populate_type_cache,
-										   econtext->ecxt_per_query_memory,
-										   op->resnull, (Node *) escontext);
-	}
+	*op->resvalue = json_populate_type(*op->resvalue, JSONBOID,
+									   op->d.jsonexpr_coercion.targettype,
+									   op->d.jsonexpr_coercion.targettypmod,
+									   &op->d.jsonexpr_coercion.json_populate_type_cache,
+									   econtext->ecxt_per_query_memory,
+									   op->resnull, (Node *) escontext);
 }
 
 /*
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index 0c4d5953dc..2c49100259 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -1950,10 +1950,8 @@ llvm_compile_expr(ExprState *state)
 					 */
 					if (jsestate->jump_empty >= 0 ||
 						jsestate->jump_error >= 0 ||
-						jsestate->jump_eval_result_coercion >= 0 ||
-						jsestate->num_item_coercions > 0)
+						jsestate->jump_eval_coercion_expr >= 0)
 					{
-						int			i;
 						LLVMValueRef v_jump_empty;
 						LLVMValueRef v_jump_error;
 						LLVMValueRef v_jump_coercion;
@@ -1961,8 +1959,7 @@ llvm_compile_expr(ExprState *state)
 						LLVMBasicBlockRef b_done,
 									b_empty,
 									b_error,
-									b_result_coercion,
-								   *b_item_coercions = NULL;
+									b_coercion_expr;
 
 						b_empty =
 							l_bb_before_v(opblocks[opno + 1],
@@ -1970,21 +1967,9 @@ llvm_compile_expr(ExprState *state)
 						b_error =
 							l_bb_before_v(opblocks[opno + 1],
 										  "op.%d.jsonexpr_error", opno);
-						b_result_coercion =
+						b_coercion_expr =
 							l_bb_before_v(opblocks[opno + 1],
-										  "op.%d.jsonexpr_result_coercion", opno);
-						if (jsestate->num_item_coercions > 0)
-						{
-							b_item_coercions = palloc(sizeof(LLVMBasicBlockRef) *
-													  jsestate->num_item_coercions);
-							for (i = 0; i < jsestate->num_item_coercions; i++)
-							{
-								b_item_coercions[i] =
-									l_bb_before_v(opblocks[opno + 1],
-												  "op.%d.jsonexpr_item_coercion.%d",
-												  opno, i);
-							}
-						}
+										  "op.%d.jsonexpr_coercion_expr", opno);
 						b_done =
 							l_bb_before_v(opblocks[opno + 1],
 										  "op.%d.jsonexpr_done", opno);
@@ -1992,66 +1977,45 @@ llvm_compile_expr(ExprState *state)
 						v_switch = LLVMBuildSwitch(b,
 												   v_ret,
 												   b_done,
-												   jsestate->num_item_coercions + 3);
+												   3);
 						/* Returned jsestate->jump_empty? */
 						if (jsestate->jump_empty >= 0)
 						{
 							v_jump_empty = l_int32_const(lc, jsestate->jump_empty);
 							LLVMAddCase(v_switch, v_jump_empty, b_empty);
 						}
-						/* Returned jsestate->jump_error? */
-						if (jsestate->jump_error >= 0)
-						{
-							v_jump_error = l_int32_const(lc, jsestate->jump_error);
-							LLVMAddCase(v_switch, v_jump_error, b_error);
-						}
-						/* Returned jsestate->jump_eval_result_coercion? */
-						if (jsestate->jump_eval_result_coercion >= 0)
-						{
-							v_jump_coercion = l_int32_const(lc, jsestate->jump_eval_result_coercion);
-							LLVMAddCase(v_switch, v_jump_coercion, b_result_coercion);
-						}
-
-						/*
-						 * Returned one of
-						 * jsestate->eval_item_coercion_jumps[]?
-						 */
-						for (i = 0; i < jsestate->num_item_coercions; i++)
-						{
-							if (jsestate->eval_item_coercion_jumps[i] >= 0)
-							{
-								v_jump_coercion = l_int32_const(lc, jsestate->eval_item_coercion_jumps[i]);
-								LLVMAddCase(v_switch, v_jump_coercion, b_item_coercions[i]);
-							}
-						}
-
 						/* ON EMPTY code */
 						LLVMPositionBuilderAtEnd(b, b_empty);
 						if (jsestate->jump_empty >= 0)
 							LLVMBuildBr(b, opblocks[jsestate->jump_empty]);
 						else
 							LLVMBuildUnreachable(b);
+
+						/* Returned jsestate->jump_error? */
+						if (jsestate->jump_error >= 0)
+						{
+							v_jump_error = l_int32_const(lc, jsestate->jump_error);
+							LLVMAddCase(v_switch, v_jump_error, b_error);
+						}
 						/* ON ERROR code */
 						LLVMPositionBuilderAtEnd(b, b_error);
 						if (jsestate->jump_error >= 0)
 							LLVMBuildBr(b, opblocks[jsestate->jump_error]);
 						else
 							LLVMBuildUnreachable(b);
-						/* result_coercion code */
-						LLVMPositionBuilderAtEnd(b, b_result_coercion);
-						if (jsestate->jump_eval_result_coercion >= 0)
-							LLVMBuildBr(b, opblocks[jsestate->jump_eval_result_coercion]);
-						else
-							LLVMBuildUnreachable(b);
-						/* item coercion code blocks */
-						for (i = 0; i < jsestate->num_item_coercions; i++)
+
+						/* Returned jsestate->jump_eval_coercion_expr? */
+						if (jsestate->jump_eval_coercion_expr >= 0)
 						{
-							LLVMPositionBuilderAtEnd(b, b_item_coercions[i]);
-							if (jsestate->eval_item_coercion_jumps[i] >= 0)
-								LLVMBuildBr(b, opblocks[jsestate->eval_item_coercion_jumps[i]]);
-							else
-								LLVMBuildUnreachable(b);
+							v_jump_coercion = l_int32_const(lc, jsestate->jump_eval_coercion_expr);
+							LLVMAddCase(v_switch, v_jump_coercion, b_coercion_expr);
 						}
+						/* coercion_expr code */
+						LLVMPositionBuilderAtEnd(b, b_coercion_expr);
+						if (jsestate->jump_eval_coercion_expr >= 0)
+							LLVMBuildBr(b, opblocks[jsestate->jump_eval_coercion_expr]);
+						else
+							LLVMBuildUnreachable(b);
 
 						LLVMPositionBuilderAtEnd(b, b_done);
 					}
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index 538ccb30aa..b13cfa4201 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -861,14 +861,12 @@ makeJsonValueExpr(Expr *raw_expr, Expr *formatted_expr,
  *	  creates a JsonBehavior node
  */
 JsonBehavior *
-makeJsonBehavior(JsonBehaviorType type, Node *expr, JsonCoercion *coercion,
-				 int location)
+makeJsonBehavior(JsonBehaviorType btype, Node *expr, int location)
 {
 	JsonBehavior *behavior = makeNode(JsonBehavior);
 
-	behavior->btype = type;
+	behavior->btype = btype;
 	behavior->expr = expr;
-	behavior->coercion = coercion;
 	behavior->location = location;
 
 	return behavior;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index d794192fae..aefdcdb9ec 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -243,24 +243,11 @@ exprType(const Node *expr)
 				type = jexpr->returning->typid;
 				break;
 			}
-		case T_JsonCoercion:
-			{
-				const JsonCoercion *coercion = (const JsonCoercion *) expr;
-
-				type = coercion->targettype;
-				break;
-			}
-		case T_JsonItemCoercion:
-			type = exprType(((JsonItemCoercion *) expr)->coercion);
-			break;
 		case T_JsonBehavior:
 			{
 				const JsonBehavior *behavior = (const JsonBehavior *) expr;
 
-				if (behavior->coercion)
-					type = exprType((Node *) behavior->coercion);
-				else
-					type = exprType(behavior->expr);
+				type = exprType(behavior->expr);
 				break;
 			}
 		case T_NullTest:
@@ -527,23 +514,11 @@ exprTypmod(const Node *expr)
 				return jexpr->returning->typmod;
 			}
 			break;
-		case T_JsonCoercion:
-			{
-				const JsonCoercion *coercion = (const JsonCoercion *) expr;
-
-				return coercion->targettypmod;
-			}
-			break;
-		case T_JsonItemCoercion:
-			return exprTypmod(((JsonItemCoercion *) expr)->coercion);
 		case T_JsonBehavior:
 			{
 				const JsonBehavior *behavior = (const JsonBehavior *) expr;
 
-				if (behavior->coercion)
-					return exprTypmod((Node *) behavior->coercion);
-				else
-					return exprTypmod(behavior->expr);
+				return exprTypmod(behavior->expr);
 			}
 			break;
 		case T_CoerceToDomain:
@@ -1026,21 +1001,16 @@ exprCollation(const Node *expr)
 			coll = InvalidOid;	/* ... so it has no collation */
 			break;
 		case T_JsonExpr:
-			coll = exprCollation(((JsonExpr *) expr)->result_coercion);
-			break;
-		case T_JsonCoercion:
-			coll = ((const JsonCoercion *) expr)->collation;
-			break;
-		case T_JsonItemCoercion:
-			coll = exprCollation(((JsonItemCoercion *) expr)->coercion);
+			if (((JsonExpr *) expr)->coercion_expr)
+				coll = exprCollation(((JsonExpr *) expr)->coercion_expr);
+			else
+				coll = ((JsonExpr *) expr)->collation;
 			break;
 		case T_JsonBehavior:
 			{
 				JsonBehavior *behavior = (JsonBehavior *) expr;
 
-				if (behavior->coercion)
-					coll = exprCollation((Node *) behavior->coercion);
-				else if (behavior->expr)
+				if (behavior->expr)
 					coll = exprCollation(behavior->expr);
 				else
 					coll = InvalidOid;
@@ -1289,28 +1259,10 @@ exprSetCollation(Node *expr, Oid collation)
 			{
 				JsonExpr   *jexpr = (JsonExpr *) expr;
 
-				if (jexpr->result_coercion)
-					exprSetCollation((Node *) jexpr->result_coercion, collation);
+				if (jexpr->coercion_expr)
+					exprSetCollation((Node *) jexpr->coercion_expr, collation);
 				else
-					Assert(!OidIsValid(collation)); /* result is always a
-													 * json[b] type */
-			}
-			break;
-		case T_JsonItemCoercion:
-			{
-				JsonItemCoercion *item_coercion = (JsonItemCoercion *) expr;
-
-				if (item_coercion->coercion)
-					exprSetCollation(item_coercion->coercion, collation);
-			}
-			break;
-		case T_JsonCoercion:
-			{
-				JsonCoercion *coercion = (JsonCoercion *) expr;
-
-				if (coercion->cast_expr)
-					exprSetCollation(coercion->cast_expr, collation);
-				coercion->collation = collation;
+					jexpr->collation = collation;
 			}
 			break;
 		case T_JsonBehavior:
@@ -1319,8 +1271,6 @@ exprSetCollation(Node *expr, Oid collation)
 
 				if (behavior->expr)
 					exprSetCollation(behavior->expr, collation);
-				if (behavior->coercion)
-					exprSetCollation((Node *) behavior->coercion, collation);
 			}
 			break;
 		case T_NullTest:
@@ -2400,9 +2350,9 @@ expression_tree_walker_impl(Node *node,
 
 				if (WALK(jexpr->formatted_expr))
 					return true;
-				if (WALK(jexpr->result_coercion))
+				if (WALK(jexpr->path_spec))
 					return true;
-				if (WALK(jexpr->item_coercions))
+				if (WALK(jexpr->coercion_expr))
 					return true;
 				if (WALK(jexpr->passing_values))
 					return true;
@@ -2413,30 +2363,12 @@ expression_tree_walker_impl(Node *node,
 					return true;
 			}
 			break;
-		case T_JsonCoercion:
-			{
-				JsonCoercion *coercion = (JsonCoercion *) node;
-
-				if (WALK(coercion->cast_expr))
-					return true;
-			}
-			break;
-		case T_JsonItemCoercion:
-			{
-				JsonItemCoercion *item_coercion = (JsonItemCoercion *) node;
-
-				if (WALK(item_coercion->coercion))
-					return true;
-			}
-			break;
 		case T_JsonBehavior:
 			{
 				JsonBehavior *behavior = (JsonBehavior *) node;
 
 				if (WALK(behavior->expr))
 					return true;
-				if (WALK(behavior->coercion))
-					return true;
 			}
 			break;
 		case T_NullTest:
@@ -3449,10 +3381,9 @@ expression_tree_mutator_impl(Node *node,
 				JsonExpr   *newnode;
 
 				FLATCOPY(newnode, jexpr, JsonExpr);
-				MUTATE(newnode->path_spec, jexpr->path_spec, Node *);
 				MUTATE(newnode->formatted_expr, jexpr->formatted_expr, Node *);
-				MUTATE(newnode->result_coercion, jexpr->result_coercion, Node *);
-				MUTATE(newnode->item_coercions, jexpr->item_coercions, List *);
+				MUTATE(newnode->path_spec, jexpr->path_spec, Node *);
+				MUTATE(newnode->coercion_expr, jexpr->coercion_expr, Node *);
 				MUTATE(newnode->passing_values, jexpr->passing_values, List *);
 				/* assume mutator does not care about passing_names */
 				MUTATE(newnode->on_empty, jexpr->on_empty, JsonBehavior *);
@@ -3460,25 +3391,6 @@ expression_tree_mutator_impl(Node *node,
 				return (Node *) newnode;
 			}
 			break;
-		case T_JsonCoercion:
-			{
-				JsonCoercion *coercion = (JsonCoercion *) node;
-				JsonCoercion *newnode;
-
-				FLATCOPY(newnode, coercion, JsonCoercion);
-				MUTATE(newnode->cast_expr, coercion->cast_expr, Node *);
-				return (Node *) newnode;
-			}
-		case T_JsonItemCoercion:
-			{
-				JsonItemCoercion *item_coercion = (JsonItemCoercion *) node;
-				JsonItemCoercion *newnode;
-
-				FLATCOPY(newnode, item_coercion, JsonItemCoercion);
-				MUTATE(newnode->coercion, item_coercion->coercion, Node *);
-				return (Node *) newnode;
-			}
-			break;
 		case T_JsonBehavior:
 			{
 				JsonBehavior *behavior = (JsonBehavior *) node;
@@ -3486,7 +3398,6 @@ expression_tree_mutator_impl(Node *node,
 
 				FLATCOPY(newnode, behavior, JsonBehavior);
 				MUTATE(newnode->expr, behavior->expr, Node *);
-				MUTATE(newnode->coercion, behavior->coercion, JsonCoercion *);
 				return (Node *) newnode;
 			}
 			break;
@@ -4205,8 +4116,6 @@ raw_expression_tree_walker_impl(Node *node,
 
 				if (WALK(jb->expr))
 					return true;
-				if (WALK(jb->coercion))
-					return true;
 			}
 			break;
 		case T_NullTest:
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 91354b8178..c247eefb0c 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -16632,9 +16632,9 @@ json_wrapper_behavior:
 
 json_behavior:
 			DEFAULT a_expr
-				{ $$ = (Node *) makeJsonBehavior(JSON_BEHAVIOR_DEFAULT, $2, NULL, @1); }
+				{ $$ = (Node *) makeJsonBehavior(JSON_BEHAVIOR_DEFAULT, $2, @1); }
 			| json_behavior_type
-				{ $$ = (Node *) makeJsonBehavior($1, NULL, NULL, @1); }
+				{ $$ = (Node *) makeJsonBehavior($1, NULL, @1); }
 		;
 
 json_behavior_type:
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 5a5130b160..a898449ed4 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -93,19 +93,10 @@ static Node *transformJsonScalarExpr(ParseState *pstate, JsonScalarExpr *expr);
 static Node *transformJsonSerializeExpr(ParseState *pstate,
 										JsonSerializeExpr *expr);
 static Node *transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *p);
-static JsonExpr *transformJsonExprCommon(ParseState *pstate, JsonFuncExpr *func,
-										 const char *constructName);
 static void transformJsonPassingArgs(ParseState *pstate, const char *constructName,
 									 JsonFormatType format, List *args,
 									 List **passing_values, List **passing_names);
-static Node *coerceJsonFuncExprOutput(ParseState *pstate, JsonExpr *jsexpr);
-static Oid	JsonFuncExprDefaultReturnType(JsonExpr *jsexpr);
-static Node *coerceJsonExpr(ParseState *pstate, Node *expr,
-							const JsonReturning *returning, bool omit_quotes);
-static JsonCoercion *makeJsonCoercion(const JsonReturning *returning,
-									  bool omit_quotes, Node *cast_expr);
-static List *InitJsonItemCoercions(ParseState *pstate, const JsonReturning *returning,
-								   Oid contextItemTypeId);
+static void coerceJsonExprOutput(ParseState *pstate, JsonExpr *jsexpr);
 static JsonBehavior *transformJsonBehavior(ParseState *pstate, JsonBehavior *behavior,
 										   JsonBehaviorType default_behavior,
 										   JsonReturning *returning);
@@ -4258,22 +4249,27 @@ transformJsonSerializeExpr(ParseState *pstate, JsonSerializeExpr *expr)
 static Node *
 transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
 {
-	JsonExpr   *jsexpr = NULL;
+	JsonExpr   *jsexpr;
+	Node	   *path_spec;
 	const char *func_name = NULL;
+	JsonFormatType default_format;
 
 	switch (func->op)
 	{
 		case JSON_EXISTS_OP:
 			func_name = "JSON_EXISTS";
+			default_format = JS_FORMAT_DEFAULT;
 			break;
 		case JSON_QUERY_OP:
 			func_name = "JSON_QUERY";
+			default_format = JS_FORMAT_JSONB;
 			break;
 		case JSON_VALUE_OP:
 			func_name = "JSON_VALUE";
+			default_format = JS_FORMAT_DEFAULT;
 			break;
 		default:
-			elog(ERROR, "invalid JsonFuncExpr op");
+			elog(ERROR, "invalid JsonFuncExpr op %d", (int) func->op);
 			break;
 	}
 
@@ -4289,7 +4285,7 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
 			format->encoding != JS_ENC_DEFAULT)
 			ereport(ERROR,
 					errcode(ERRCODE_SYNTAX_ERROR),
-					errmsg("cannot specify FORMAT in RETURNING clause of %s()",
+					errmsg("cannot specify FORMAT JSON in RETURNING clause of %s()",
 						   func_name),
 					parser_errposition(pstate, format->location));
 	}
@@ -4304,7 +4300,49 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
 				errmsg("SQL/JSON QUOTES behavior must not be specified when WITH WRAPPER is used"),
 				parser_errposition(pstate, func->location));
 
-	jsexpr = transformJsonExprCommon(pstate, func, func_name);
+	jsexpr = makeNode(JsonExpr);
+	jsexpr->location = func->location;
+	jsexpr->op = func->op;
+	jsexpr->formatted_expr = transformJsonValueExpr(pstate, func_name,
+													func->context_item,
+													default_format,
+													JSONBOID, false);
+
+	/*
+	 * jsonpath machinery can only handle jsonb documents, so coerce the input
+	 * if not already of jsonb type.
+	 */
+	if (exprType(jsexpr->formatted_expr) != JSONBOID)
+		jsexpr->formatted_expr = coerce_to_specific_type(pstate,
+														 jsexpr->formatted_expr, JSONBOID,
+														 func_name);
+	jsexpr->format = func->context_item->format;
+
+	path_spec = transformExprRecurse(pstate, func->pathspec);
+	path_spec =	coerce_to_target_type(pstate, path_spec, exprType(path_spec),
+									  JSONPATHOID, -1,
+									  COERCION_EXPLICIT, COERCE_IMPLICIT_CAST,
+									  exprLocation(path_spec));
+	if (path_spec == NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("JSON path expression must be of type %s, not of type %s",
+						"jsonpath", format_type_be(exprType(path_spec))),
+				 parser_errposition(pstate, exprLocation(path_spec))));
+	jsexpr->path_spec = path_spec;
+
+	/*
+	 * Transform and coerce to json[b] passing arguments, whose format is
+	 * determined by context item type.
+	 */
+	transformJsonPassingArgs(pstate, func_name,
+							 exprType(jsexpr->formatted_expr) == JSONBOID ?
+							 JS_FORMAT_JSONB : JS_FORMAT_JSON,
+							 func->passing,
+							 &jsexpr->passing_values,
+							 &jsexpr->passing_names);
+
+	jsexpr->returning = transformJsonOutput(pstate, func->output, false);
 
 	switch (func->op)
 	{
@@ -4335,11 +4373,11 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
 			{
 				JsonReturning *ret = jsexpr->returning;
 
-				ret->typid = JsonFuncExprDefaultReturnType(jsexpr);
+				ret->typid = JSONBOID;
 				ret->typmod = -1;
 			}
 
-			jsexpr->result_coercion = coerceJsonFuncExprOutput(pstate, jsexpr);
+			coerceJsonExprOutput(pstate, jsexpr);
 
 			if (func->on_empty)
 				jsexpr->on_empty = transformJsonBehavior(pstate,
@@ -4353,7 +4391,7 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
 
 		case JSON_VALUE_OP:
 			/* Always omit quotes from scalar strings. */
-			jsexpr->omit_quotes = (func->quotes == JS_QUOTES_OMIT);
+			jsexpr->omit_quotes = true;
 
 			/* JSON_VALUE returns text by default. */
 			if (!OidIsValid(jsexpr->returning->typid))
@@ -4369,16 +4407,7 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
 			jsexpr->returning->format->format_type = JS_FORMAT_DEFAULT;
 			jsexpr->returning->format->encoding = JS_ENC_DEFAULT;
 
-			jsexpr->result_coercion = coerceJsonFuncExprOutput(pstate, jsexpr);
-
-			/*
-			 * Initialize expressions to coerce the scalar value returned by
-			 * JsonPathValue() to the "returning" type.
-			 */
-			if (jsexpr->result_coercion)
-				jsexpr->item_coercions =
-					InitJsonItemCoercions(pstate, jsexpr->returning,
-										  exprType(jsexpr->formatted_expr));
+			coerceJsonExprOutput(pstate, jsexpr);
 
 			if (func->on_empty)
 				jsexpr->on_empty = transformJsonBehavior(pstate,
@@ -4391,70 +4420,13 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
 			break;
 
 		default:
-			elog(ERROR, "invalid JsonFuncExpr op");
+			elog(ERROR, "invalid JsonFuncExpr op %d", (int) func->op);
 			break;
 	}
 
 	return (Node *) jsexpr;
 }
 
-/*
- * Common code for JSON_VALUE, JSON_QUERY, JSON_EXISTS transformation
- * into a JsonExpr node.
- */
-static JsonExpr *
-transformJsonExprCommon(ParseState *pstate, JsonFuncExpr *func,
-						const char *constructName)
-{
-	JsonExpr   *jsexpr = makeNode(JsonExpr);
-	Node	   *path_spec;
-
-	jsexpr->location = func->location;
-	jsexpr->op = func->op;
-	jsexpr->formatted_expr = transformJsonValueExpr(pstate, constructName,
-													func->context_item,
-													JS_FORMAT_JSON,
-													InvalidOid, false);
-
-	if (exprType(jsexpr->formatted_expr) != JSONBOID)
-		ereport(ERROR,
-				errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				errmsg("%s() is not yet implemented for the json type",
-					   constructName),
-				errhint("Try casting the argument to jsonb"),
-				parser_errposition(pstate, exprLocation(jsexpr->formatted_expr)));
-
-	jsexpr->format = func->context_item->format;
-
-	path_spec = transformExprRecurse(pstate, func->pathspec);
-	jsexpr->path_spec =
-		coerce_to_target_type(pstate, path_spec, exprType(path_spec),
-							  JSONPATHOID, -1,
-							  COERCION_EXPLICIT, COERCE_IMPLICIT_CAST,
-							  exprLocation(path_spec));
-	if (!jsexpr->path_spec)
-		ereport(ERROR,
-				(errcode(ERRCODE_DATATYPE_MISMATCH),
-				 errmsg("JSON path expression must be of type %s, not of type %s",
-						"jsonpath", format_type_be(exprType(path_spec))),
-				 parser_errposition(pstate, exprLocation(path_spec))));
-
-	/*
-	 * Transform and coerce to json[b] passing arguments, whose format is
-	 * determined by context item type.
-	 */
-	transformJsonPassingArgs(pstate, constructName,
-							 exprType(jsexpr->formatted_expr) == JSONBOID ?
-							 JS_FORMAT_JSONB : JS_FORMAT_JSON,
-							 func->passing,
-							 &jsexpr->passing_values,
-							 &jsexpr->passing_names);
-
-	jsexpr->returning = transformJsonOutput(pstate, func->output, false);
-
-	return jsexpr;
-}
-
 /*
  * Transform a JSON PASSING clause.
  */
@@ -4481,29 +4453,47 @@ transformJsonPassingArgs(ParseState *pstate, const char *constructName,
 }
 
 /*
- * Create an expression to coerce the output of JSON_VALUE() / JSON_QUERY()
- * to the output type, if needed.
+ * Set up to coerce the result value of JSON_VALUE() / JSON_QUERY() to the
+ * RETURNING type (default or user-specified), if needed.
  */
-static Node *
-coerceJsonFuncExprOutput(ParseState *pstate, JsonExpr *jsexpr)
+static void
+coerceJsonExprOutput(ParseState *pstate, JsonExpr *jsexpr)
 {
 	JsonReturning *returning = jsexpr->returning;
 	Node	   *context_item = jsexpr->formatted_expr;
-	Node	   *coercion_expr = NULL;
 	int			default_typmod;
 	Oid			default_typid;
 	bool		omit_quotes =
 		jsexpr->op == JSON_QUERY_OP && jsexpr->omit_quotes;
+	Node	   *coercion_expr = NULL;
 
 	Assert(returning);
 
 	/*
-	 * Cast functions from jsonb to the following types (jsonb_bool() et al)
-	 * don't handle errors softly, so force to use json_populate_type() using
-	 * a JsonCoercion node so that any errors are handled appropriately.
+	 * Check for cases where the coercion should be handled at runtime, that
+	 * is, without using a cast expression.
 	 */
-	if (jsexpr->op == JSON_QUERY_OP)
+	if (jsexpr->op == JSON_VALUE_OP)
 	{
+		/*
+		 * Use cast expressions for types with typmod and domain types.
+		 */
+		if (returning->typmod == -1 &&
+			get_typtype(returning->typid) != TYPTYPE_DOMAIN)
+		{
+			jsexpr->use_io_coercion = true;
+			return;
+		}
+	}
+	else if (jsexpr->op == JSON_QUERY_OP)
+	{
+		/*
+		 * Cast functions from jsonb to the following types (jsonb_bool() et
+		 * al) don't handle errors softly, so coerce either by calling
+		 * json_populate_type() or the type's input function so that any
+		 * errors are handled appropriately. The latter only if OMIT QUOTES
+		 * is true.
+		 */
 		switch (returning->typid)
 		{
 			case BOOLOID:
@@ -4513,173 +4503,68 @@ coerceJsonFuncExprOutput(ParseState *pstate, JsonExpr *jsexpr)
 			case INT8OID:
 			case FLOAT4OID:
 			case FLOAT8OID:
-				return (Node *) makeJsonCoercion(returning, omit_quotes, NULL);
+				if (jsexpr->omit_quotes)
+					jsexpr->use_io_coercion = true;
+				else
+					jsexpr->use_json_coercion = true;
+				return;
 			default:
 				break;
 		}
 	}
 
-	default_typid = JsonFuncExprDefaultReturnType(jsexpr);
-	default_typmod = -1;
+	/* Look up a cast expression. */
+
+	/*
+	 * For JSON_VALUE() and for JSON_QUERY() when OMIT QUOTES is true,
+	 * ExecEvalJsonExprPath() will convert a quote-stripped source value to
+	 * its text representation, so use TEXTOID as the source type.
+	 */
+	if (omit_quotes || jsexpr->op == JSON_VALUE_OP)
+	{
+		default_typid = TEXTOID;
+		default_typmod = -1;
+	}
+	else
+	{
+		default_typid = exprType(context_item);
+		default_typmod = exprTypmod(context_item);
+	}
+
 	if (returning->typid != default_typid ||
-		returning->typmod != default_typmod ||
-		omit_quotes)
+		returning->typmod != default_typmod)
 	{
 		/*
 		 * We abuse CaseTestExpr here as placeholder to pass the result of
-		 * evaluating the JSON_VALUE/QUERY jsonpath expression as input to the
-		 * coercion expression.
+		 * jsonpath evaluation as input to the coercion expression.
 		 */
 		CaseTestExpr *placeholder = makeNode(CaseTestExpr);
 
-		/*
-		 * When OMIT QUOTES is true, ExecEvalJsonCoercion() will convert a
-		 * quote-stripped source value to its text representation, so use
-		 * TEXTOID as the source type.
-		 */
-		placeholder->typeId = omit_quotes ? TEXTOID : exprType(context_item);
-		placeholder->typeMod = omit_quotes ? -1 : exprTypmod(context_item);
-
-		Assert(placeholder->typeId == default_typid ||
-			   placeholder->typeId == TEXTOID);
-		Assert(placeholder->typeMod == default_typmod ||
-			   placeholder->typeMod == -1);
+		placeholder->typeId = default_typid;
+		placeholder->typeMod = default_typmod;
 
-		coercion_expr = coerceJsonExpr(pstate, (Node *) placeholder,
-									   returning, omit_quotes);
+		coercion_expr = coerceJsonFuncExpr(pstate, (Node *) placeholder,
+										   returning, false);
+		if (coercion_expr == (Node *) placeholder)
+			coercion_expr = NULL;
 	}
 
-	return coercion_expr;
-}
-
-/* Returns the default type for a given JsonExpr for a given JsonFormat. */
-static Oid
-JsonFuncExprDefaultReturnType(JsonExpr *jsexpr)
-{
-	JsonFormat *format = jsexpr->format;
-	Node	   *context_item = jsexpr->formatted_expr;
-
-	Assert(format);
-	if (format->format_type == JS_FORMAT_JSONB)
-		return JSONBOID;
-	else if (format->format_type == JS_FORMAT_DEFAULT &&
-			 exprType(context_item) == JSONBOID)
-		return JSONBOID;
-
-	return JSONOID;
-}
-
-/*
- * Returns a JsonCoercion node to coerce a jsonb-valued expression to the
- * target type given by 'returning' using either json_populate_type() or
- * by using the target type's input function.
- */
-static JsonCoercion *
-makeJsonCoercion(const JsonReturning *returning, bool omit_quotes,
-				 Node *cast_expr)
-{
-	JsonCoercion *coercion = makeNode(JsonCoercion);
+	jsexpr->coercion_expr = coercion_expr;
 
-	coercion->targettype = returning->typid;
-	coercion->targettypmod = returning->typmod;
-	coercion->omit_quotes = omit_quotes;
-	coercion->cast_expr = cast_expr;
-
-	return coercion;
-}
-
-/*
- * Coerce the result of JSON_VALUE / JSON_QUERY () (or a behavior expression)
- * to the output type
- *
- * Returns NULL if no coercion needed (the input expresssion is already of the
- * desired type) and OMIT QUOTES is false.
- *
- * Returns a JsonCoercion node if the cast was not found or if OMIT QUOTES is
- * true.
- */
-static Node *
-coerceJsonExpr(ParseState *pstate, Node *expr, const JsonReturning *returning,
-			   bool omit_quotes)
-{
-	Node	   *coerced_expr;
-
-	Assert(expr != NULL);
-	coerced_expr = coerceJsonFuncExpr(pstate, expr, returning, false);
-
-	if (coerced_expr == expr && !omit_quotes)
-		return NULL;
-
-	/* Use coerced_expr for JsonCoercion.cast_expr iff coercion is needed. */
-	if (coerced_expr == NULL || omit_quotes)
-		return (Node *) makeJsonCoercion(returning, omit_quotes,
-										 coerced_expr == expr ?
-										 NULL : coerced_expr);
-
-	return coerced_expr;
-}
-
-/*
- * Initialize JsonCoercion nodes for coercing a given JSON item value produced
- * by JSON_VALUE to the target "returning" type; also see
- * ExecPrepareJsonItemCoercion().
- */
-static List *
-InitJsonItemCoercions(ParseState *pstate, const JsonReturning *returning,
-					  Oid contextItemTypeId)
-{
-	List	   *item_coercions = NIL;
-	int			i;
-	Oid			typeoid;
-	struct
-	{
-		JsonItemType item_type;
-		Oid			typeoid;
-	}			item_types[] =
-	{
-		{JsonItemTypeNull, UNKNOWNOID},
-		{JsonItemTypeString, TEXTOID},
-		{JsonItemTypeNumeric, NUMERICOID},
-		{JsonItemTypeBoolean, BOOLOID},
-		{JsonItemTypeDate, DATEOID},
-		{JsonItemTypeTime, TIMEOID},
-		{JsonItemTypeTimetz, TIMETZOID},
-		{JsonItemTypeTimestamp, TIMESTAMPOID},
-		{JsonItemTypeTimestamptz, TIMESTAMPTZOID},
-		{JsonItemTypeComposite, contextItemTypeId},
-		{JsonItemTypeInvalid, InvalidOid}
-	};
-
-	for (i = 0; OidIsValid(typeoid = item_types[i].typeoid); i++)
+	if (coercion_expr == NULL)
 	{
-		Node	   *expr;
-		JsonItemCoercion *item_coercion = makeNode(JsonItemCoercion);
-
-		if (typeoid == UNKNOWNOID)
-		{
-			expr = (Node *) makeNullConst(UNKNOWNOID, -1, InvalidOid);
-		}
+		/*
+		 * Either no cast was found or coercion is unnecessary but still must
+		 * convert the string value to the output type.
+		 */
+		if (omit_quotes || jsexpr->op == JSON_VALUE_OP)
+			jsexpr->use_io_coercion = true;
 		else
-		{
-			CaseTestExpr *placeholder = makeNode(CaseTestExpr);
-
-			/*
-			 * We abuse CaseTestExpr here as placeholder to pass the result of
-			 * JSON_VALUE jsonpath expression to the coercion function.
-			 */
-			placeholder->typeId = item_types[i].typeoid;
-			placeholder->typeMod = -1;
-			placeholder->collation = InvalidOid;
-
-			expr = (Node *) placeholder;
-		}
-
-		item_coercion->item_type = item_types[i].item_type;
-		item_coercion->coercion = coerceJsonExpr(pstate, expr, returning, false);
-		item_coercions = lappend(item_coercions, item_coercion);
+			jsexpr->use_json_coercion = true;
 	}
 
-	return item_coercions;
+	Assert(jsexpr->coercion_expr != NULL ||
+		   (jsexpr->use_io_coercion != jsexpr->use_json_coercion));
 }
 
 /*
@@ -4758,16 +4643,16 @@ transformJsonBehavior(ParseState *pstate, JsonBehavior *behavior,
 					  JsonBehaviorType default_behavior,
 					  JsonReturning *returning)
 {
-	JsonBehaviorType behavior_type = default_behavior;
+	JsonBehaviorType btype = default_behavior;
 	Node	   *expr = NULL;
-	JsonCoercion *coercion = NULL;
+	bool		coerce = false;
 	int			location = -1;
 
 	if (behavior)
 	{
-		behavior_type = behavior->btype;
+		btype = behavior->btype;
 		location = behavior->location;
-		if (behavior_type == JSON_BEHAVIOR_DEFAULT)
+		if (btype == JSON_BEHAVIOR_DEFAULT)
 		{
 			expr = transformExprRecurse(pstate, behavior->expr);
 			if (!IsA(expr, Const) && !IsA(expr, FuncExpr) &&
@@ -4791,8 +4676,8 @@ transformJsonBehavior(ParseState *pstate, JsonBehavior *behavior,
 		}
 	}
 
-	if (expr == NULL && behavior_type != JSON_BEHAVIOR_ERROR)
-		expr = GetJsonBehaviorConstExpr(behavior_type, location);
+	if (expr == NULL && btype != JSON_BEHAVIOR_ERROR)
+		expr = GetJsonBehaviorConstExpr(btype, location);
 
 	if (expr)
 	{
@@ -4801,15 +4686,15 @@ transformJsonBehavior(ParseState *pstate, JsonBehavior *behavior,
 
 		/*
 		 * Coerce NULLs and "internal" (that is, not specified by the user)
-		 * jsonb-valued expressions with a JsonCoercion node.
+		 * jsonb-valued expressions at runtime using json_populate_type().
 		 *
 		 * For other (user-specified) non-NULL values, try to find a cast and
 		 * error out if one is not found.
 		 */
 		if (isnull ||
 			(exprType(expr) == JSONBOID &&
-			 behavior_type == default_behavior))
-			coercion = makeJsonCoercion(returning, false, NULL);
+			 btype == default_behavior))
+			coerce = true;
 		else
 			coerced_expr =
 				coerce_to_target_type(pstate, expr, exprType(expr),
@@ -4828,5 +4713,12 @@ transformJsonBehavior(ParseState *pstate, JsonBehavior *behavior,
 			expr = coerced_expr;
 	}
 
-	return makeJsonBehavior(behavior_type, expr, coercion, location);
+	if (behavior)
+		behavior->expr = expr;
+	else
+		behavior = makeJsonBehavior(btype, expr, location);
+
+	behavior->coerce = coerce;
+
+	return behavior;
 }
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 383efe5b6c..1276f33604 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -2019,6 +2019,9 @@ FigureColnameInternal(Node *node, char **name)
 				case JSON_VALUE_OP:
 					*name = "json_value";
 					return 2;
+				default:
+					elog(ERROR, "unrecognized JsonExpr op: %d",
+						 (int) ((JsonFuncExpr *) node)->op);
 			}
 			break;
 		default:
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 0539c2424f..155274d7e8 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -9886,6 +9886,9 @@ get_rule_expr(Node *node, deparse_context *context,
 					case JSON_VALUE_OP:
 						appendStringInfoString(buf, "JSON_VALUE(");
 						break;
+					default:
+						elog(ERROR, "unrecognized JsonExpr op: %d",
+							 (int) jexpr->op);
 				}
 
 				get_rule_expr(jexpr->formatted_expr, context, showimplicit);
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 4bbf4acdf6..64698202a5 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -705,9 +705,8 @@ typedef struct ExprEvalStep
 		/* for EEOP_JSONEXPR_COERCION */
 		struct
 		{
-			JsonCoercion *coercion;
-			FmgrInfo   *input_finfo;
-			Oid			typioparam;
+			Oid			targettype;
+			int32		targettypmod;
 			void	   *json_populate_type_cache;
 			ErrorSaveContext *escontext;
 		}			jsonexpr_coercion;
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index cd2ce63fa1..b17fa52d82 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1028,6 +1028,10 @@ typedef struct JsonExprState
 	/* JsonPathVariable entries for passing_values */
 	List	   *args;
 
+	/* Type input function info for JSON_VALUE() result coercion. */
+	FmgrInfo   *input_finfo;
+	FunctionCallInfo input_fcinfo;
+
 	/*
 	 * Addresses of steps that implement the non-ERROR variant of ON EMPTY
 	 * and ON ERROR behaviors, respectively.
@@ -1039,22 +1043,10 @@ typedef struct JsonExprState
 	 * Addresses of steps to coerce the result value of jsonpath evaluation to
 	 * the RETURNING type.
 	 *
-	 * jump_eval_result_coercion points to the step to evaluate the coercion
-	 * given in JsonExpr.result_coercion.  -1 if no coercion is necessary.
-	 *
-	 * Only valid for JSON_VALUE, eval_item_coercion_jumps is an array of
-	 * num_item_coercions elements each containing a step address to coerce
-	 * a value of given JsonItemType returned by JsonPathValue() to the
-	 * RETURNING type, or -1 if no coercion is necessary.
-	 * item_coercion_is_cast is an array of boolean flags of the same length
-	 * that indicates whether each valid step address in the
-	 * eval_item_coercion_jumps array corresponds to a cast expression or a
-	 * JsonCoercion node.
+	 * jump_eval_coercion_expr points to the step to evaluate the coercion
+	 * given in JsonExpr.coercion_expr.  -1 if no coercion is necessary.
 	 */
-	int			jump_eval_result_coercion;
-	int			num_item_coercions;
-	int		   *eval_item_coercion_jumps;
-	bool	   *item_coercion_is_cast;
+	int			jump_eval_coercion_expr;
 
 	/*
 	 * Address to jump to when skipping all the steps after performing
diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h
index 91d95fc52b..fdc78270e5 100644
--- a/src/include/nodes/makefuncs.h
+++ b/src/include/nodes/makefuncs.h
@@ -112,11 +112,11 @@ extern JsonFormat *makeJsonFormat(JsonFormatType type, JsonEncoding encoding,
 								  int location);
 extern JsonValueExpr *makeJsonValueExpr(Expr *raw_expr, Expr *formatted_expr,
 										JsonFormat *format);
-extern JsonBehavior *makeJsonBehavior(JsonBehaviorType type, Node *expr,
-									  JsonCoercion *coercion, int location);
 extern Node *makeJsonKeyValue(Node *key, Node *value);
 extern Node *makeJsonIsPredicate(Node *expr, JsonFormat *format,
 								 JsonValueType item_type, bool unique_keys,
 								 int location);
+extern JsonBehavior *makeJsonBehavior(JsonBehaviorType btype, Node *expr,
+									  int location);
 
 #endif							/* MAKEFUNC_H */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 377982f8fa..6da09bcb5f 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1725,77 +1725,13 @@ typedef enum JsonBehaviorType
 	JSON_BEHAVIOR_DEFAULT,
 } JsonBehaviorType;
 
-/*
- * JsonCoercion
- *		Information about coercing a SQL/JSON value to the specified
- *		type at runtime
- *
- * A node of this type is created if the parser cannot find a cast expression
- * using coerce_type() or if OMIT QUOTES is specified for JSON_QUERY; see
- * coerceJsonFuncExprOutput().
- *
- * If it's the latter, 'cast_expr' may contain the cast expression, evaluated
- * separately from this node, that will do the actual coercion of the
- * quote-stripped string.  If no cast expression is given, the string will be
- * coerced by calling the target type's input function.  See
- * ExecEvalJsonCoercion().
- */
-typedef struct JsonCoercion
-{
-	NodeTag		type;
-
-	Oid			targettype;
-	int32		targettypmod;
-	bool		omit_quotes;	/* OMIT QUOTES specified for JSON_QUERY? */
-	Node	   *cast_expr;		/* coercion cast expression or NULL */
-	Oid			collation;
-} JsonCoercion;
-
-/*
- * JsonItemType
- *		Possible types for scalar values returned by JSON_VALUE()
- *
- * The comment next to each item type mentions the corresponding
- * JsonbValue.jbvType.
- */
-typedef enum JsonItemType
-{
-	JsonItemTypeNull,			/* jbvNull */
-	JsonItemTypeString,			/* jbvString */
-	JsonItemTypeNumeric,		/* jbvNumeric */
-	JsonItemTypeBoolean,		/* jbvBool */
-	JsonItemTypeDate,			/* jbvDatetime: DATEOID */
-	JsonItemTypeTime,			/* jbvDatetime: TIMEOID */
-	JsonItemTypeTimetz,			/* jbvDatetime: TIMETZOID */
-	JsonItemTypeTimestamp,		/* jbvDatetime: TIMESTAMPOID */
-	JsonItemTypeTimestamptz,	/* jbvDatetime: TIMESTAMPTZOID */
-	JsonItemTypeComposite,		/* jbvArray, jbvObject, jbvBinary */
-	JsonItemTypeInvalid,
-} JsonItemType;
-
-/*
- * JsonItemCoercion
- *		Coercion expression for the given JsonItemType
- *
- * If not NULL, 'coercion' given the expression node to convert a scalar value
- * extracted from a JsonbValue of the given type to the target type given by
- * JsonExpr.returning.  NULL means the coercion is unnecessary.
- */
-typedef struct JsonItemCoercion
-{
-	NodeTag		type;
-
-	JsonItemType item_type;
-	Node	   *coercion;
-} JsonItemCoercion;
-
 /*
  * JsonBehavior
  *		Information about ON ERROR / ON EMPTY behaviors of JSON_VALUE(),
  *		JSON_QUERY(), and JSON_EXISTS()
  *
  * 'expr' is the expression to emit when a given behavior (EMPTY or ERROR)
- * occurs on evaluating the SQL/JSON query function.  'coercion' is set
+ * occurs on evaluating the SQL/JSON query function.  'coerce' is set to true
  * if 'expr' isn't already of the expected target type given by
  * JsonExpr.returning.
  */
@@ -1805,8 +1741,7 @@ typedef struct JsonBehavior
 
 	JsonBehaviorType btype;
 	Node	   *expr;
-	JsonCoercion *coercion;		/* to coerce behavior expression when there is
-								 * no cast to the target type */
+	bool		coerce;
 	int			location;		/* token location, or -1 if unknown */
 } JsonBehavior;
 
@@ -1853,16 +1788,17 @@ typedef struct JsonExpr
 	JsonBehavior *on_error;
 
 	/*
-	 * Expression to convert the result of jsonpath functions to the RETURNING
-	 * type
-	 */
-	Node	   *result_coercion;
-
-	/*
-	 * List of expressions for coercing JSON_VALUE() result values, containing
-	 * one element for every JsonItemType.
+	 * Information about converting the result of jsonpath functions
+	 * JsonPathQuery() and JsonPathValue() to the RETURNING type.
+	 *
+	 * coercion_expr is a cast expression if the parser can find it for the
+	 * source and the target type.  If not, either use_io_coercion or
+	 * use_json_coercion is set to determine the coercion method to use at
+	 * runtime; see coerceJsonExprOutput() and ExecInitJsonExpr().
 	 */
-	List	   *item_coercions;
+	Node	   *coercion_expr;
+	bool		use_io_coercion;
+	bool		use_json_coercion;
 
 	/* WRAPPER specification for JSON_QUERY */
 	JsonWrapper wrapper;
@@ -1870,6 +1806,9 @@ typedef struct JsonExpr
 	/* KEEP or OMIT QUOTES for singleton scalars returned by JSON_QUERY() */
 	bool		omit_quotes;
 
+	/* JsonExpr's collation, if coercion_expr is NULL. */
+	Oid			collation;
+
 	/* Original JsonFuncExpr's location */
 	int			location;
 } JsonExpr;
diff --git a/src/test/regress/expected/sqljson_queryfuncs.out b/src/test/regress/expected/sqljson_queryfuncs.out
index f5b57465d6..5a537d0655 100644
--- a/src/test/regress/expected/sqljson_queryfuncs.out
+++ b/src/test/regress/expected/sqljson_queryfuncs.out
@@ -1,10 +1,4 @@
 -- JSON_EXISTS
--- json arguments currently not supported
-SELECT JSON_EXISTS(NULL FORMAT JSON, '$');
-ERROR:  JSON_EXISTS() is not yet implemented for the json type
-LINE 1: SELECT JSON_EXISTS(NULL FORMAT JSON, '$');
-                           ^
-HINT:  Try casting the argument to jsonb
 SELECT JSON_EXISTS(NULL::jsonb, '$');
  json_exists 
 -------------
@@ -147,12 +141,6 @@ SELECT JSON_EXISTS(jsonb '1', '$.a > 2' ERROR ON ERROR);
 (1 row)
 
 -- JSON_VALUE
--- json arguments currently not supported
-SELECT JSON_VALUE(NULL FORMAT JSON, '$');
-ERROR:  JSON_VALUE() is not yet implemented for the json type
-LINE 1: SELECT JSON_VALUE(NULL FORMAT JSON, '$');
-                          ^
-HINT:  Try casting the argument to jsonb
 SELECT JSON_VALUE(NULL::jsonb, '$');
  json_value 
 ------------
@@ -174,7 +162,7 @@ SELECT JSON_VALUE(jsonb 'null', '$' RETURNING int);
 SELECT JSON_VALUE(jsonb 'true', '$');
  json_value 
 ------------
- true
+ t
 (1 row)
 
 SELECT JSON_VALUE(jsonb 'true', '$' RETURNING bool);
@@ -203,7 +191,11 @@ SELECT JSON_VALUE(jsonb '123', '$' RETURNING text);
 
 /* jsonb bytea ??? */
 SELECT JSON_VALUE(jsonb '123', '$' RETURNING bytea ERROR ON ERROR);
-ERROR:  SQL/JSON item cannot be cast to target type
+ json_value 
+------------
+ \x313233
+(1 row)
+
 SELECT JSON_VALUE(jsonb '1.23', '$');
  json_value 
 ------------
@@ -213,7 +205,7 @@ SELECT JSON_VALUE(jsonb '1.23', '$');
 SELECT JSON_VALUE(jsonb '1.23', '$' RETURNING int);
  json_value 
 ------------
-          1
+           
 (1 row)
 
 SELECT JSON_VALUE(jsonb '"1.23"', '$' RETURNING numeric);
@@ -329,11 +321,7 @@ SELECT JSON_VALUE(jsonb '1',  '$.a' RETURNING sqljsonb_int_not_null DEFAULT 2 ON
 (1 row)
 
 SELECT JSON_VALUE(jsonb '1',  '$.a' RETURNING sqljsonb_int_not_null DEFAULT NULL ON EMPTY ERROR ON ERROR);
- json_value 
-------------
-           
-(1 row)
-
+ERROR:  domain sqljsonb_int_not_null does not allow null values
 CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple');
 CREATE DOMAIN rgb AS rainbow CHECK (VALUE IN ('red', 'green', 'blue'));
 SELECT JSON_VALUE('"purple"'::jsonb, 'lax $[*]' RETURNING rgb);
@@ -433,7 +421,7 @@ SELECT JSON_VALUE(jsonb '["1"]', '$[*]' RETURNING int DEFAULT 2 + 3 ON ERROR);
 (1 row)
 
 SELECT JSON_VALUE(jsonb '["1"]', '$[*]' RETURNING int FORMAT JSON); -- RETURNING FORMAT not allowed
-ERROR:  cannot specify FORMAT in RETURNING clause of JSON_VALUE()
+ERROR:  cannot specify FORMAT JSON in RETURNING clause of JSON_VALUE()
 LINE 1: ...CT JSON_VALUE(jsonb '["1"]', '$[*]' RETURNING int FORMAT JSO...
                                                              ^
 -- RETUGNING pseudo-types not allowed
@@ -531,13 +519,12 @@ SELECT JSON_VALUE(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +
  "2018-02-21T02:34:56+00:00"
 (1 row)
 
+-- Test that numeric JSON values are coerced uniformly
+select json_value('{"a": 1.234}', '$.a' returning int error on error);
+ERROR:  invalid input syntax for type integer: "1.234"
+select json_value('{"a": "1.234"}', '$.a' returning int error on error);
+ERROR:  invalid input syntax for type integer: "1.234"
 -- JSON_QUERY
--- json arguments currently not supported
-SELECT JSON_QUERY(NULL FORMAT JSON, '$');
-ERROR:  JSON_QUERY() is not yet implemented for the json type
-LINE 1: SELECT JSON_QUERY(NULL FORMAT JSON, '$');
-                          ^
-HINT:  Try casting the argument to jsonb
 SELECT JSON_VALUE(NULL::jsonb, '$');
  json_value 
 ------------
@@ -1259,3 +1246,24 @@ SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a' WITH WRAPPER);
 -- Should fail (invalid path)
 SELECT JSON_QUERY(jsonb '{"a": 123}', 'error' || ' ' || 'error');
 ERROR:  syntax error at or near " " of jsonpath input
+-- Non-jsonb inputs automatically coerced to jsonb
+SELECT JSON_EXISTS(json '{"a": 123}', '$' || '.' || 'a');
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_QUERY(NULL FORMAT JSON, '$');
+ json_query 
+------------
+ 
+(1 row)
+
+-- Test non-const jsonpath
+CREATE TEMP TABLE jsonpaths (path) AS SELECT '$';
+SELECT json_value('"aaa"', path RETURNING json) FROM jsonpaths;
+ json_value 
+------------
+ "aaa"
+(1 row)
+
diff --git a/src/test/regress/sql/sqljson_queryfuncs.sql b/src/test/regress/sql/sqljson_queryfuncs.sql
index 5be2d8e3f8..d01b172376 100644
--- a/src/test/regress/sql/sqljson_queryfuncs.sql
+++ b/src/test/regress/sql/sqljson_queryfuncs.sql
@@ -1,8 +1,4 @@
 -- JSON_EXISTS
-
--- json arguments currently not supported
-SELECT JSON_EXISTS(NULL FORMAT JSON, '$');
-
 SELECT JSON_EXISTS(NULL::jsonb, '$');
 SELECT JSON_EXISTS(jsonb '[]', '$');
 SELECT JSON_EXISTS(JSON_OBJECT(RETURNING jsonb), '$');
@@ -35,10 +31,6 @@ SELECT JSON_EXISTS(jsonb '1', '$ > 2');
 SELECT JSON_EXISTS(jsonb '1', '$.a > 2' ERROR ON ERROR);
 
 -- JSON_VALUE
-
--- json arguments currently not supported
-SELECT JSON_VALUE(NULL FORMAT JSON, '$');
-
 SELECT JSON_VALUE(NULL::jsonb, '$');
 
 SELECT JSON_VALUE(jsonb 'null', '$');
@@ -143,10 +135,11 @@ SELECT JSON_VALUE(jsonb 'null', '$ts' PASSING timestamp '2018-02-21 12:34:56 +10
 SELECT JSON_VALUE(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING json);
 SELECT JSON_VALUE(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING jsonb);
 
--- JSON_QUERY
+-- Test that numeric JSON values are coerced uniformly
+select json_value('{"a": 1.234}', '$.a' returning int error on error);
+select json_value('{"a": "1.234"}', '$.a' returning int error on error);
 
--- json arguments currently not supported
-SELECT JSON_QUERY(NULL FORMAT JSON, '$');
+-- JSON_QUERY
 
 SELECT JSON_VALUE(NULL::jsonb, '$');
 
@@ -425,3 +418,11 @@ SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a');
 SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a' WITH WRAPPER);
 -- Should fail (invalid path)
 SELECT JSON_QUERY(jsonb '{"a": 123}', 'error' || ' ' || 'error');
+
+-- Non-jsonb inputs automatically coerced to jsonb
+SELECT JSON_EXISTS(json '{"a": 123}', '$' || '.' || 'a');
+SELECT JSON_QUERY(NULL FORMAT JSON, '$');
+
+-- Test non-const jsonpath
+CREATE TEMP TABLE jsonpaths (path) AS SELECT '$';
+SELECT json_value('"aaa"', path RETURNING json) FROM jsonpaths;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 616e315ec4..7b0395510d 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1280,7 +1280,6 @@ JsonArrayQueryConstructor
 JsonBaseObjectInfo
 JsonBehavior
 JsonBehaviorType
-JsonCoercion
 JsonConstructorExpr
 JsonConstructorExprState
 JsonConstructorType
@@ -1293,7 +1292,6 @@ JsonFormat
 JsonFormatType
 JsonHashEntry
 JsonIsPredicate
-JsonItemCoercion
 JsonItemType
 JsonIterateStringValuesAction
 JsonKeyValue
-- 
2.43.0

