From 1f902d499eda862a98e881a2227b8d2cf3565374 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 3 Jul 2020 15:42:10 -0400 Subject: [PATCH] Inline plpgsql's exec_stmt() into exec_stmts(). This saves one level of C function call per plpgsql statement executed, and permits a tiny additional optimization of not saving and restoring estate->err_stmt for each statement in a block. The net effect seems nearly un-measurable on x86_64, but there's a clear win on aarch64, amounting to two or three percent in a loop over a few simple plpgsql statements. To do this, we have to get rid of the other existing call sites for exec_stmt(). Replace them with exec_toplevel_block(), which is just defined to do what exec_stmts() does, but for a single PLpgSQL_stmt_block statement. Hard-wiring the expectation of which statement type applies here allows us to skip the dispatch switch, making this not much uglier than the previous factorization. Amit Khandekar, tweaked a bit by me Discussion: https://postgr.es/m/CAJ3gD9eBNrmUD7WBBLG8ohaZ485H9y+4eihQTgr+K8Lhka3vcQ@mail.gmail.com --- src/pl/plpgsql/src/pl_exec.c | 344 ++++++++++++++++++----------------- 1 file changed, 179 insertions(+), 165 deletions(-) diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index f41d675d65..54900e01c8 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -260,12 +260,12 @@ static MemoryContext get_stmt_mcontext(PLpgSQL_execstate *estate); static void push_stmt_mcontext(PLpgSQL_execstate *estate); static void pop_stmt_mcontext(PLpgSQL_execstate *estate); +static int exec_toplevel_block(PLpgSQL_execstate *estate, + PLpgSQL_stmt_block *block); static int exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block); static int exec_stmts(PLpgSQL_execstate *estate, List *stmts); -static int exec_stmt(PLpgSQL_execstate *estate, - PLpgSQL_stmt *stmt); static int exec_stmt_assign(PLpgSQL_execstate *estate, PLpgSQL_stmt_assign *stmt); static int exec_stmt_perform(PLpgSQL_execstate *estate, @@ -599,11 +599,9 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo, * Now call the toplevel block of statements */ estate.err_text = NULL; - estate.err_stmt = (PLpgSQL_stmt *) (func->action); - rc = exec_stmt(&estate, (PLpgSQL_stmt *) func->action); + rc = exec_toplevel_block(&estate, func->action); if (rc != PLPGSQL_RC_RETURN) { - estate.err_stmt = NULL; estate.err_text = NULL; ereport(ERROR, (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT), @@ -613,7 +611,6 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo, /* * We got a return value - process it */ - estate.err_stmt = NULL; estate.err_text = gettext_noop("while casting return value to function's return type"); fcinfo->isnull = estate.retisnull; @@ -1015,18 +1012,15 @@ plpgsql_exec_trigger(PLpgSQL_function *func, * Now call the toplevel block of statements */ estate.err_text = NULL; - estate.err_stmt = (PLpgSQL_stmt *) (func->action); - rc = exec_stmt(&estate, (PLpgSQL_stmt *) func->action); + rc = exec_toplevel_block(&estate, func->action); if (rc != PLPGSQL_RC_RETURN) { - estate.err_stmt = NULL; estate.err_text = NULL; ereport(ERROR, (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT), errmsg("control reached end of trigger procedure without RETURN"))); } - estate.err_stmt = NULL; estate.err_text = gettext_noop("during function exit"); if (estate.retisset) @@ -1176,18 +1170,15 @@ plpgsql_exec_event_trigger(PLpgSQL_function *func, EventTriggerData *trigdata) * Now call the toplevel block of statements */ estate.err_text = NULL; - estate.err_stmt = (PLpgSQL_stmt *) (func->action); - rc = exec_stmt(&estate, (PLpgSQL_stmt *) func->action); + rc = exec_toplevel_block(&estate, func->action); if (rc != PLPGSQL_RC_RETURN) { - estate.err_stmt = NULL; estate.err_text = NULL; ereport(ERROR, (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT), errmsg("control reached end of trigger procedure without RETURN"))); } - estate.err_stmt = NULL; estate.err_text = gettext_noop("during function exit"); /* @@ -1584,6 +1575,40 @@ exception_matches_conditions(ErrorData *edata, PLpgSQL_condition *cond) } +/* ---------- + * exec_toplevel_block Execute the toplevel block + * + * This is intentionally equivalent to executing exec_stmts() with a + * list consisting of the one statement. One tiny difference is that + * we do not bother to save the entry value of estate->err_stmt; + * that's assumed to be NULL. + * ---------- + */ +static int +exec_toplevel_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block) +{ + int rc; + + estate->err_stmt = (PLpgSQL_stmt *) block; + + /* Let the plugin know that we are about to execute this statement */ + if (*plpgsql_plugin_ptr && (*plpgsql_plugin_ptr)->stmt_beg) + ((*plpgsql_plugin_ptr)->stmt_beg) (estate, (PLpgSQL_stmt *) block); + + CHECK_FOR_INTERRUPTS(); + + rc = exec_stmt_block(estate, block); + + /* Let the plugin know that we have finished executing this statement */ + if (*plpgsql_plugin_ptr && (*plpgsql_plugin_ptr)->stmt_end) + ((*plpgsql_plugin_ptr)->stmt_end) (estate, (PLpgSQL_stmt *) block); + + estate->err_stmt = NULL; + + return rc; +} + + /* ---------- * exec_stmt_block Execute a block of statements * ---------- @@ -1917,6 +1942,7 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block) static int exec_stmts(PLpgSQL_execstate *estate, List *stmts) { + PLpgSQL_stmt *save_estmt = estate->err_stmt; ListCell *s; if (stmts == NIL) @@ -1933,162 +1959,150 @@ exec_stmts(PLpgSQL_execstate *estate, List *stmts) foreach(s, stmts) { PLpgSQL_stmt *stmt = (PLpgSQL_stmt *) lfirst(s); - int rc = exec_stmt(estate, stmt); + int rc; + + estate->err_stmt = stmt; + + /* Let the plugin know that we are about to execute this statement */ + if (*plpgsql_plugin_ptr && (*plpgsql_plugin_ptr)->stmt_beg) + ((*plpgsql_plugin_ptr)->stmt_beg) (estate, stmt); + + CHECK_FOR_INTERRUPTS(); + + switch (stmt->cmd_type) + { + case PLPGSQL_STMT_BLOCK: + rc = exec_stmt_block(estate, (PLpgSQL_stmt_block *) stmt); + break; + + case PLPGSQL_STMT_ASSIGN: + rc = exec_stmt_assign(estate, (PLpgSQL_stmt_assign *) stmt); + break; + + case PLPGSQL_STMT_PERFORM: + rc = exec_stmt_perform(estate, (PLpgSQL_stmt_perform *) stmt); + break; + + case PLPGSQL_STMT_CALL: + rc = exec_stmt_call(estate, (PLpgSQL_stmt_call *) stmt); + break; + + case PLPGSQL_STMT_GETDIAG: + rc = exec_stmt_getdiag(estate, (PLpgSQL_stmt_getdiag *) stmt); + break; + + case PLPGSQL_STMT_IF: + rc = exec_stmt_if(estate, (PLpgSQL_stmt_if *) stmt); + break; + + case PLPGSQL_STMT_CASE: + rc = exec_stmt_case(estate, (PLpgSQL_stmt_case *) stmt); + break; + + case PLPGSQL_STMT_LOOP: + rc = exec_stmt_loop(estate, (PLpgSQL_stmt_loop *) stmt); + break; + + case PLPGSQL_STMT_WHILE: + rc = exec_stmt_while(estate, (PLpgSQL_stmt_while *) stmt); + break; + + case PLPGSQL_STMT_FORI: + rc = exec_stmt_fori(estate, (PLpgSQL_stmt_fori *) stmt); + break; + + case PLPGSQL_STMT_FORS: + rc = exec_stmt_fors(estate, (PLpgSQL_stmt_fors *) stmt); + break; + + case PLPGSQL_STMT_FORC: + rc = exec_stmt_forc(estate, (PLpgSQL_stmt_forc *) stmt); + break; + + case PLPGSQL_STMT_FOREACH_A: + rc = exec_stmt_foreach_a(estate, (PLpgSQL_stmt_foreach_a *) stmt); + break; + + case PLPGSQL_STMT_EXIT: + rc = exec_stmt_exit(estate, (PLpgSQL_stmt_exit *) stmt); + break; + + case PLPGSQL_STMT_RETURN: + rc = exec_stmt_return(estate, (PLpgSQL_stmt_return *) stmt); + break; + + case PLPGSQL_STMT_RETURN_NEXT: + rc = exec_stmt_return_next(estate, (PLpgSQL_stmt_return_next *) stmt); + break; + + case PLPGSQL_STMT_RETURN_QUERY: + rc = exec_stmt_return_query(estate, (PLpgSQL_stmt_return_query *) stmt); + break; + + case PLPGSQL_STMT_RAISE: + rc = exec_stmt_raise(estate, (PLpgSQL_stmt_raise *) stmt); + break; + + case PLPGSQL_STMT_ASSERT: + rc = exec_stmt_assert(estate, (PLpgSQL_stmt_assert *) stmt); + break; + + case PLPGSQL_STMT_EXECSQL: + rc = exec_stmt_execsql(estate, (PLpgSQL_stmt_execsql *) stmt); + break; + + case PLPGSQL_STMT_DYNEXECUTE: + rc = exec_stmt_dynexecute(estate, (PLpgSQL_stmt_dynexecute *) stmt); + break; + + case PLPGSQL_STMT_DYNFORS: + rc = exec_stmt_dynfors(estate, (PLpgSQL_stmt_dynfors *) stmt); + break; + + case PLPGSQL_STMT_OPEN: + rc = exec_stmt_open(estate, (PLpgSQL_stmt_open *) stmt); + break; + + case PLPGSQL_STMT_FETCH: + rc = exec_stmt_fetch(estate, (PLpgSQL_stmt_fetch *) stmt); + break; + + case PLPGSQL_STMT_CLOSE: + rc = exec_stmt_close(estate, (PLpgSQL_stmt_close *) stmt); + break; + + case PLPGSQL_STMT_COMMIT: + rc = exec_stmt_commit(estate, (PLpgSQL_stmt_commit *) stmt); + break; + + case PLPGSQL_STMT_ROLLBACK: + rc = exec_stmt_rollback(estate, (PLpgSQL_stmt_rollback *) stmt); + break; + + case PLPGSQL_STMT_SET: + rc = exec_stmt_set(estate, (PLpgSQL_stmt_set *) stmt); + break; + + default: + /* point err_stmt to parent, since this one seems corrupt */ + estate->err_stmt = save_estmt; + elog(ERROR, "unrecognized cmd_type: %d", stmt->cmd_type); + rc = -1; /* keep compiler quiet */ + } + + /* Let the plugin know that we have finished executing this statement */ + if (*plpgsql_plugin_ptr && (*plpgsql_plugin_ptr)->stmt_end) + ((*plpgsql_plugin_ptr)->stmt_end) (estate, stmt); if (rc != PLPGSQL_RC_OK) - return rc; - } - - return PLPGSQL_RC_OK; -} - - -/* ---------- - * exec_stmt Distribute one statement to the statements - * type specific execution function. - * ---------- - */ -static int -exec_stmt(PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt) -{ - PLpgSQL_stmt *save_estmt; - int rc = -1; - - save_estmt = estate->err_stmt; - estate->err_stmt = stmt; - - /* Let the plugin know that we are about to execute this statement */ - if (*plpgsql_plugin_ptr && (*plpgsql_plugin_ptr)->stmt_beg) - ((*plpgsql_plugin_ptr)->stmt_beg) (estate, stmt); - - CHECK_FOR_INTERRUPTS(); - - switch (stmt->cmd_type) - { - case PLPGSQL_STMT_BLOCK: - rc = exec_stmt_block(estate, (PLpgSQL_stmt_block *) stmt); - break; - - case PLPGSQL_STMT_ASSIGN: - rc = exec_stmt_assign(estate, (PLpgSQL_stmt_assign *) stmt); - break; - - case PLPGSQL_STMT_PERFORM: - rc = exec_stmt_perform(estate, (PLpgSQL_stmt_perform *) stmt); - break; - - case PLPGSQL_STMT_CALL: - rc = exec_stmt_call(estate, (PLpgSQL_stmt_call *) stmt); - break; - - case PLPGSQL_STMT_GETDIAG: - rc = exec_stmt_getdiag(estate, (PLpgSQL_stmt_getdiag *) stmt); - break; - - case PLPGSQL_STMT_IF: - rc = exec_stmt_if(estate, (PLpgSQL_stmt_if *) stmt); - break; - - case PLPGSQL_STMT_CASE: - rc = exec_stmt_case(estate, (PLpgSQL_stmt_case *) stmt); - break; - - case PLPGSQL_STMT_LOOP: - rc = exec_stmt_loop(estate, (PLpgSQL_stmt_loop *) stmt); - break; - - case PLPGSQL_STMT_WHILE: - rc = exec_stmt_while(estate, (PLpgSQL_stmt_while *) stmt); - break; - - case PLPGSQL_STMT_FORI: - rc = exec_stmt_fori(estate, (PLpgSQL_stmt_fori *) stmt); - break; - - case PLPGSQL_STMT_FORS: - rc = exec_stmt_fors(estate, (PLpgSQL_stmt_fors *) stmt); - break; - - case PLPGSQL_STMT_FORC: - rc = exec_stmt_forc(estate, (PLpgSQL_stmt_forc *) stmt); - break; - - case PLPGSQL_STMT_FOREACH_A: - rc = exec_stmt_foreach_a(estate, (PLpgSQL_stmt_foreach_a *) stmt); - break; - - case PLPGSQL_STMT_EXIT: - rc = exec_stmt_exit(estate, (PLpgSQL_stmt_exit *) stmt); - break; - - case PLPGSQL_STMT_RETURN: - rc = exec_stmt_return(estate, (PLpgSQL_stmt_return *) stmt); - break; - - case PLPGSQL_STMT_RETURN_NEXT: - rc = exec_stmt_return_next(estate, (PLpgSQL_stmt_return_next *) stmt); - break; - - case PLPGSQL_STMT_RETURN_QUERY: - rc = exec_stmt_return_query(estate, (PLpgSQL_stmt_return_query *) stmt); - break; - - case PLPGSQL_STMT_RAISE: - rc = exec_stmt_raise(estate, (PLpgSQL_stmt_raise *) stmt); - break; - - case PLPGSQL_STMT_ASSERT: - rc = exec_stmt_assert(estate, (PLpgSQL_stmt_assert *) stmt); - break; - - case PLPGSQL_STMT_EXECSQL: - rc = exec_stmt_execsql(estate, (PLpgSQL_stmt_execsql *) stmt); - break; - - case PLPGSQL_STMT_DYNEXECUTE: - rc = exec_stmt_dynexecute(estate, (PLpgSQL_stmt_dynexecute *) stmt); - break; - - case PLPGSQL_STMT_DYNFORS: - rc = exec_stmt_dynfors(estate, (PLpgSQL_stmt_dynfors *) stmt); - break; - - case PLPGSQL_STMT_OPEN: - rc = exec_stmt_open(estate, (PLpgSQL_stmt_open *) stmt); - break; - - case PLPGSQL_STMT_FETCH: - rc = exec_stmt_fetch(estate, (PLpgSQL_stmt_fetch *) stmt); - break; - - case PLPGSQL_STMT_CLOSE: - rc = exec_stmt_close(estate, (PLpgSQL_stmt_close *) stmt); - break; - - case PLPGSQL_STMT_COMMIT: - rc = exec_stmt_commit(estate, (PLpgSQL_stmt_commit *) stmt); - break; - - case PLPGSQL_STMT_ROLLBACK: - rc = exec_stmt_rollback(estate, (PLpgSQL_stmt_rollback *) stmt); - break; - - case PLPGSQL_STMT_SET: - rc = exec_stmt_set(estate, (PLpgSQL_stmt_set *) stmt); - break; - - default: + { estate->err_stmt = save_estmt; - elog(ERROR, "unrecognized cmd_type: %d", stmt->cmd_type); - } - - /* Let the plugin know that we have finished executing this statement */ - if (*plpgsql_plugin_ptr && (*plpgsql_plugin_ptr)->stmt_end) - ((*plpgsql_plugin_ptr)->stmt_end) (estate, stmt); + return rc; + } + } /* end of loop over statements */ estate->err_stmt = save_estmt; - - return rc; + return PLPGSQL_RC_OK; }