On 22.08.2019 18:56, Pavel Stehule wrote:


čt 22. 8. 2019 v 17:51 odesílatel Konstantin Knizhnik <k.knizh...@postgrespro.ru <mailto:k.knizh...@postgrespro.ru>> napsal:

    Some more information...
    First of all I found out that marking PL/pgSQL function as
    immutable significantly increase speed of its execution:
    19808 ms vs. 27594. It happens because exec_eval_simple_expr is
    taken snapshot if function is volatile (default).
    I wonder if PL/pgSQL compiler can detect that evaluated expression
    itself is actually immutable  and there is no need to take snapshot
    for each invocation of this function. Also I have tried yet
    another PL language - JavaScript, which is now new outsider,
    despite to the fact that
    v8 JIT compiler is very good.


I have a plan to do some work in this direction. Snapshot is not necessary for almost buildin functions. If expr calls only buildin functions, then probably can be called without snapshot and without any work with plan cache.


I wonder if the following simple patch is correct?


--
Konstantin Knizhnik
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index a4697dc..98619be 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -6157,7 +6157,7 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
 	 * updates made so far by our own function.
 	 */
 	oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
-	if (!estate->readonly_func)
+	if (!estate->readonly_func && expr->expr_needs_snapshot)
 	{
 		CommandCounterIncrement();
 		PushActiveSnapshot(GetTransactionSnapshot());
@@ -6182,7 +6182,7 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
 
 	estate->paramLI->parserSetupArg = save_setup_arg;
 
-	if (!estate->readonly_func)
+	if (!estate->readonly_func && expr->expr_needs_snapshot)
 		PopActiveSnapshot();
 
 	MemoryContextSwitchTo(oldcontext);
@@ -7978,6 +7978,31 @@ exec_simple_check_plan(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
 }
 
 /*
+ * expr_needs_snapshot --- check if expression contains calls of non-immutable functions or subqueries
+ */
+static bool
+expr_needs_snapshot(Node* expr, void* ctx)
+{
+	if (expr == NULL)
+		return false;
+	switch (nodeTag(expr))
+	{
+		case T_FuncExpr:
+			if (func_volatile(((FuncExpr *)expr)->funcid) != PROVOLATILE_IMMUTABLE)
+				return true;
+			break;
+		case T_Query:
+		case T_SubPlan:
+		case T_AlternativeSubPlan:
+		case T_SubLink:
+			return true;
+		default:
+			break;
+	}
+	return expression_tree_walker(expr, expr_needs_snapshot, ctx);
+}
+
+/*
  * exec_save_simple_expr --- extract simple expression from CachedPlan
  */
 static void
@@ -8046,6 +8071,7 @@ exec_save_simple_expr(PLpgSQL_expr *expr, CachedPlan *cplan)
 	 * current transaction".
 	 */
 	expr->expr_simple_expr = tle_expr;
+	expr->expr_needs_snapshot = expr_needs_snapshot(tle_expr, NULL);
 	expr->expr_simple_generation = cplan->generation;
 	expr->expr_simple_state = NULL;
 	expr->expr_simple_in_use = false;
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index f66b2ba..454131f 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -243,6 +243,7 @@ typedef struct PLpgSQL_expr
 	 */
 	ExprState  *expr_simple_state;	/* eval tree for expr_simple_expr */
 	bool		expr_simple_in_use; /* true if eval tree is active */
+	bool		expr_needs_snapshot; /* true if simple expression calls non-immutable functions or performs subqueries */
 	LocalTransactionId expr_simple_lxid;
 } PLpgSQL_expr;
 

Reply via email to