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
