I'm working on a patch that implements the PL/pgSQL instrumentation stuff (i.e. the PL/pgSQL debugger) that I discussed at the Anniversary Summit and I need some opinions (this seems like a good place to look for opinions :-)

A quick review: the PL/pgSQL debugger is designed as an optional "plugin" that loads into the PL/pgSQL interpreter on-demand. You can use the plugin idea to implement other kinds of instrumentation (I demo'ed a tracer and a profiler at the conference, along with a debugger). A plugin architecture greatly reduces the (source code) footprint that would normally be required to implement a full-featured debugger.

A plugin is basically a structure that contains a few function pointers. If those function pointers are NULL, the PL/pgSQL interpreter works exactly the way it does today. If any of those function pointers are non-NULL, the PL/pgSQL interpreter calls the target function (which points to a chunk of code inside of the plugin) and the plugin does whatever it needs to do.

Right now, the plugin structure looks like this:

typedef struct
{
   void (*init)( estate,  func, error_callback, assign_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;

I've truncated the argument list (in this e-mail) for the (*init)() function since it's rather long (error_callback and assign_expr are both function pointers).

When the PL/pgSQL intrepreter loads the plugin, it calls the plugin->init() function. When the PL/pgSQL intrepreter starts running a new function, it calls the plugin->func_beg() function. When the PL/pgSQL intrepreter completes a function, it calls the plugin->func_end() function. When the PL/pgSQL interpreter is about to execute a line of PL/pgSQL code, it calls plugin->stmt_beg() When the PL/pgSQL interpreter has finished executing a line of PL/pgSQL code, it calls plugin->stmt_end()

So here is where I need a few opinions:

1) I think the most straightforward way to load an instrumentation plugin is to define a new custom GUC variable (using the custom_variable_classes mechanism). When the PL/pgSQL call-handler loads, it can check that config. variable (something like plpgsql.plugin = '$libdir/plugin_profiler' or plpgsql.plugin = '$libdir/plugin_debugger') and load the plugin if non-NULL. That seems a little obtuse to me since custom variables don't appear in the prototype postgresql.conf file. Would it be better to add a real GUC variable instead of a custom variable?

2) Given that plpgsql.plugin points to the name of a shared-object file (or DLL or whatever you prefer to call it), we need to find *something* inside of the file. The most obvious choice would be to look for a variable (a structure or structure pointer) with a fixed name. That would mean, for example, that a plugin would define an externally visible PLpgSQL_plugin structure named "plugin_hooks" and the PL/pgSQL interpreter would look for that symbol inside of the plugin. Alternatively, we could look for a function inside of the plugin (something like 'plugin_loader') and then call that function with a pointer to a PLpgSQL_plugin structure. I prefer the function-pointer approach since we already have a reliable mechanism in place for finding a function inside of a shared-object (the same mechanism works for finding a variable instead of a function pointer, but I doubt that that has been tested in all platforms).

3) Any comments on the PLpgSQL_plugin structure? Should it include (as it's first member) a structure version number so we can add to/change the structure as needed?

4) Do we need to support multiple active plugins? Would you ever need to load the debugger at the same time you've loaded the profiler (no)? Would you ever need to load the tracer at the same time you need the debugger (probably not)? If we need to support multiple plugins, should be just introduce a meta-plugin that knows how to handle a list of other plugins? (Messy, but certainly gets the job done without worrying about it right now).

5) I'll also be adding a void pointer to the PLpgSQL_execstate structure (think of a PLpgSQL_execstate as a stack frame). The new pointer is reserved for use by the plugin. It may be handy to add a void pointer to each PLpgSQL_stmt as well - is that acceptable? (That would mean an extra 4-bytes per-line of compiled PL/pgSQL code, even if you don't have a plugin loaded).

Any other comments? Obviously, you'll have a chance to critique the patch when I get it sent in.

Thanks for your help.

         -- Korry



---------------------------(end of broadcast)---------------------------
TIP 4: Have you searched our list archives?

              http://archives.postgresql.org

Reply via email to