This is an updated version of the PL instrumentation plugin patch that I submitted on July-28. The new version re-implements the plugin loader code to use "rendezvous variables" as suggested by Tom Lane (thanks Tom, very elegant design).
I have not implemented any support for unloading shared libraries. Once we've finalized the design for rendezvous variables, I'll submit a separate documentation patch.
-- Korry
-- Korry Douglas [EMAIL PROTECTED] EnterpriseDB http://www.enterprisedb.com |
Index: src/backend/tcop/postgres.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/tcop/postgres.c,v retrieving revision 1.497 diff -w -c -r1.497 postgres.c *** src/backend/tcop/postgres.c 10 Aug 2006 00:44:01 -0000 1.497 --- src/backend/tcop/postgres.c 11 Aug 2006 15:53:24 -0000 *************** *** 2976,2981 **** --- 2976,2987 ---- BeginReportingGUCOptions(); /* + * Load any shared-libraries indicated by the backend_load_libraries + * GUC variable. + */ + process_backend_libraries(); + + /* * Also set up handler to log session end; we have to wait till now to be * sure Log_disconnections has its final value. */ Index: src/backend/utils/fmgr/dfmgr.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/utils/fmgr/dfmgr.c,v retrieving revision 1.87 diff -w -c -r1.87 dfmgr.c *** src/backend/utils/fmgr/dfmgr.c 8 Aug 2006 19:15:08 -0000 1.87 --- src/backend/utils/fmgr/dfmgr.c 11 Aug 2006 15:53:26 -0000 *************** *** 23,34 **** #endif #include "miscadmin.h" #include "utils/dynamic_loader.h" ! /* signatures for PostgreSQL-specific library init/fini functions */ typedef void (*PG_init_t)(void); typedef void (*PG_fini_t)(void); /* * List of dynamically loaded files (kept in malloc'd memory). */ --- 23,40 ---- #endif #include "miscadmin.h" #include "utils/dynamic_loader.h" ! #include "utils/hsearch.h" /* signatures for PostgreSQL-specific library init/fini functions */ typedef void (*PG_init_t)(void); typedef void (*PG_fini_t)(void); + typedef struct + { + char varName[NAMEDATALEN]; + void *varValue; + } rendezvousHashEntry; + /* * List of dynamically loaded files (kept in malloc'd memory). */ *************** *** 324,329 **** --- 330,380 ---- return pg_dlsym(filehandle, funcname); } + /* + * Find (or create) a rendezvous variable that one dynamically + * loaded library can use to meet up with another. + * + * When you first call this function, a simple variable is + * created with the given name. The value of the variable is + * void pointer (initially set to NULL). The next time you + * call find_rendezvous_variable() with the same name, you + * get the address of the void pointer created by the first + * call. + * + * Dynamically loaded libraries can use rendezvous variables + * to find each other and share information (through the + * void pointer). + */ + void ** + find_rendezvous_variable(const char *varName) + { + static HTAB *rendezvousHash = NULL; + char key[NAMEDATALEN] = {0}; + rendezvousHashEntry *hentry; + bool found; + + /* Create a variable hash if we haven't already done so */ + if (rendezvousHash == NULL) + { + HASHCTL ctl; + + ctl.keysize = NAMEDATALEN; + ctl.entrysize = sizeof(rendezvousHashEntry); + rendezvousHash = hash_create("Rendezvous variable hash", 5, &ctl, HASH_ELEM); + + Assert(rendezvousHash != NULL); + } + + /* Turn the varName into a fixed-size string */ + snprintf(key, NAMEDATALEN, "%s", varName); + + hentry = (rendezvousHashEntry *) hash_search(rendezvousHash, key, HASH_ENTER, &found); + + if (!found) + hentry->varValue = NULL; + + return &hentry->varValue; + } static bool file_exists(const char *name) Index: src/backend/utils/init/miscinit.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/utils/init/miscinit.c,v retrieving revision 1.156 diff -w -c -r1.156 miscinit.c *** src/backend/utils/init/miscinit.c 8 Aug 2006 19:15:08 -0000 1.156 --- src/backend/utils/init/miscinit.c 11 Aug 2006 15:53:30 -0000 *************** *** 1097,1120 **** *------------------------------------------------------------------------- */ ! /* GUC variable: list of library names to be preloaded */ char *preload_libraries_string = NULL; /* ! * process any libraries that should be preloaded at postmaster start */ ! void ! process_preload_libraries(void) { char *rawstring; List *elemlist; ListCell *l; ! if (preload_libraries_string == NULL) return; /* Need a modifiable copy of string */ ! rawstring = pstrdup(preload_libraries_string); /* Parse string into list of identifiers */ if (!SplitIdentifierString(rawstring, ',', &elemlist)) --- 1097,1126 ---- *------------------------------------------------------------------------- */ ! /* ! * GUC variables: list of library names to be preloaded, list of ! * library names to load into each backend ! */ char *preload_libraries_string = NULL; + char *backend_libraries_string = NULL; /* ! * load the shared libraries listed in 'libraries', if something ! * goes wrong, throw an error that indicates a problem loading ! * the libraries specified by 'gucname' */ ! static void ! load_libraries(const char *libraries, const char *gucname) { char *rawstring; List *elemlist; ListCell *l; ! if (libraries == NULL) return; /* Need a modifiable copy of string */ ! rawstring = pstrdup(libraries); /* Parse string into list of identifiers */ if (!SplitIdentifierString(rawstring, ',', &elemlist)) *************** *** 1124,1130 **** list_free(elemlist); ereport(LOG, (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("invalid list syntax for parameter \"preload_libraries\""))); return; } --- 1130,1136 ---- list_free(elemlist); ereport(LOG, (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("invalid list syntax for parameter \"%s\"", gucname))); return; } *************** *** 1137,1146 **** canonicalize_path(filename); (void) load_external_function(filename, NULL, true, NULL); ereport(LOG, ! (errmsg("preloaded library \"%s\"", filename))); pfree(filename); } pfree(rawstring); list_free(elemlist); } --- 1143,1170 ---- canonicalize_path(filename); (void) load_external_function(filename, NULL, true, NULL); ereport(LOG, ! (errmsg("%s \"%s\"", gucname, filename))); pfree(filename); } pfree(rawstring); list_free(elemlist); } + + /* + * process any libraries that should be preloaded at postmaster start + */ + void + process_preload_libraries(void) + { + load_libraries(preload_libraries_string, "preload_libraries"); + } + + /* + * process any libraries that should be preloaded at backend start + */ + void + process_backend_libraries(void) + { + load_libraries(backend_libraries_string, "backend_load_libraries"); + } Index: src/backend/utils/misc/guc.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/utils/misc/guc.c,v retrieving revision 1.333 diff -w -c -r1.333 guc.c *** src/backend/utils/misc/guc.c 29 Jul 2006 03:02:56 -0000 1.333 --- src/backend/utils/misc/guc.c 11 Aug 2006 15:53:41 -0000 *************** *** 1973,1978 **** --- 1973,1989 ---- "", NULL, NULL }, + + { + {"backend_load_libraries", PGC_POSTMASTER, RESOURCES_KERNEL, + gettext_noop("Lists shared libraries to load into each backend."), + NULL, + GUC_LIST_INPUT | GUC_LIST_QUOTE | GUC_SUPERUSER_ONLY + }, + &backend_libraries_string, + "", NULL, NULL + }, + { {"regex_flavor", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS, gettext_noop("Sets the regular expression \"flavor\"."), Index: src/include/fmgr.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/fmgr.h,v retrieving revision 1.45 diff -w -c -r1.45 fmgr.h *** src/include/fmgr.h 31 May 2006 20:58:09 -0000 1.45 --- src/include/fmgr.h 11 Aug 2006 15:53:43 -0000 *************** *** 489,495 **** bool signalNotFound, void **filehandle); extern PGFunction lookup_external_function(void *filehandle, char *funcname); extern void load_file(char *filename); ! /* * !!! OLD INTERFACE !!! --- 489,495 ---- bool signalNotFound, void **filehandle); extern PGFunction lookup_external_function(void *filehandle, char *funcname); extern void load_file(char *filename); ! extern void **find_rendezvous_variable(const char *varName); /* * !!! OLD INTERFACE !!! Index: src/include/miscadmin.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/miscadmin.h,v retrieving revision 1.187 diff -w -c -r1.187 miscadmin.h *** src/include/miscadmin.h 8 Aug 2006 19:15:08 -0000 1.187 --- src/include/miscadmin.h 11 Aug 2006 15:53:45 -0000 *************** *** 308,314 **** /* in utils/init/miscinit.c */ extern bool IgnoreSystemIndexes; extern char *preload_libraries_string; ! extern void SetReindexProcessing(Oid heapOid, Oid indexOid); extern void ResetReindexProcessing(void); extern bool ReindexIsProcessingHeap(Oid heapOid); --- 308,314 ---- /* in utils/init/miscinit.c */ extern bool IgnoreSystemIndexes; extern char *preload_libraries_string; ! extern char *backend_libraries_string; extern void SetReindexProcessing(Oid heapOid, Oid indexOid); extern void ResetReindexProcessing(void); extern bool ReindexIsProcessingHeap(Oid heapOid); *************** *** 320,324 **** --- 320,325 ---- unsigned long id2); extern void ValidatePgVersion(const char *path); extern void process_preload_libraries(void); + extern void process_backend_libraries(void); #endif /* MISCADMIN_H */ Index: src/pl/plpgsql/src/pl_exec.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v retrieving revision 1.174 diff -w -c -r1.174 pl_exec.c *** src/pl/plpgsql/src/pl_exec.c 13 Jul 2006 16:49:20 -0000 1.174 --- src/pl/plpgsql/src/pl_exec.c 11 Aug 2006 15:53:53 -0000 *************** *** 255,260 **** --- 255,266 ---- 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 */ estate.err_text = NULL; *************** *** 389,394 **** --- 395,407 ---- } } + /* + * Tell the (optional) plugin that we've finished executing 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; *************** *** 583,588 **** --- 596,607 ---- 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 */ estate.err_text = NULL; *************** *** 635,640 **** --- 654,666 ---- rettup = SPI_copytuple((HeapTuple) (estate.retval)); } + /* + * Tell the (optional) plugin that we've finished executing 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; *************** *** 1039,1044 **** --- 1065,1074 ---- 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) *************** *** 1130,1135 **** --- 1160,1169 ---- estate->err_stmt = save_estmt; + /* Let the plugin know that we have finished executing this statement */ + if (*plugin_ptr && (*plugin_ptr)->stmt_end) + ((*plugin_ptr)->stmt_end)(estate, stmt); + return rc; } *************** *** 2204,2209 **** --- 2238,2252 ---- * 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 the executor + * for doing things like variable assignments and stack traces + */ + if (*plugin_ptr && (*plugin_ptr)->func_setup) + ((*plugin_ptr)->func_setup)(estate, func, plpgsql_exec_error_callback, exec_assign_expr); } /* ---------- Index: src/pl/plpgsql/src/pl_handler.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/pl/plpgsql/src/pl_handler.c,v retrieving revision 1.30 diff -w -c -r1.30 pl_handler.c *** src/pl/plpgsql/src/pl_handler.c 8 Aug 2006 19:15:09 -0000 1.30 --- src/pl/plpgsql/src/pl_handler.c 11 Aug 2006 15:53:53 -0000 *************** *** 28,33 **** --- 28,34 ---- PG_MODULE_MAGIC; + PLpgSQL_plugin **plugin_ptr = NULL; /* * _PG_init() - library load-time initialization *************** *** 46,51 **** --- 47,57 ---- plpgsql_HashTableInit(); RegisterXactCallback(plpgsql_xact_cb, NULL); + /* + * Try to rendezvous with any plugin that we may have loaded. + */ + plugin_ptr = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin"); + inited = true; } Index: src/pl/plpgsql/src/plpgsql.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v retrieving revision 1.78 diff -w -c -r1.78 plpgsql.h *** src/pl/plpgsql/src/plpgsql.h 8 Aug 2006 19:15:09 -0000 1.78 --- src/pl/plpgsql/src/plpgsql.h 11 Aug 2006 15:53:55 -0000 *************** *** 621,629 **** --- 621,661 ---- 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. + * We keep one of these structures (in pl_handler) and initialize + * it by loading a plugin (if desired). This structure is basically + * a collection of function pointers - at various points in pl_exec.c, + * we call those functions (if they 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 (that is, the PL/pgSQL 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 + */ + + typedef struct + { + void (*func_setup)(PLpgSQL_execstate * estate, PLpgSQL_function * func, void (*error_callback)(void *arg), void (*assign_expr)( PLpgSQL_execstate *estate, PLpgSQL_datum *target, PLpgSQL_expr *expr)); + 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); + } PLpgSQL_plugin; + + /********************************************************************** * Global variable declarations **********************************************************************/ *************** *** 645,650 **** --- 677,684 ---- extern bool plpgsql_check_syntax; extern MemoryContext compile_tmp_cxt; + extern PLpgSQL_plugin ** plugin_ptr; + /********************************************************************** * Function declarations **********************************************************************/
---------------------------(end of broadcast)--------------------------- TIP 4: Have you searched our list archives?
http://archives.postgresql.org