Index: src/backend/utils/fmgr/fmgr.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v
retrieving revision 1.101
diff -w -c -r1.101 fmgr.c
*** src/backend/utils/fmgr/fmgr.c	30 May 2006 21:21:30 -0000	1.101
--- src/backend/utils/fmgr/fmgr.c	28 Jul 2006 13:22:15 -0000
***************
*** 25,30 ****
--- 25,31 ----
  #include "utils/fmgrtab.h"
  #include "utils/lsyscache.h"
  #include "utils/syscache.h"
+ #include "utils/guc.h"
  
  /*
   * Declaration for old-style function pointer type.  This is now used only
***************
*** 69,76 ****
  	const Pg_finfo_record *inforec;		/* address of its info record */
  } CFuncHashTabEntry;
  
! static HTAB *CFuncHash = NULL;
  
  
  static void fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt,
  					   bool ignore_security);
--- 70,87 ----
  	const Pg_finfo_record *inforec;		/* address of its info record */
  } CFuncHashTabEntry;
  
! /*
!  * Hashtable for keeping track of language-handler plugins
!  */
! 
! typedef struct
! {
! 	char	varName[NAMEDATALEN];	/* Name of GUC variable for this handler      */
! 	char  * varValue;				/* A place to store the value of the variable */
! } pluginHashTabEntry;
  
+ static HTAB *CFuncHash = NULL;
+ static HTAB *pluginHash = NULL;
  
  static void fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt,
  					   bool ignore_security);
***************
*** 81,87 ****
  			  PGFunction user_fn, const Pg_finfo_record *inforec);
  static Datum fmgr_oldstyle(PG_FUNCTION_ARGS);
  static Datum fmgr_security_definer(PG_FUNCTION_ARGS);
! 
  
  /*
   * Lookup routines for builtin-function table.	We can search by either Oid
--- 92,100 ----
  			  PGFunction user_fn, const Pg_finfo_record *inforec);
  static Datum fmgr_oldstyle(PG_FUNCTION_ARGS);
  static Datum fmgr_security_definer(PG_FUNCTION_ARGS);
! static char * findPluginName(const char * varName);
! static void createPluginHash(void);
! static const char * assign_plugin(const char *newval, bool doit, GucSource source);
  
  /*
   * Lookup routines for builtin-function table.	We can search by either Oid
***************
*** 2081,2083 ****
--- 2094,2286 ----
  
  	return argtype;
  }
+ 
+ /*--------------------------------------------------------------------------
+  * Support routines for loadable procedural-language instrumentation plugins
+  *--------------------------------------------------------------------------
+  */
+ 
+ /*
+  * Load a plugin for a procedural-language handler (or just about 
+  * anything else).
+  *
+  * A language handler (such as pl_handler.c) can call this function
+  * to load an instrumentation plugin (typically a structure that 
+  * contains a set of function pointers that the language handler 
+  * will invoke at appropriate points during the execution of a PL
+  * function).  
+  *
+  * 'hooks' will typically point to a language-specific structure
+  * that the plugin will populate with function pointers (and any
+  * other data required by the language handler).
+  *
+  * 'varName' is the name of a GUC variable (such as 'plpgsql.plugin')
+  * that determines the name of the plugin's shared-object library.
+  *
+  * For example, the PL/pgSQL language handler calls load_pl_plugin()
+  * like this:
+  *		PLpgSQL_plugin 	hookFunctions;
+  *
+  *		load_pl_plugin(&hookFunctions, "plpgsql.plugin" );
+  *
+  * Note that load_pl_plugin() allocates space (in pluginHash) for 
+  * the GUC variables so callers don't have to deal with that.
+  *
+  * Also note that there is (currently) no corresponding unload_pl_plugin()
+  * function - if you want to change plugins, you have to change the 
+  * value of the GUC variable, and then LOAD 'plpgsql' again.
+  */
+ 
+ void 
+ load_pl_plugin(void * hooks, char * varName)
+ {
+ 	plugin_loader   loader;
+ 	char		  * pluginName;
+ 
+ 	Assert(hooks != NULL);
+ 	Assert(varName != NULL);
+ 
+ 	/* Ensure that our variable name->plugin name hash exists */
+ 	createPluginHash();
+ 
+ 	/* See if we have a GUC setting for the given variable name */
+ 	if ((pluginName = findPluginName(varName)) == NULL )
+ 		return;
+ 
+ 	/* 
+ 	 * If the pluginName contains a ':' we assume that the user 
+ 	 * gave us the name of the plugin loader function (following
+ 	 * the ':') otherwise, we look for a function whose name is
+ 	 * 'load_plugin'.
+ 	 */
+ 
+ 	if (strstr(pluginName, ":") == NULL)
+ 	{
+ 		loader = (plugin_loader)load_external_function(pluginName, "load_plugin", true, NULL);
+ 	}
+ 	else
+ 	{
+ 		/* 
+ 		 * The user gave us a string of the form 'libName:funcName' - pick it apart,
+ 		 * load the given libName, and search for the given funcName within that 
+ 		 * library
+ 		 */
+ 
+ 		char * libName  = pstrdup(pluginName);
+ 		char * sep      = strstr(libName, ":");
+ 		char * funcName = sep+1;
+ 
+ 		*sep = '\0';
+ 
+ 		loader = (plugin_loader)load_external_function(libName, funcName, true, NULL);
+ 
+ 		pfree(libName);
+ 	}
+ 
+ 	/*
+ 	 * Call the loader function that we found (note - load_external_function()
+ 	 * throws an error if it can't find the loader function so its safe to call
+ 	 * *loader without checking for NULL.
+ 	 */
+ 
+ 	Assert(loader != NULL);
+ 
+ 	(*loader)(hooks);
+ }
+ 
+ /*
+  * Creates the hash table that keeps track of which PL plugins we have loaded.
+  * The key to this hash is the name of the GUC variable (such as 'plpgsql.plugin')
+  * and the data for each entry is simply a char pointer that points to the value
+  * of that variable.
+  *
+  * Note: you can call this function as many times as you like, it only does 
+  * interesting work the first time you call it.
+  */
+ 
+ static void
+ createPluginHash(void)
+ {
+ 	if (pluginHash == NULL)
+ 	{
+ 		HASHCTL	ctl;
+ 
+ 		ctl.keysize   = NAMEDATALEN;
+ 		ctl.entrysize = sizeof( pluginHashTabEntry );
+ 
+ 		pluginHash = hash_create( "PL Plugin Cache", 5, &ctl, HASH_ELEM);
+ 
+ 		Assert(pluginHash != NULL);
+ 	}
+ }
+ 
+ /*
+  * findPluginName() creates a new (custom classed) GUC variable named 'varName'
+  * and returns the value (possibly NULL) of that variable.  Presumably, varName
+  * is the name of a PL plugin library.
+  */
+ 
+ static char *
+ findPluginName(const char * varName)
+ {
+ 	char	             key[NAMEDATALEN] = {0};
+ 	pluginHashTabEntry * hentry;
+ 	bool				 found;
+ 
+ 	Assert(pluginHash != NULL);
+ 	
+ 	/* Add the variable to our pluginHash if it's not already there */
+ 	snprintf(key, NAMEDATALEN - 1, "%s", varName);
+ 
+ 	hentry = (pluginHashTabEntry *) hash_search(pluginHash, key, HASH_ENTER, &found);
+ 
+ 	if (!found)
+ 	{
+ 		/*
+ 		 * We didn't find 'varName' in our hash, tell the GUC mechanism about 
+ 		 * this variable.
+ 		 *
+ 		 * NOTE: We must define a USERSET variable, but we really want an SUSET 
+ 		 * variable.  Apparently, you can't really create SUSET custom-classed 
+ 		 * variables.  So, we create a USERSET variable instead and use an assign-
+ 		 * hook to enforce privileges.
+ 		 */
+ 
+ 		DefineCustomStringVariable( varName,
+ 		    "Name of instrumentation plugin to use when procedural-language function is invoked",
+ 			 NULL, &hentry->varValue, PGC_USERSET, assign_plugin, NULL );
+ 	}
+ 
+ 	return hentry->varValue;
+ }
+ 
+ /*
+  * This function is an assign-hook for custom-classed PL plugin 
+  * GUC variables. We want to prevent non-superusers from loading
+  * arbitrary PL plugins so we check privileges here.
+  */
+ 
+ static const char *
+ assign_plugin(const char *newval, bool doit, GucSource source)
+ {
+ 	if (doit)
+ 	{
+ 		/*
+ 		 * If this plugin name is being modified by a client 
+ 		 * application or by the user (i.e. a SET command),
+ 		 * make sure that we have superuser() privileges.
+ 		 */
+ 		if (source > PGC_S_USER)
+ 		{
+ 			if( !superuser())
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 						 errmsg("must be superuser to modify plugin name")));
+ 		}
+ 
+ 		return strdup(newval);
+ 	}
+ 
+ 	return newval;
+ }
+ 
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	28 Jul 2006 13:22:19 -0000
***************
*** 340,345 ****
--- 340,347 ----
  	int		namedatalen;		/* NAMEDATALEN */
  } Pg_magic_struct;
  
+ typedef void (*plugin_loader)(void *);
+ 
  /* The actual data block contents */
  #define PG_MODULE_MAGIC_DATA \
  { \
***************
*** 480,485 ****
--- 482,489 ----
  extern Oid	get_fn_expr_argtype(FmgrInfo *flinfo, int argnum);
  extern Oid	get_call_expr_argtype(fmNodePtr expr, int argnum);
  
+ extern void load_pl_plugin(void * hooks, char * varName);
+ 
  /*
   * Routines in dfmgr.c
   */
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	28 Jul 2006 13:22:22 -0000
***************
*** 255,260 ****
--- 255,266 ----
  	exec_set_found(&estate, false);
  
  	/*
+ 	 * Let the instrumentation plugin peek at this function
+ 	 */
+ 	if (plugin.func_beg)
+ 		plugin.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.func_end)
+ 		plugin.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.func_beg)
+ 		plugin.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.func_end)
+ 		plugin.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.stmt_beg)
+ 		plugin.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.stmt_end)
+ 		plugin.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.func_setup)
+ 		plugin.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.29
diff -w -c -r1.29 pl_handler.c
*** src/pl/plpgsql/src/pl_handler.c	30 May 2006 22:12:16 -0000	1.29
--- src/pl/plpgsql/src/pl_handler.c	28 Jul 2006 13:22:24 -0000
***************
*** 23,28 ****
--- 23,29 ----
  #include "utils/builtins.h"
  #include "utils/lsyscache.h"
  #include "utils/syscache.h"
+ #include "fmgr.h"
  
  extern DLLIMPORT bool check_function_bodies;
  
***************
*** 32,37 ****
--- 33,40 ----
  
  static void plpgsql_init_all(void);
  
+ PLpgSQL_plugin plugin = {0};	/* Hooks for (optional) instrumentation plugin */
+ 
  
  /*
   * plpgsql_init()			- postmaster-startup safe initialization
***************
*** 48,53 ****
--- 51,59 ----
  	plpgsql_HashTableInit();
  	RegisterXactCallback(plpgsql_xact_cb, NULL);
  	plpgsql_firstcall = false;
+ 
+ 	/* Load any plugins identified by the plpgsql.plugin GUC variable */
+ 	load_pl_plugin(&plugin, "plpgsql.plugin");
  }
  
  /*
Index: src/pl/plpgsql/src/plpgsql.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v
retrieving revision 1.77
diff -w -c -r1.77 plpgsql.h
*** src/pl/plpgsql/src/plpgsql.h	11 Jul 2006 17:26:59 -0000	1.77
--- src/pl/plpgsql/src/plpgsql.h	28 Jul 2006 13:22:24 -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;
+ 
  /**********************************************************************
   * Function declarations
   **********************************************************************/
