From 45d938995ef8bab8049638a0ee63efbc9595ab45 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Mon, 15 Aug 2022 15:04:17 +0300
Subject: [PATCH v6 2/3] Add EEOP_SUBTRANS executor step

---
 src/backend/executor/execExprInterp.c | 102 +++++++++++++++++++++++++-
 src/backend/jit/llvm/llvmjit_expr.c   |  90 +++++++++++++++++++++--
 src/backend/jit/llvm/llvmjit_types.c  |   1 +
 src/include/executor/execExpr.h       |  13 ++++
 4 files changed, 198 insertions(+), 8 deletions(-)

diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 353346d5528..662d47f8cbb 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -158,6 +158,8 @@ static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod,
 									bool *changed);
 static void ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op,
 							   ExprContext *econtext, bool checkisnull);
+static void ExecEvalSubtransInterp(ExprState *state, ExprEvalStep *op,
+								   ExprContext *econtext);
 
 /* fast-path evaluation functions */
 static Datum ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull);
@@ -398,7 +400,8 @@ ExecReadyInterpretedExpr(ExprState *state)
  * (Only applies when EEO_USE_COMPUTED_GOTO is defined.)
  */
 static Datum
-ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
+ExecInterpExprStep(ExprState *state, int stepno,
+				   ExprContext *econtext, bool *isnull)
 {
 	ExprEvalStep *op;
 	TupleTableSlot *resultslot;
@@ -488,6 +491,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		&&CASE_EEOP_GROUPING_FUNC,
 		&&CASE_EEOP_WINDOW_FUNC,
 		&&CASE_EEOP_SUBPLAN,
+		&&CASE_EEOP_SUBTRANS,
 		&&CASE_EEOP_JSON_CONSTRUCTOR,
 		&&CASE_EEOP_IS_JSON,
 		&&CASE_EEOP_JSONEXPR,
@@ -519,7 +523,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 #endif							/* EEO_USE_COMPUTED_GOTO */
 
 	/* setup state */
-	op = state->steps;
+	op = &state->steps[stepno];
 	resultslot = state->resultslot;
 	innerslot = econtext->ecxt_innertuple;
 	outerslot = econtext->ecxt_outertuple;
@@ -1827,6 +1831,16 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			EEO_NEXT();
 		}
 
+		EEO_CASE(EEOP_SUBTRANS)
+		{
+			/* too complex for an inline implementation */
+			if (ExecEvalSubtrans(state, op, econtext,
+								 ExecEvalSubtransInterp))
+				EEO_JUMP(op->d.subtrans.jump_done);
+			else
+				EEO_JUMP(op->d.subtrans.jump_error);
+		}
+
 		EEO_CASE(EEOP_JSON_CONSTRUCTOR)
 		{
 			/* too complex for an inline implementation */
@@ -1861,6 +1875,12 @@ out:
 	return state->resvalue;
 }
 
+static Datum
+ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+	return ExecInterpExprStep(state, 0, econtext, isnull);
+}
+
 /*
  * Expression evaluation callback that performs extra checks before executing
  * the expression. Declared extern so other methods of execution can use it
@@ -4620,6 +4640,84 @@ ExecAggPlainTransByRef(AggState *aggstate, AggStatePerTrans pertrans,
 	MemoryContextSwitchTo(oldContext);
 }
 
+/*
+ * Execute subexpression steps using recursive interpreter call.
+ * "op" points EEOP_SUBTRANS step.
+ */
+static void
+ExecEvalSubtransInterp(ExprState *state, ExprEvalStep *op,
+					   ExprContext *econtext)
+{
+	/* Recursively call interpreter for next step ignoring result */
+	bool		isnull;
+
+	(void) ExecInterpExprStep(state, op - state->steps + 1, econtext,
+							  &isnull);
+}
+
+/*
+ * Execute EEOP_SUBTRANS, calling "eval" function inside a
+ * subtransaction.  This function executes sequence of subexpression
+ * steps that begins right after current step and ends with EEOP_DONE.
+ */
+bool
+ExecEvalSubtrans(ExprState *state, ExprEvalStep *op,
+						 ExprContext *econtext, ExecEvalSubroutine eval)
+{
+	MemoryContext oldcontext = CurrentMemoryContext;
+	ResourceOwner oldowner = CurrentResourceOwner;
+	bool		error;
+
+	/*
+	 * We should catch exceptions of category ERRCODE_DATA_EXCEPTION and
+	 * execute the corresponding ON ERROR behavior then.
+	 */
+	BeginInternalSubTransaction(NULL);
+	/* Want to execute expressions inside function's memory context */
+	MemoryContextSwitchTo(oldcontext);
+
+	PG_TRY();
+	{
+		eval(state, op, econtext);
+
+		/* Commit the inner transaction, return to outer xact context */
+		ReleaseCurrentSubTransaction();
+		MemoryContextSwitchTo(oldcontext);
+		CurrentResourceOwner = oldowner;
+
+		error = false;
+	}
+	PG_CATCH();
+	{
+		ErrorData  *edata;
+		int			ecategory;
+
+		/* Save error info in oldcontext */
+		MemoryContextSwitchTo(oldcontext);
+		edata = CopyErrorData();
+		FlushErrorState();
+
+		/* Abort the inner transaction */
+		RollbackAndReleaseCurrentSubTransaction();
+		MemoryContextSwitchTo(oldcontext);
+		CurrentResourceOwner = oldowner;
+
+		ecategory = ERRCODE_TO_CATEGORY(edata->sqlerrcode);
+
+		if (ecategory != ERRCODE_DATA_EXCEPTION && 	/* jsonpath and other data errors */
+			ecategory != ERRCODE_INTEGRITY_CONSTRAINT_VIOLATION) /* domain errors */
+			ReThrowError(edata);
+
+		*op->resvalue = (Datum) 0;
+		*op->resnull = true;
+
+		error = true;
+	}
+	PG_END_TRY();
+
+	return !error;
+}
+
 /*
  * Evaluate a JSON constructor expression.
  */
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index bd3965143da..614ed58f1cb 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -53,6 +53,8 @@ typedef struct CompiledExprState
 
 
 static Datum ExecRunCompiledExpr(ExprState *state, ExprContext *econtext, bool *isNull);
+static void ExecEvalSubtransCompiled(ExprState *state, ExprEvalStep *op,
+									 ExprContext *econtext);
 
 static LLVMValueRef BuildV1Call(LLVMJitContext *context, LLVMBuilderRef b,
 								LLVMModuleRef mod, FunctionCallInfo fcinfo,
@@ -72,10 +74,10 @@ static LLVMValueRef create_LifetimeEnd(LLVMModuleRef mod);
 
 
 /*
- * JIT compile expression.
+ * JIT compile expression staring from the specified step.
  */
-bool
-llvm_compile_expr(ExprState *state)
+static bool
+llvm_compile_expr_step(ExprState *state, int start_opno)
 {
 	PlanState  *parent = state->parent;
 	char	   *funcname;
@@ -224,13 +226,13 @@ llvm_compile_expr(ExprState *state)
 
 	/* allocate blocks for each op upfront, so we can do jumps easily */
 	opblocks = palloc(sizeof(LLVMBasicBlockRef) * state->steps_len);
-	for (int opno = 0; opno < state->steps_len; opno++)
+	for (int opno = start_opno; opno < state->steps_len; opno++)
 		opblocks[opno] = l_bb_append_v(eval_fn, "b.op.%d.start", opno);
 
 	/* jump from entry to first block */
-	LLVMBuildBr(b, opblocks[0]);
+	LLVMBuildBr(b, opblocks[start_opno]);
 
-	for (int opno = 0; opno < state->steps_len; opno++)
+	for (int opno = start_opno; opno < state->steps_len; opno++)
 	{
 		ExprEvalStep *op;
 		ExprEvalOp	opcode;
@@ -2395,6 +2397,62 @@ llvm_compile_expr(ExprState *state)
 				LLVMBuildBr(b, opblocks[opno + 1]);
 				break;
 
+			case EEOP_SUBTRANS:
+				{
+					ExprState *substate;
+					LLVMValueRef v_ret;
+					LLVMTypeRef v_functype;
+
+					/* Create subexpression ExprState */
+					substate = makeNode(ExprState);
+					substate->parent = state->parent;
+					substate->steps = state->steps;
+					substate->steps_len = op->d.subtrans.jump_next;
+
+					/*
+					 * Compile it starting from the first subexpression
+					 * step, which is the next step.
+					 */
+					llvm_compile_expr_step(substate, opno + 1);
+
+					/*
+					 * Save subexpression state in the private area of
+					 * the step.
+					 */
+					op->d.subtrans.private_data = substate;
+
+					/*
+					 * Call ExecEvalJsonExprSubtrans() passing
+					 * ExecEvalSubtransCompiled(), which will execute
+					 * subexpression getting ExprState from the private
+					 * area.
+					 */
+					v_functype = llvm_pg_var_func_type("TypeExecEvalSubroutine");
+
+					v_ret = build_EvalXFunc(b, mod, "ExecEvalSubtrans",
+											v_state, op, v_econtext,
+											l_ptr_const(ExecEvalSubtransCompiled,
+														LLVMPointerType(v_functype, 0)));
+					v_ret = LLVMBuildZExt(b, v_ret, TypeStorageBool, "");
+
+					/*
+					 * Jump to "error" or "done" steps depending of
+					 * returned boolean value (TRUE means "done").
+					 */
+					LLVMBuildCondBr(b,
+									LLVMBuildICmp(b,
+												  LLVMIntEQ,
+												  v_ret,
+												  l_sbool_const(0),
+												  ""),
+									opblocks[op->d.subtrans.jump_error],
+									opblocks[op->d.subtrans.jump_done]);
+
+					/* Skip subexpression steps */
+					opno = op->d.subtrans.jump_next - 1;
+					break;
+				}
+
 			case EEOP_JSON_CONSTRUCTOR:
 				build_EvalXFunc(b, mod, "ExecEvalJsonConstructor",
 								v_state, op, v_econtext);
@@ -2447,6 +2505,15 @@ llvm_compile_expr(ExprState *state)
 	return true;
 }
 
+/*
+ * JIT compile expression staring from the first step.
+ */
+bool
+llvm_compile_expr(ExprState *state)
+{
+	return llvm_compile_expr_step(state, 0);
+}
+
 /*
  * Run compiled expression.
  *
@@ -2475,6 +2542,17 @@ ExecRunCompiledExpr(ExprState *state, ExprContext *econtext, bool *isNull)
 	return func(state, econtext, isNull);
 }
 
+/* Execute subexpression steps using compiled ExprState */
+static void
+ExecEvalSubtransCompiled(ExprState *state, ExprEvalStep *op,
+						 ExprContext *econtext)
+{
+	ExprState  *substate = op->d.subtrans.private_data;
+	bool		isnull;
+
+	(void) ExecEvalExpr(substate, econtext, &isnull);
+}
+
 static LLVMValueRef
 BuildV1Call(LLVMJitContext *context, LLVMBuilderRef b,
 			LLVMModuleRef mod, FunctionCallInfo fcinfo,
diff --git a/src/backend/jit/llvm/llvmjit_types.c b/src/backend/jit/llvm/llvmjit_types.c
index 373471ad27f..a9ddba6e4cd 100644
--- a/src/backend/jit/llvm/llvmjit_types.c
+++ b/src/backend/jit/llvm/llvmjit_types.c
@@ -133,6 +133,7 @@ void	   *referenced_functions[] =
 	ExecEvalSysVar,
 	ExecEvalWholeRowVar,
 	ExecEvalXmlExpr,
+	ExecEvalSubtrans,
 	ExecEvalJsonConstructor,
 	ExecEvalJsonIsPredicate,
 	ExecEvalJson,
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index de9bbd2abcd..14064dd9521 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -242,6 +242,7 @@ typedef enum ExprEvalOp
 	EEOP_GROUPING_FUNC,
 	EEOP_WINDOW_FUNC,
 	EEOP_SUBPLAN,
+	EEOP_SUBTRANS,
 	EEOP_JSON_CONSTRUCTOR,
 	EEOP_IS_JSON,
 	EEOP_JSONEXPR,
@@ -680,6 +681,15 @@ typedef struct ExprEvalStep
 			int			setoff;
 		}			agg_trans;
 
+		/* for EEOP_SUBTRANS */
+		struct
+		{
+			void	   *private_data;
+			int			jump_done;
+			int			jump_error;
+			int			jump_next;
+		}			subtrans;
+
 		/* for EEOP_JSON_CONSTRUCTOR */
 		struct
 		{
@@ -866,6 +876,9 @@ extern void ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op,
 								ExprContext *econtext);
 extern void ExecEvalSysVar(ExprState *state, ExprEvalStep *op,
 						   ExprContext *econtext, TupleTableSlot *slot);
+extern bool ExecEvalSubtrans(ExprState *state, ExprEvalStep *op,
+							 ExprContext *econtext,
+							 ExecEvalSubroutine func);
 extern void ExecEvalJsonConstructor(ExprState *state, ExprEvalStep *op,
 									ExprContext *econtext);
 extern void ExecEvalJson(ExprState *state, ExprEvalStep *op,
-- 
2.25.1

