From: Tom Lane Date: Tue, 15 Aug 2006 19:01:17 +0000 (+0000) Subject: Add hooks to allow debugging and performance measurement plugins X-Git-Tag: REL8_2_BETA1~308 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=355865c5a7c76238731fe71071c3cc73427ea570;p=postgresql Add hooks to allow debugging and performance measurement plugins to instrument PL/pgSQL. Korry Douglas --- diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index cf445a8a31..f8213d4a50 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.175 2006/08/14 21:14:41 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.176 2006/08/15 19:01:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -252,6 +252,12 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo) */ exec_set_found(&estate, false); + /* + * Let the instrumentation plugin peek at this function + */ + if (*plugin_ptr && (*plugin_ptr)->func_beg) + ((*plugin_ptr)->func_beg)(&estate, func); + /* * Now call the toplevel block of statements */ @@ -387,6 +393,12 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo) } } + /* + * Let the instrumentation plugin peek at this function + */ + if (*plugin_ptr && (*plugin_ptr)->func_end) + ((*plugin_ptr)->func_end)(&estate, func); + /* Clean up any leftover temporary memory */ FreeExprContext(estate.eval_econtext); estate.eval_econtext = NULL; @@ -580,6 +592,12 @@ plpgsql_exec_trigger(PLpgSQL_function *func, */ exec_set_found(&estate, false); + /* + * Let the instrumentation plugin peek at this function + */ + if (*plugin_ptr && (*plugin_ptr)->func_beg) + ((*plugin_ptr)->func_beg)(&estate, func); + /* * Now call the toplevel block of statements */ @@ -633,6 +651,12 @@ plpgsql_exec_trigger(PLpgSQL_function *func, rettup = SPI_copytuple((HeapTuple) (estate.retval)); } + /* + * Let the instrumentation plugin peek at this function + */ + if (*plugin_ptr && (*plugin_ptr)->func_end) + ((*plugin_ptr)->func_end)(&estate, func); + /* Clean up any leftover temporary memory */ FreeExprContext(estate.eval_econtext); estate.eval_econtext = NULL; @@ -1037,6 +1061,10 @@ exec_stmt(PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt) save_estmt = estate->err_stmt; estate->err_stmt = stmt; + /* Let the plugin know that we are about to execute this statement */ + if (*plugin_ptr && (*plugin_ptr)->stmt_beg) + ((*plugin_ptr)->stmt_beg)(estate, stmt); + CHECK_FOR_INTERRUPTS(); switch (stmt->cmd_type) @@ -1122,6 +1150,10 @@ exec_stmt(PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt) elog(ERROR, "unrecognized cmdtype: %d", stmt->cmd_type); } + /* Let the plugin know that we have finished executing this statement */ + if (*plugin_ptr && (*plugin_ptr)->stmt_end) + ((*plugin_ptr)->stmt_end)(estate, stmt); + estate->err_stmt = save_estmt; return rc; @@ -2123,6 +2155,21 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate, * child of simple_eval_estate. */ estate->eval_econtext = CreateExprContext(simple_eval_estate); + + /* + * Let the plugin see this function before we initialize any + * local PL/pgSQL variables - note that we also give the plugin + * a few function pointers so it can call back into PL/pgSQL + * for doing things like variable assignments and stack traces + */ + if (*plugin_ptr) + { + (*plugin_ptr)->error_callback = plpgsql_exec_error_callback; + (*plugin_ptr)->assign_expr = exec_assign_expr; + + if ((*plugin_ptr)->func_setup) + ((*plugin_ptr)->func_setup)(estate, func); + } } /* ---------- diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c index 22264f5a28..a0f2cb4655 100644 --- a/src/pl/plpgsql/src/pl_handler.c +++ b/src/pl/plpgsql/src/pl_handler.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.30 2006/08/08 19:15:09 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.31 2006/08/15 19:01:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,6 +28,8 @@ extern DLLIMPORT bool check_function_bodies; PG_MODULE_MAGIC; +PLpgSQL_plugin **plugin_ptr = NULL; + /* * _PG_init() - library load-time initialization @@ -46,6 +48,9 @@ _PG_init(void) plpgsql_HashTableInit(); RegisterXactCallback(plpgsql_xact_cb, NULL); + /* Set up a rendezvous point with optional instrumentation plugin */ + plugin_ptr = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin"); + inited = true; } diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index 1d53177f32..206f9b9875 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.79 2006/08/14 21:14:41 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.80 2006/08/15 19:01:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -617,9 +617,57 @@ typedef struct PLpgSQL_function *err_func; /* current func */ PLpgSQL_stmt *err_stmt; /* current stmt */ const char *err_text; /* additional state info */ + void *plugin_info; /* reserved for use by optional plugin */ } PLpgSQL_execstate; +/* + * A PLpgSQL_plugin structure represents an instrumentation plugin. + * To instrument PL/pgSQL, a plugin library must access the rendezvous + * variable "PLpgSQL_plugin" and set it to point to a PLpgSQL_plugin struct. + * Typically the struct could just be static data in the plugin library. + * We expect that a plugin would do this at library load time (_PG_init()). + * It must also be careful to set the rendezvous variable back to NULL + * if it is unloaded (_PG_fini()). + * + * This structure is basically a collection of function pointers --- at + * various interesting points in pl_exec.c, we call these functions + * (if the pointers are non-NULL) to give the plugin a chance to watch + * what we are doing. + * + * func_setup is called when we start a function, before we've initialized + * the local variables defined by the function. + * + * func_beg is called when we start a function, after we've initialized + * the local variables. + * + * func_end is called at the end of a function. + * + * stmt_beg and stmt_end are called before and after (respectively) each + * statement. + * + * Also, immediately before any call to func_setup, PL/pgSQL fills in the + * error_callback and assign_expr fields with pointers to its own + * plpgsql_exec_error_callback and exec_assign_expr functions. This is + * a somewhat ad-hoc expedient to simplify life for debugger plugins. + */ + +typedef struct +{ + /* Function pointers set up by the plugin */ + void (*func_setup) (PLpgSQL_execstate *estate, PLpgSQL_function *func); + void (*func_beg) (PLpgSQL_execstate *estate, PLpgSQL_function *func); + void (*func_end) (PLpgSQL_execstate *estate, PLpgSQL_function *func); + void (*stmt_beg) (PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt); + void (*stmt_end) (PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt); + + /* Function pointers set by PL/pgSQL itself */ + void (*error_callback) (void *arg); + void (*assign_expr) (PLpgSQL_execstate *estate, PLpgSQL_datum *target, + PLpgSQL_expr *expr); +} PLpgSQL_plugin; + + /********************************************************************** * Global variable declarations **********************************************************************/ @@ -641,6 +689,8 @@ extern PLpgSQL_function *plpgsql_curr_compile; extern bool plpgsql_check_syntax; extern MemoryContext compile_tmp_cxt; +extern PLpgSQL_plugin **plugin_ptr; + /********************************************************************** * Function declarations **********************************************************************/