Inline plpgsql's exec_stmt() into exec_stmts().
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 3 Jul 2020 19:42:10 +0000 (15:42 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 3 Jul 2020 19:42:10 +0000 (15:42 -0400)
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

index f41d675d656342a3045da52f5049b8db68d240b3..54900e01c8f59bc2e3141c29fd8fa161c805daca 100644 (file)
@@ -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);
-
-               if (rc != PLPGSQL_RC_OK)
-                       return rc;
-       }
+               int                     rc;
 
-       return PLPGSQL_RC_OK;
-}
+               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);
 
-/* ----------
- * 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;
+               CHECK_FOR_INTERRUPTS();
 
-       /* 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);
+               switch (stmt->cmd_type)
+               {
+                       case PLPGSQL_STMT_BLOCK:
+                               rc = exec_stmt_block(estate, (PLpgSQL_stmt_block *) stmt);
+                               break;
 
-       CHECK_FOR_INTERRUPTS();
+                       case PLPGSQL_STMT_ASSIGN:
+                               rc = exec_stmt_assign(estate, (PLpgSQL_stmt_assign *) stmt);
+                               break;
 
-       switch (stmt->cmd_type)
-       {
-               case PLPGSQL_STMT_BLOCK:
-                       rc = exec_stmt_block(estate, (PLpgSQL_stmt_block *) stmt);
-                       break;
+                       case PLPGSQL_STMT_PERFORM:
+                               rc = exec_stmt_perform(estate, (PLpgSQL_stmt_perform *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_ASSIGN:
-                       rc = exec_stmt_assign(estate, (PLpgSQL_stmt_assign *) stmt);
-                       break;
+                       case PLPGSQL_STMT_CALL:
+                               rc = exec_stmt_call(estate, (PLpgSQL_stmt_call *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_PERFORM:
-                       rc = exec_stmt_perform(estate, (PLpgSQL_stmt_perform *) stmt);
-                       break;
+                       case PLPGSQL_STMT_GETDIAG:
+                               rc = exec_stmt_getdiag(estate, (PLpgSQL_stmt_getdiag *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_CALL:
-                       rc = exec_stmt_call(estate, (PLpgSQL_stmt_call *) stmt);
-                       break;
+                       case PLPGSQL_STMT_IF:
+                               rc = exec_stmt_if(estate, (PLpgSQL_stmt_if *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_GETDIAG:
-                       rc = exec_stmt_getdiag(estate, (PLpgSQL_stmt_getdiag *) stmt);
-                       break;
+                       case PLPGSQL_STMT_CASE:
+                               rc = exec_stmt_case(estate, (PLpgSQL_stmt_case *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_IF:
-                       rc = exec_stmt_if(estate, (PLpgSQL_stmt_if *) stmt);
-                       break;
+                       case PLPGSQL_STMT_LOOP:
+                               rc = exec_stmt_loop(estate, (PLpgSQL_stmt_loop *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_CASE:
-                       rc = exec_stmt_case(estate, (PLpgSQL_stmt_case *) stmt);
-                       break;
+                       case PLPGSQL_STMT_WHILE:
+                               rc = exec_stmt_while(estate, (PLpgSQL_stmt_while *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_LOOP:
-                       rc = exec_stmt_loop(estate, (PLpgSQL_stmt_loop *) stmt);
-                       break;
+                       case PLPGSQL_STMT_FORI:
+                               rc = exec_stmt_fori(estate, (PLpgSQL_stmt_fori *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_WHILE:
-                       rc = exec_stmt_while(estate, (PLpgSQL_stmt_while *) stmt);
-                       break;
+                       case PLPGSQL_STMT_FORS:
+                               rc = exec_stmt_fors(estate, (PLpgSQL_stmt_fors *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_FORI:
-                       rc = exec_stmt_fori(estate, (PLpgSQL_stmt_fori *) stmt);
-                       break;
+                       case PLPGSQL_STMT_FORC:
+                               rc = exec_stmt_forc(estate, (PLpgSQL_stmt_forc *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_FORS:
-                       rc = exec_stmt_fors(estate, (PLpgSQL_stmt_fors *) stmt);
-                       break;
+                       case PLPGSQL_STMT_FOREACH_A:
+                               rc = exec_stmt_foreach_a(estate, (PLpgSQL_stmt_foreach_a *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_FORC:
-                       rc = exec_stmt_forc(estate, (PLpgSQL_stmt_forc *) stmt);
-                       break;
+                       case PLPGSQL_STMT_EXIT:
+                               rc = exec_stmt_exit(estate, (PLpgSQL_stmt_exit *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_FOREACH_A:
-                       rc = exec_stmt_foreach_a(estate, (PLpgSQL_stmt_foreach_a *) stmt);
-                       break;
+                       case PLPGSQL_STMT_RETURN:
+                               rc = exec_stmt_return(estate, (PLpgSQL_stmt_return *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_EXIT:
-                       rc = exec_stmt_exit(estate, (PLpgSQL_stmt_exit *) stmt);
-                       break;
+                       case PLPGSQL_STMT_RETURN_NEXT:
+                               rc = exec_stmt_return_next(estate, (PLpgSQL_stmt_return_next *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_RETURN:
-                       rc = exec_stmt_return(estate, (PLpgSQL_stmt_return *) stmt);
-                       break;
+                       case PLPGSQL_STMT_RETURN_QUERY:
+                               rc = exec_stmt_return_query(estate, (PLpgSQL_stmt_return_query *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_RETURN_NEXT:
-                       rc = exec_stmt_return_next(estate, (PLpgSQL_stmt_return_next *) stmt);
-                       break;
+                       case PLPGSQL_STMT_RAISE:
+                               rc = exec_stmt_raise(estate, (PLpgSQL_stmt_raise *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_RETURN_QUERY:
-                       rc = exec_stmt_return_query(estate, (PLpgSQL_stmt_return_query *) stmt);
-                       break;
+                       case PLPGSQL_STMT_ASSERT:
+                               rc = exec_stmt_assert(estate, (PLpgSQL_stmt_assert *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_RAISE:
-                       rc = exec_stmt_raise(estate, (PLpgSQL_stmt_raise *) stmt);
-                       break;
+                       case PLPGSQL_STMT_EXECSQL:
+                               rc = exec_stmt_execsql(estate, (PLpgSQL_stmt_execsql *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_ASSERT:
-                       rc = exec_stmt_assert(estate, (PLpgSQL_stmt_assert *) stmt);
-                       break;
+                       case PLPGSQL_STMT_DYNEXECUTE:
+                               rc = exec_stmt_dynexecute(estate, (PLpgSQL_stmt_dynexecute *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_EXECSQL:
-                       rc = exec_stmt_execsql(estate, (PLpgSQL_stmt_execsql *) stmt);
-                       break;
+                       case PLPGSQL_STMT_DYNFORS:
+                               rc = exec_stmt_dynfors(estate, (PLpgSQL_stmt_dynfors *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_DYNEXECUTE:
-                       rc = exec_stmt_dynexecute(estate, (PLpgSQL_stmt_dynexecute *) stmt);
-                       break;
+                       case PLPGSQL_STMT_OPEN:
+                               rc = exec_stmt_open(estate, (PLpgSQL_stmt_open *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_DYNFORS:
-                       rc = exec_stmt_dynfors(estate, (PLpgSQL_stmt_dynfors *) stmt);
-                       break;
+                       case PLPGSQL_STMT_FETCH:
+                               rc = exec_stmt_fetch(estate, (PLpgSQL_stmt_fetch *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_OPEN:
-                       rc = exec_stmt_open(estate, (PLpgSQL_stmt_open *) stmt);
-                       break;
+                       case PLPGSQL_STMT_CLOSE:
+                               rc = exec_stmt_close(estate, (PLpgSQL_stmt_close *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_FETCH:
-                       rc = exec_stmt_fetch(estate, (PLpgSQL_stmt_fetch *) stmt);
-                       break;
+                       case PLPGSQL_STMT_COMMIT:
+                               rc = exec_stmt_commit(estate, (PLpgSQL_stmt_commit *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_CLOSE:
-                       rc = exec_stmt_close(estate, (PLpgSQL_stmt_close *) stmt);
-                       break;
+                       case PLPGSQL_STMT_ROLLBACK:
+                               rc = exec_stmt_rollback(estate, (PLpgSQL_stmt_rollback *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_COMMIT:
-                       rc = exec_stmt_commit(estate, (PLpgSQL_stmt_commit *) stmt);
-                       break;
+                       case PLPGSQL_STMT_SET:
+                               rc = exec_stmt_set(estate, (PLpgSQL_stmt_set *) stmt);
+                               break;
 
-               case PLPGSQL_STMT_ROLLBACK:
-                       rc = exec_stmt_rollback(estate, (PLpgSQL_stmt_rollback *) 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 */
+               }
 
-               case PLPGSQL_STMT_SET:
-                       rc = exec_stmt_set(estate, (PLpgSQL_stmt_set *) stmt);
-                       break;
+               /* 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);
 
-               default:
+               if (rc != PLPGSQL_RC_OK)
+               {
                        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;
 }