v9-0008-Remove-subsidary-ExprStates-in-SQL-JSON-execution.patch

text/x-patch

Filename: v9-0008-Remove-subsidary-ExprStates-in-SQL-JSON-execution.patch
Type: text/x-patch
Part: 7
Message: Re: SQL/JSON features for v15

Patch

Format: format-patch
Series: patch v9-0008
Subject: Remove subsidary ExprStates in SQL/JSON execution
File+
src/backend/executor/execExpr.c 71 17
src/backend/executor/execExprInterp.c 93 113
src/backend/jit/llvm/llvmjit_expr.c 65 5
src/backend/jit/llvm/llvmjit_types.c 1 0
src/backend/utils/adt/jsonpath_exec.c 73 6
src/include/executor/execExpr.h 14 6
src/include/utils/jsonpath.h 0 4
From 003bfccffc69824e0fb947d11f3f75a7b70fefb4 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Mon, 29 Aug 2022 22:41:08 +0300
Subject: [PATCH v9 8/9] Remove subsidary ExprStates in SQL/JSON execution

---
 src/backend/executor/execExpr.c       |  88 ++++++++---
 src/backend/executor/execExprInterp.c | 206 ++++++++++++--------------
 src/backend/jit/llvm/llvmjit_expr.c   |  70 ++++++++-
 src/backend/jit/llvm/llvmjit_types.c  |   1 +
 src/backend/utils/adt/jsonpath_exec.c |  79 +++++++++-
 src/include/executor/execExpr.h       |  20 ++-
 src/include/utils/jsonpath.h          |   4 -
 7 files changed, 317 insertions(+), 151 deletions(-)

diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index f50e0d703f8..68227ebee21 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -4159,9 +4159,16 @@ static void
 ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state, ExprEvalStep *scratch,
 				 Datum *resv, bool *resnull)
 {
+	ExprEvalStep *step;
 	JsonExprState *jsestate;
 	ListCell   *argexprlc;
 	ListCell   *argnamelc;
+	int			skip_step_off;
+	int			execpath_step_off;
+	int			done_step_off;
+	int			default_on_empty_step_off = -1;
+	int			default_on_error_step_off = -1;
+	int			default_on_empty_jump_off = -1;
 
 	/* JSON_TABLE preudo-function returns context item as a result */
 	if (jexpr->op == JSON_TABLE_OP)
@@ -4174,9 +4181,6 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state, ExprEvalStep *scratch,
 	jsestate = palloc0(sizeof(JsonExprState));
 	jsestate->jsexpr = jexpr;
 
-	scratch->opcode = EEOP_JSONEXPR;
-	scratch->d.jsonexpr.jsestate = jsestate;
-
 	ExecInitExprRec((Expr *) jexpr->formatted_expr, state,
 					&jsestate->formatted_expr.value,
 					&jsestate->formatted_expr.isnull);
@@ -4185,15 +4189,12 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state, ExprEvalStep *scratch,
 					&jsestate->pathspec.value,
 					&jsestate->pathspec.isnull);
 
-	if (jexpr->on_empty)
-		jsestate->default_on_empty =
-			ExecInitExpr((Expr *) jexpr->on_empty->default_expr,
-						 state->parent);
-
-	jsestate->default_on_error =
-		ExecInitExpr((Expr *) jexpr->on_error->default_expr,
-					 state->parent);
+	scratch->opcode = EEOP_JSONEXPR_SKIP;
+	scratch->d.jsonexpr_skip.jsestate = jsestate;
+	skip_step_off = state->steps_len;
+	ExprEvalPushStep(state, scratch);
 
+	/* PASSING args. */
 	jsestate->args = NIL;
 
 	forboth(argexprlc, jexpr->passing_values,
@@ -4206,16 +4207,69 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state, ExprEvalStep *scratch,
 		var->name = pstrdup(argname->sval);
 		var->typid = exprType((Node *) argexpr);
 		var->typmod = exprTypmod((Node *) argexpr);
-		var->estate = ExecInitExpr(argexpr, state->parent);
+
+		/*
+		 * A separate ExprState is not necessary for these expressions
+		 * when being evaluated for a JsonExpr, like in this case,
+		 * because they will evaluated as the steps of the JsonExpr.
+		 */
+		var->estate = NULL;
 		var->econtext = NULL;
 		var->mcxt = NULL;
-		var->evaluated = false;
-		var->value = (Datum) 0;
-		var->isnull = true;
 
-		jsestate->args =
-			lappend(jsestate->args, var);
+		/*
+		 * Mark these as always evaluated because they must have been
+		 * evaluated before JSON path evaluation begins, because we
+		 * haven't pushed the step for the latter yet.
+		 */
+		var->evaluated = true;
+
+		ExecInitExprRec((Expr *) argexpr, state, &var->value, &var->isnull);
+
+		jsestate->args = lappend(jsestate->args, var);
 	}
 
+	/* Step for the actual JSON path evaluation. */
+	scratch->opcode = EEOP_JSONEXPR_PATH;
+	scratch->d.jsonexpr.jsestate = jsestate;
+	execpath_step_off = state->steps_len;
 	ExprEvalPushStep(state, scratch);
+
+	if (jexpr->on_empty && jexpr->on_empty->default_expr)
+	{
+		default_on_empty_step_off = state->steps_len;
+		ExecInitExprRec((Expr *) jexpr->on_empty->default_expr, state, resv, resnull);
+
+		scratch->opcode = EEOP_JUMP;
+		default_on_empty_jump_off = state->steps_len;
+		ExprEvalPushStep(state, scratch);
+	}
+
+	if (jexpr->on_error->default_expr)
+	{
+		default_on_error_step_off = state->steps_len;
+		ExecInitExprRec((Expr *) jexpr->on_error->default_expr, state, resv, resnull);
+	}
+
+	/* Adjust jump locations */
+	done_step_off = state->steps_len;
+
+	/* EEOP_JSONEXPR_SKIP */
+	step = &state->steps[skip_step_off];
+	step->d.jsonexpr_skip.jump_done = done_step_off;
+
+	/* EEOP_JSONEXPR_PATH */
+	step = &state->steps[execpath_step_off];
+	step->d.jsonexpr.jump_done = done_step_off;
+	step->d.jsonexpr.jump_default_on_empty =
+		default_on_empty_step_off > 0 ? default_on_empty_step_off : done_step_off;
+	step->d.jsonexpr.jump_default_on_error =
+		default_on_error_step_off > 0 ? default_on_error_step_off : done_step_off;
+
+	/* EEOP_JUMP */
+	if (default_on_empty_jump_off > 0)
+	{
+		step = &state->steps[default_on_empty_jump_off];
+		step->d.jump.jumpdone = done_step_off;
+	}
 }
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 45c648817ba..d39223e0d15 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -491,7 +491,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		&&CASE_EEOP_SUBPLAN,
 		&&CASE_EEOP_JSON_CONSTRUCTOR,
 		&&CASE_EEOP_IS_JSON,
-		&&CASE_EEOP_JSONEXPR,
+		&&CASE_EEOP_JSONEXPR_SKIP,
+		&&CASE_EEOP_JSONEXPR_PATH,
 		&&CASE_EEOP_AGG_STRICT_DESERIALIZE,
 		&&CASE_EEOP_AGG_DESERIALIZE,
 		&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
@@ -1842,11 +1843,26 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			EEO_NEXT();
 		}
 
-		EEO_CASE(EEOP_JSONEXPR)
+		EEO_CASE(EEOP_JSONEXPR_SKIP)
 		{
 			/* too complex for an inline implementation */
-			ExecEvalJsonExpr(state, op, econtext);
-			EEO_NEXT();
+			if (ExecEvalJsonExprSkip(state, op))
+				EEO_JUMP(op->d.jsonexpr_skip.jump_done);
+			else
+				EEO_NEXT();
+		}
+
+		EEO_CASE(EEOP_JSONEXPR_PATH)
+		{
+			/* too complex for an inline implementation */
+			int			res = ExecEvalJsonExpr(state, op);
+
+			if (res == 0)
+				EEO_JUMP(op->d.jsonexpr.jump_default_on_empty);
+			else if (res == -1)
+				EEO_JUMP(op->d.jsonexpr.jump_default_on_error);
+			else
+				EEO_JUMP(op->d.jsonexpr.jump_done);
 		}
 
 		EEO_CASE(EEOP_LAST)
@@ -4701,59 +4717,6 @@ ExecEvalJsonConstructor(ExprState *state, ExprEvalStep *op,
 	*op->resnull = isnull;
 }
 
-/*
- * Evaluate a JSON path variable caching computed value.
- */
-int
-EvalJsonPathVar(void *cxt, char *varName, int varNameLen,
-				JsonbValue *val, JsonbValue *baseObject)
-{
-	JsonPathVariableEvalContext *var = NULL;
-	List	   *vars = cxt;
-	ListCell   *lc;
-	int			id = 1;
-
-	if (!varName)
-		return list_length(vars);
-
-	foreach(lc, vars)
-	{
-		var = lfirst(lc);
-
-		if (!strncmp(var->name, varName, varNameLen))
-			break;
-
-		var = NULL;
-		id++;
-	}
-
-	if (!var)
-		return -1;
-
-	if (!var->evaluated)
-	{
-		MemoryContext oldcxt = var->mcxt ?
-		MemoryContextSwitchTo(var->mcxt) : NULL;
-
-		var->value = ExecEvalExpr(var->estate, var->econtext, &var->isnull);
-		var->evaluated = true;
-
-		if (oldcxt)
-			MemoryContextSwitchTo(oldcxt);
-	}
-
-	if (var->isnull)
-	{
-		val->type = jbvNull;
-		return 0;
-	}
-
-	JsonItemFromDatum(var->value, var->typid, var->typmod, val);
-
-	*baseObject = *val;
-	return id;
-}
-
 /*
  * Check whether we need to override default coercion in
  * JSON_QUERY(OMIT QUOTES) case.
@@ -5210,9 +5173,8 @@ ExecJsonValueCoercion(JsonbValue *item, Oid typid, int32 typmod,
  * type.
  */
 static Datum
-ExecEvalJsonBehavior(ExprContext *econtext, JsonBehavior *behavior,
-					 ExprState *default_estate,
-					 Oid ret_typid, int32 ret_typmod, bool *is_null)
+ExecEvalJsonBehavior(JsonBehavior *behavior, Oid ret_typid,
+					 int32 ret_typmod, bool *is_null)
 {
 	*is_null = false;
 
@@ -5262,12 +5224,8 @@ ExecEvalJsonBehavior(ExprContext *econtext, JsonBehavior *behavior,
 			return (Datum) 0;
 
 		case JSON_BEHAVIOR_DEFAULT:
-			/*
-			 * Execute DEFAULT expression.
-			 * Coercion is not needed here, because expression is
-			 * already coerced to the target type by the parser.
-			 */
-			return ExecEvalExpr(default_estate, econtext, is_null);
+			Assert(0);
+			return (Datum) 0;	/* must be handled by caller */
 
 		default:
 			elog(ERROR, "unrecognized SQL/JSON behavior %d", behavior->btype);
@@ -5276,28 +5234,27 @@ ExecEvalJsonBehavior(ExprContext *econtext, JsonBehavior *behavior,
 }
 
 static Datum
-ExecEvalJsonExprInternal(ExprState *state,
-						 JsonExprState *jsestate, ExprContext *econtext,
+ExecEvalJsonExprInternal(ExprState *state, JsonExprState *jsestate,
 						 JsonPath *path, Datum item, bool *resnull,
-						 bool *error)
+						 bool *empty, bool *error)
 {
 	JsonExpr   *jexpr = jsestate->jsexpr;
-	bool		empty = false;
 	Datum		res = (Datum) 0;
 	Oid			ret_typid = jexpr->returning->typid;
 	int32		ret_typmod = jexpr->returning->typmod;
 
 	*resnull = true;
+	*empty = false;
 
 	switch (jexpr->op)
 	{
 		case JSON_QUERY_OP:
-			res = JsonPathQuery(item, path, jexpr->wrapper, &empty, error,
+			res = JsonPathQuery(item, path, jexpr->wrapper, empty, error,
 								jsestate->args);
 			if (error && *error)
 				return (Datum) 0;
 
-			if (empty)
+			if (*empty)
 				break;
 
 			*resnull = !DatumGetPointer(res);
@@ -5364,13 +5321,13 @@ ExecEvalJsonExprInternal(ExprState *state,
 
 		case JSON_VALUE_OP:
 			{
-				JsonbValue *jbv = JsonPathValue(item, path, &empty, error,
+				JsonbValue *jbv = JsonPathValue(item, path, empty, error,
 												jsestate->args);
 
 				if (error && *error)
 					return (Datum) 0;
 
-				if (empty)
+				if (*empty)
 					break;
 
 				if (!jbv)
@@ -5408,78 +5365,101 @@ ExecEvalJsonExprInternal(ExprState *state,
 			return (Datum) 0;
 	}
 
-	Assert(empty);
-	Assert(jexpr->on_empty);	/* it is not JSON_EXISTS */
+	Assert(*empty);
 
-	if (jexpr->on_empty->btype == JSON_BEHAVIOR_ERROR)
-	{
-		if (error)
-		{
-			*error = true;
-			return (Datum) 0;
-		}
+	return (Datum) 0;
+}
 
-		ereport(ERROR,
-				(errcode(ERRCODE_NO_SQL_JSON_ITEM),
-				 errmsg("no SQL/JSON item")));
+bool
+ExecEvalJsonExprSkip(ExprState *state, ExprEvalStep *op)
+{
+	JsonExprState *jsestate = op->d.jsonexpr_skip.jsestate;
+
+	/*
+	 * Skip if either of the input expressions has turned out to be
+	 * NULL, though do execute domain checks for NULLs, which are
+	 * handled by the coercion step.
+	 */
+	if (jsestate->formatted_expr.isnull ||
+		jsestate->pathspec.isnull)
+	{
+		*op->resvalue = (Datum) 0;
+		*op->resnull = true;
+		return true;
 	}
 
-	/* Execute ON EMPTY behavior */
-	return ExecEvalJsonBehavior(econtext, jexpr->on_empty,
-								jsestate->default_on_empty,
-								ret_typid, ret_typmod, resnull);
+	/*
+	 * Go evaluate the PASSING args if any and subsequently JSON path
+	 * itself.
+	 */
+	return false;
 }
 
 /* ----------------------------------------------------------------
  *		ExecEvalJsonExpr
  * ----------------------------------------------------------------
+ *
+ * Return value:
+ *   1 - Ok, jump to the end of JsonExpr
+ *   0 - empty result, need to execute DEFAULT ON EMPTY expression
+ *  -1 - error occured, need to execute DEFAULT ON ERROR expression
  */
-void
-ExecEvalJsonExpr(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+int
+ExecEvalJsonExpr(ExprState *state, ExprEvalStep *op)
 {
 	JsonExprState *jsestate = op->d.jsonexpr.jsestate;
 	JsonExpr   *jexpr = jsestate->jsexpr;
 	Datum		item;
 	Datum		res;
 	JsonPath   *path;
-	ListCell   *lc;
+	bool		empty = false;
 	bool		error = false;
-	bool		throwErrors = jexpr->on_error->btype == JSON_BEHAVIOR_ERROR;
-
-	if (jsestate->formatted_expr.isnull || jsestate->pathspec.isnull)
-	{
-		*op->resnull = true;
-		*op->resvalue = (Datum) 0;
-		return;
-	}
+	bool		throw_errors = jexpr->on_error->btype == JSON_BEHAVIOR_ERROR;
 
 	item = jsestate->formatted_expr.value;
 	path = DatumGetJsonPathP(jsestate->pathspec.value);
 
-	/* reset JSON path variable contexts */
-	foreach(lc, jsestate->args)
+	res = ExecEvalJsonExprInternal(state, jsestate, path, item,
+								   op->resnull, &empty,
+								   throw_errors ? NULL : &error);
+
+	if (empty && !error)
 	{
-		JsonPathVariableEvalContext *var = lfirst(lc);
+		Assert(jexpr->on_empty);	/* it is not JSON_EXISTS */
 
-		var->econtext = econtext;
-		var->evaluated = false;
-	}
+		if (jexpr->on_empty->btype == JSON_BEHAVIOR_ERROR)
+		{
+			if (throw_errors)
+				ereport(ERROR,
+						(errcode(ERRCODE_NO_SQL_JSON_ITEM),
+						 errmsg("no SQL/JSON item")));
 
-	res = ExecEvalJsonExprInternal(state, jsestate, econtext,
-								   path, item, op->resnull,
-								   throwErrors ? NULL : &error);
+			error = true; /* fallback to error behavior */
+		}
+		else if (jexpr->on_empty->btype == JSON_BEHAVIOR_DEFAULT)
+			return 0;	/* jump to ON EMPTY expression */
+		else
+			/* Execute ON EMPTY behavior */
+			res = ExecEvalJsonBehavior(jexpr->on_empty,
+									   jexpr->returning->typid,
+									   jexpr->returning->typmod,
+									   op->resnull);
+	}
 
 	if (error)
 	{
-		Assert(!throwErrors);
+		Assert(!throw_errors);
+
+		if (jexpr->on_error->btype == JSON_BEHAVIOR_DEFAULT)
+			return -1;	/* jump to ON ERROR expression */
 
 		/* Execute ON ERROR behavior */
-		res = ExecEvalJsonBehavior(econtext, jexpr->on_error,
-								   jsestate->default_on_error,
+		res = ExecEvalJsonBehavior(jexpr->on_error,
 								   jexpr->returning->typid,
 								   jexpr->returning->typmod,
 								   op->resnull);
 	}
 
 	*op->resvalue = res;
+	return 1;	/* jump to the end of expression */
 }
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index fd72630f5e6..7681a77cd63 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -2407,11 +2407,71 @@ llvm_compile_expr(ExprState *state)
 				LLVMBuildBr(b, opblocks[opno + 1]);
 				break;
 
-			case EEOP_JSONEXPR:
-				build_EvalXFunc(b, mod, "ExecEvalJsonExpr",
-								v_state, op, v_econtext);
-				LLVMBuildBr(b, opblocks[opno + 1]);
-				break;
+			case EEOP_JSONEXPR_SKIP:
+				{
+					LLVMValueRef v_ret;
+
+					/*
+					 * Call ExecEvalJsonExprSkip() to decide if JSON
+					 * path evaluation can be skipped.  This returns
+					 * boolean "skip" flag.
+					 */
+					v_ret = build_EvalXFunc(b, mod, "ExecEvalJsonExprSkip",
+											v_state, op);
+					v_ret = LLVMBuildZExt(b, v_ret, TypeStorageBool, "");
+
+					/*
+					 * Jump to coercion step if true was returned,
+					 * which signifies skipping of JSON path evaluation,
+					 * else to the next step which must point to the
+					 * steps to evaluate PASSING args, if any, or to
+					 * the JSON path evaluation.
+					 */
+					LLVMBuildCondBr(b,
+									LLVMBuildICmp(b,
+												  LLVMIntEQ,
+												  v_ret,
+												  l_sbool_const(0),
+												  ""),
+									opblocks[opno + 1],
+									opblocks[op->d.jsonexpr_skip.jump_done]);
+					break;
+				}
+
+			case EEOP_JSONEXPR_PATH:
+				{
+					LLVMValueRef v_ret;
+					LLVMValueRef v_switch;
+					int			n_jumps = 0;
+
+					v_ret = build_EvalXFunc(b, mod, "ExecEvalJsonExpr",
+											v_state, op);
+
+					if (op->d.jsonexpr.jump_default_on_empty != op->d.jsonexpr.jump_done)
+						n_jumps++;
+
+					if (op->d.jsonexpr.jump_default_on_error != op->d.jsonexpr.jump_done)
+						n_jumps++;
+
+					if (!n_jumps)
+						LLVMBuildBr(b, opblocks[op->d.jsonexpr.jump_done]);
+					else
+					{
+						v_switch = LLVMBuildSwitch(b, v_ret,
+												   opblocks[op->d.jsonexpr.jump_done],
+												   n_jumps);
+
+						if (op->d.jsonexpr.jump_default_on_empty != op->d.jsonexpr.jump_done)
+							LLVMAddCase(v_switch, l_int32_const(0),
+										opblocks[op->d.jsonexpr.jump_default_on_empty]);
+
+						if (op->d.jsonexpr.jump_default_on_error != op->d.jsonexpr.jump_done)
+							LLVMAddCase(v_switch, l_int32_const(-1),
+										opblocks[op->d.jsonexpr.jump_default_on_error]);
+					}
+
+					break;
+				}
 
 			case EEOP_LAST:
 				Assert(false);
diff --git a/src/backend/jit/llvm/llvmjit_types.c b/src/backend/jit/llvm/llvmjit_types.c
index 37fe64654b6..b9148ce515c 100644
--- a/src/backend/jit/llvm/llvmjit_types.c
+++ b/src/backend/jit/llvm/llvmjit_types.c
@@ -136,6 +136,7 @@ void	   *referenced_functions[] =
 	ExecEvalJsonConstructor,
 	ExecEvalJsonIsPredicate,
 	ExecEvalJsonExpr,
+	ExecEvalJsonExprSkip,
 	MakeExpandedObjectReadOnlyInternal,
 	slot_getmissingattrs,
 	slot_getsomeattrs_int,
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 5b6a4805721..b8949789710 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -286,6 +286,8 @@ static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt,
 										   JsonPathItem *jsp, JsonValueList *found, JsonPathBool res);
 static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
 							JsonbValue *value);
+static int evalJsonPathVariable(void *cxt, char *varName, int varNameLen,
+								JsonbValue *val, JsonbValue *baseObject);
 static void getJsonPathVariable(JsonPathExecContext *cxt,
 								JsonPathItem *variable, JsonbValue *value);
 static int	getJsonPathVariableFromJsonb(void *varsJsonb, char *varName,
@@ -2172,6 +2174,71 @@ getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
 	}
 }
 
+/*
+ * Evaluate a JSON path variable caching computed value.
+ */
+static int
+evalJsonPathVariable(void *cxt, char *varName, int varNameLen,
+					 JsonbValue *val, JsonbValue *baseObject)
+{
+	JsonPathVariableEvalContext *var = NULL;
+	List	   *vars = cxt;
+	ListCell   *lc;
+	int			id = 1;
+
+	if (!varName)
+		return list_length(vars);
+
+	foreach(lc, vars)
+	{
+		var = lfirst(lc);
+
+		if (!strncmp(var->name, varName, varNameLen))
+			break;
+
+		var = NULL;
+		id++;
+	}
+
+	if (!var)
+		return -1;
+
+	/*
+	 * When belonging to a JsonExpr, path variables are computed with the
+	 * JsonExpr's ExprState (var->estate is NULL), so don't need to be computed
+	 * here.  In some other cases, such as when the path variables belonging
+	 * to a JsonTable instead, those variables must be evaluated on their own,
+	 * without the enclosing JsonExpr itself needing to be evaluated, so must
+	 * be handled here.
+	 */
+	if (!var->estate)
+	{
+		Assert(var->evaluated);
+	}
+	else if (!var->evaluated)
+	{
+		MemoryContext oldcxt = var->mcxt ? MemoryContextSwitchTo(var->mcxt) : NULL;
+
+		Assert(var->econtext);
+		var->value = ExecEvalExpr(var->estate, var->econtext, &var->isnull);
+		var->evaluated = true;
+
+		if (oldcxt)
+			MemoryContextSwitchTo(oldcxt);
+	}
+
+	if (var->isnull)
+	{
+		val->type = jbvNull;
+		return 0;
+	}
+
+	JsonItemFromDatum(var->value, var->typid, var->typmod, val);
+
+	*baseObject = *val;
+	return id;
+}
+
 /*
  * Get the value of variable passed to jsonpath executor
  */
@@ -2903,7 +2970,7 @@ compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
 bool
 JsonPathExists(Datum jb, JsonPath *jp, List *vars, bool *error)
 {
-	JsonPathExecResult res = executeJsonPath(jp, vars, EvalJsonPathVar,
+	JsonPathExecResult res = executeJsonPath(jp, vars, evalJsonPathVariable,
 											 DatumGetJsonbP(jb), !error, NULL,
 											 true);
 
@@ -2925,8 +2992,8 @@ JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty,
 	JsonPathExecResult res PG_USED_FOR_ASSERTS_ONLY;
 	int			count;
 
-	res = executeJsonPath(jp, vars, EvalJsonPathVar, DatumGetJsonbP(jb), !error,
-						  &found, true);
+	res = executeJsonPath(jp, vars, evalJsonPathVariable,
+						  DatumGetJsonbP(jb), !error, &found, true);
 
 	Assert(error || !jperIsError(res));
 
@@ -2992,8 +3059,8 @@ JsonPathValue(Datum jb, JsonPath *jp, bool *empty, bool *error, List *vars)
 	JsonPathExecResult jper PG_USED_FOR_ASSERTS_ONLY;
 	int			count;
 
-	jper = executeJsonPath(jp, vars, EvalJsonPathVar, DatumGetJsonbP(jb), !error,
-						   &found, true);
+	jper = executeJsonPath(jp, vars, evalJsonPathVariable,
+						   DatumGetJsonbP(jb), !error, &found, true);
 
 	Assert(error || !jperIsError(jper));
 
@@ -3306,7 +3373,7 @@ JsonTableResetContextItem(JsonTableScanState *scan, Datum item)
 
 	oldcxt = MemoryContextSwitchTo(scan->mcxt);
 
-	res = executeJsonPath(scan->path, scan->args, EvalJsonPathVar, js,
+	res = executeJsonPath(scan->path, scan->args, evalJsonPathVariable, js,
 						  scan->errorOnError, &scan->found, false /* FIXME */ );
 
 	MemoryContextSwitchTo(oldcxt);
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index adabf57c97b..b005bf440dd 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -244,7 +244,8 @@ typedef enum ExprEvalOp
 	EEOP_SUBPLAN,
 	EEOP_JSON_CONSTRUCTOR,
 	EEOP_IS_JSON,
-	EEOP_JSONEXPR,
+	EEOP_JSONEXPR_SKIP,
+	EEOP_JSONEXPR_PATH,
 
 	/* aggregation related nodes */
 	EEOP_AGG_STRICT_DESERIALIZE,
@@ -696,8 +697,18 @@ typedef struct ExprEvalStep
 		struct
 		{
 			struct JsonExprState *jsestate;
+			int			jump_done;
+			int			jump_default_on_empty; /* ON EMPTY DEFAULT expression */
+			int			jump_default_on_error; /* ON ERROR DEFAULT expression */
 		}			jsonexpr;
 
+		/* for EEOP_JSONEXPR_SKIP */
+		struct
+		{
+			struct JsonExprState *jsestate;
+			int			jump_done;
+		}			jsonexpr_skip;
+
 	}			d;
 } ExprEvalStep;
 
@@ -764,11 +775,8 @@ typedef struct JsonExprState
 
 	NullableDatum
 				formatted_expr,	/* formatted context item value */
-				coercion_expr,	/* input for JSON item coercion */
 				pathspec;		/* path specification value */
 
-	ExprState  *default_on_empty;	/* ON EMPTY DEFAULT expression */
-	ExprState  *default_on_error;	/* ON ERROR DEFAULT expression */
 	List	   *args;			/* passing arguments */
 } JsonExprState;
 
@@ -831,8 +839,8 @@ extern void ExecEvalSysVar(ExprState *state, ExprEvalStep *op,
 						   ExprContext *econtext, TupleTableSlot *slot);
 extern void ExecEvalJsonConstructor(ExprState *state, ExprEvalStep *op,
 									ExprContext *econtext);
-extern void ExecEvalJsonExpr(ExprState *state, ExprEvalStep *op,
-							 ExprContext *econtext);
+extern int ExecEvalJsonExpr(ExprState *state, ExprEvalStep *op);
+extern bool ExecEvalJsonExprSkip(ExprState *state, ExprEvalStep *op);
 
 extern void ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup pergroup,
 							 ExprContext *aggcontext);
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index e9134b7eab4..a33a770f7aa 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -280,10 +280,6 @@ extern Datum JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper,
 						   bool *empty, bool *error, List *vars);
 extern JsonbValue *JsonPathValue(Datum jb, JsonPath *jp, bool *empty,
 								 bool *error, List *vars);
-
-extern int	EvalJsonPathVar(void *vars, char *varName, int varNameLen,
-							JsonbValue *val, JsonbValue *baseObject);
-
 extern PGDLLIMPORT const TableFuncRoutine JsonbTableRoutine;
 
 #endif
-- 
2.17.1