Hi
Ășt 25. 4. 2023 v 10:27 odesĂlatel Pavel Stehule <pavel.steh...@gmail.com> napsal: > Hi > > When I implemented profiler and coverage check to plpgsql_check I had to > write a lot of hard maintaining code related to corect finishing some > operations (counter incrementing) usually executed by stmt_end and func_end > hooks. It is based on the fmgr hook and its own statement call stack. Can > be nice if I can throw this code and use some nice buildin API. > > Can we enhance dbg API with two hooks stmt_end_err func_end_err ? > > These hooks can be called from exception handlers before re raising. > > Or we can define new hooks like executor hooks - stmt_exec and func_exec. > In custom hooks the exception can be catched. > > What do you think about this proposal? > > I did quick and ugly benchmark on worst case CREATE OR REPLACE FUNCTION public.speedtest(i integer) RETURNS void LANGUAGE plpgsql IMMUTABLE AS $function$ declare c int = 0; begin while c < i loop c := c + 1; end loop; raise notice '%', c; end; $function$ and is possible to write some code (see ugly patch) without any performance impacts if the hooks are not used. When hooks are active, then there is 7% performance lost. It is not nice - but this is the worst case. The impact on real code should be significantly lower Regards Pavel
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 4b76f7699a..dc21abd54d 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -1974,157 +1974,180 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block) } -/* ---------- - * exec_stmts Iterate over a list of statements - * as long as their return code is OK - * ---------- - */ static int -exec_stmts(PLpgSQL_execstate *estate, List *stmts) +exec_stmt(PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt) { - PLpgSQL_stmt *save_estmt = estate->err_stmt; - ListCell *s; + int rc; - if (stmts == NIL) + switch (stmt->cmd_type) { - /* - * Ensure we do a CHECK_FOR_INTERRUPTS() even though there is no - * statement. This prevents hangup in a tight loop if, for instance, - * there is a LOOP construct with an empty body. - */ - CHECK_FOR_INTERRUPTS(); - return PLPGSQL_RC_OK; - } + case PLPGSQL_STMT_BLOCK: + rc = exec_stmt_block(estate, (PLpgSQL_stmt_block *) stmt); + break; - foreach(s, stmts) - { - PLpgSQL_stmt *stmt = (PLpgSQL_stmt *) lfirst(s); - int rc; + case PLPGSQL_STMT_ASSIGN: + rc = exec_stmt_assign(estate, (PLpgSQL_stmt_assign *) stmt); + break; - estate->err_stmt = stmt; + case PLPGSQL_STMT_PERFORM: + rc = exec_stmt_perform(estate, (PLpgSQL_stmt_perform *) stmt); + break; - /* 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); + case PLPGSQL_STMT_CALL: + rc = exec_stmt_call(estate, (PLpgSQL_stmt_call *) stmt); + break; - CHECK_FOR_INTERRUPTS(); + case PLPGSQL_STMT_GETDIAG: + rc = exec_stmt_getdiag(estate, (PLpgSQL_stmt_getdiag *) stmt); + break; - switch (stmt->cmd_type) - { - case PLPGSQL_STMT_BLOCK: - rc = exec_stmt_block(estate, (PLpgSQL_stmt_block *) stmt); - break; + case PLPGSQL_STMT_IF: + rc = exec_stmt_if(estate, (PLpgSQL_stmt_if *) stmt); + break; - case PLPGSQL_STMT_ASSIGN: - rc = exec_stmt_assign(estate, (PLpgSQL_stmt_assign *) stmt); - break; + case PLPGSQL_STMT_CASE: + rc = exec_stmt_case(estate, (PLpgSQL_stmt_case *) stmt); + break; - case PLPGSQL_STMT_PERFORM: - rc = exec_stmt_perform(estate, (PLpgSQL_stmt_perform *) stmt); - break; + case PLPGSQL_STMT_LOOP: + rc = exec_stmt_loop(estate, (PLpgSQL_stmt_loop *) stmt); + break; - case PLPGSQL_STMT_CALL: - rc = exec_stmt_call(estate, (PLpgSQL_stmt_call *) stmt); - break; + case PLPGSQL_STMT_WHILE: + rc = exec_stmt_while(estate, (PLpgSQL_stmt_while *) stmt); + break; - case PLPGSQL_STMT_GETDIAG: - rc = exec_stmt_getdiag(estate, (PLpgSQL_stmt_getdiag *) stmt); - break; + case PLPGSQL_STMT_FORI: + rc = exec_stmt_fori(estate, (PLpgSQL_stmt_fori *) stmt); + break; - case PLPGSQL_STMT_IF: - rc = exec_stmt_if(estate, (PLpgSQL_stmt_if *) stmt); - break; + case PLPGSQL_STMT_FORS: + rc = exec_stmt_fors(estate, (PLpgSQL_stmt_fors *) stmt); + break; - case PLPGSQL_STMT_CASE: - rc = exec_stmt_case(estate, (PLpgSQL_stmt_case *) stmt); - break; + case PLPGSQL_STMT_FORC: + rc = exec_stmt_forc(estate, (PLpgSQL_stmt_forc *) stmt); + break; - case PLPGSQL_STMT_LOOP: - rc = exec_stmt_loop(estate, (PLpgSQL_stmt_loop *) stmt); - break; + case PLPGSQL_STMT_FOREACH_A: + rc = exec_stmt_foreach_a(estate, (PLpgSQL_stmt_foreach_a *) stmt); + break; - case PLPGSQL_STMT_WHILE: - rc = exec_stmt_while(estate, (PLpgSQL_stmt_while *) stmt); - break; + case PLPGSQL_STMT_EXIT: + rc = exec_stmt_exit(estate, (PLpgSQL_stmt_exit *) stmt); + break; - case PLPGSQL_STMT_FORI: - rc = exec_stmt_fori(estate, (PLpgSQL_stmt_fori *) stmt); - break; + case PLPGSQL_STMT_RETURN: + rc = exec_stmt_return(estate, (PLpgSQL_stmt_return *) stmt); + break; - case PLPGSQL_STMT_FORS: - rc = exec_stmt_fors(estate, (PLpgSQL_stmt_fors *) stmt); - break; + case PLPGSQL_STMT_RETURN_NEXT: + rc = exec_stmt_return_next(estate, (PLpgSQL_stmt_return_next *) stmt); + break; - case PLPGSQL_STMT_FORC: - rc = exec_stmt_forc(estate, (PLpgSQL_stmt_forc *) stmt); - break; + case PLPGSQL_STMT_RETURN_QUERY: + rc = exec_stmt_return_query(estate, (PLpgSQL_stmt_return_query *) stmt); + break; - case PLPGSQL_STMT_FOREACH_A: - rc = exec_stmt_foreach_a(estate, (PLpgSQL_stmt_foreach_a *) stmt); - break; + case PLPGSQL_STMT_RAISE: + rc = exec_stmt_raise(estate, (PLpgSQL_stmt_raise *) stmt); + break; - case PLPGSQL_STMT_EXIT: - rc = exec_stmt_exit(estate, (PLpgSQL_stmt_exit *) stmt); - break; + case PLPGSQL_STMT_ASSERT: + rc = exec_stmt_assert(estate, (PLpgSQL_stmt_assert *) stmt); + break; - case PLPGSQL_STMT_RETURN: - rc = exec_stmt_return(estate, (PLpgSQL_stmt_return *) stmt); - break; + case PLPGSQL_STMT_EXECSQL: + rc = exec_stmt_execsql(estate, (PLpgSQL_stmt_execsql *) stmt); + break; - case PLPGSQL_STMT_RETURN_NEXT: - rc = exec_stmt_return_next(estate, (PLpgSQL_stmt_return_next *) stmt); - break; + case PLPGSQL_STMT_DYNEXECUTE: + rc = exec_stmt_dynexecute(estate, (PLpgSQL_stmt_dynexecute *) stmt); + break; - case PLPGSQL_STMT_RETURN_QUERY: - rc = exec_stmt_return_query(estate, (PLpgSQL_stmt_return_query *) stmt); - break; + case PLPGSQL_STMT_DYNFORS: + rc = exec_stmt_dynfors(estate, (PLpgSQL_stmt_dynfors *) stmt); + break; - case PLPGSQL_STMT_RAISE: - rc = exec_stmt_raise(estate, (PLpgSQL_stmt_raise *) stmt); - break; + case PLPGSQL_STMT_OPEN: + rc = exec_stmt_open(estate, (PLpgSQL_stmt_open *) stmt); + break; - case PLPGSQL_STMT_ASSERT: - rc = exec_stmt_assert(estate, (PLpgSQL_stmt_assert *) stmt); - break; + case PLPGSQL_STMT_FETCH: + rc = exec_stmt_fetch(estate, (PLpgSQL_stmt_fetch *) stmt); + break; - case PLPGSQL_STMT_EXECSQL: - rc = exec_stmt_execsql(estate, (PLpgSQL_stmt_execsql *) stmt); - break; + case PLPGSQL_STMT_CLOSE: + rc = exec_stmt_close(estate, (PLpgSQL_stmt_close *) stmt); + break; - case PLPGSQL_STMT_DYNEXECUTE: - rc = exec_stmt_dynexecute(estate, (PLpgSQL_stmt_dynexecute *) stmt); - break; + case PLPGSQL_STMT_COMMIT: + rc = exec_stmt_commit(estate, (PLpgSQL_stmt_commit *) stmt); + break; - case PLPGSQL_STMT_DYNFORS: - rc = exec_stmt_dynfors(estate, (PLpgSQL_stmt_dynfors *) stmt); - break; + case PLPGSQL_STMT_ROLLBACK: + rc = exec_stmt_rollback(estate, (PLpgSQL_stmt_rollback *) stmt); + break; - case PLPGSQL_STMT_OPEN: - rc = exec_stmt_open(estate, (PLpgSQL_stmt_open *) stmt); - break; + default: + elog(ERROR, "unrecognized cmd_type: %d", stmt->cmd_type); + rc = -1; /* keep compiler quiet */ + } - case PLPGSQL_STMT_FETCH: - rc = exec_stmt_fetch(estate, (PLpgSQL_stmt_fetch *) stmt); - break; + return rc; +} - case PLPGSQL_STMT_CLOSE: - rc = exec_stmt_close(estate, (PLpgSQL_stmt_close *) stmt); - break; +/* ---------- + * exec_stmts Iterate over a list of statements + * as long as their return code is OK + * ---------- + */ +static int +exec_stmts(PLpgSQL_execstate *estate, List *stmts) +{ + ListCell *s; + PLpgSQL_stmt *save_estmt = estate->err_stmt; - case PLPGSQL_STMT_COMMIT: - rc = exec_stmt_commit(estate, (PLpgSQL_stmt_commit *) stmt); - break; + if (stmts == NIL) + { + /* + * Ensure we do a CHECK_FOR_INTERRUPTS() even though there is no + * statement. This prevents hangup in a tight loop if, for instance, + * there is a LOOP construct with an empty body. + */ + CHECK_FOR_INTERRUPTS(); + return PLPGSQL_RC_OK; + } - case PLPGSQL_STMT_ROLLBACK: - rc = exec_stmt_rollback(estate, (PLpgSQL_stmt_rollback *) stmt); - break; + foreach(s, stmts) + { + PLpgSQL_stmt *stmt = (PLpgSQL_stmt *) lfirst(s); + int rc; - 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 */ + estate->err_stmt = stmt; + save_estmt = estate->err_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(); + + if (*plpgsql_plugin_ptr) + { + PG_TRY(); + { + rc = exec_stmt(estate, stmt); + } + PG_CATCH(); + { + PG_RE_THROW(); + } + PG_END_TRY(); } + else + rc = exec_stmt(estate, stmt); /* Let the plugin know that we have finished executing this statement */ if (*plpgsql_plugin_ptr && (*plpgsql_plugin_ptr)->stmt_end)