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
This commit is contained in:
parent
ecd9e9f0bc
commit
1f902d499e
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue