Обсуждение: proposal: extensible plpgsql executor - related to assertions
Hello
I am thinking how to implement a assertions and tracking to plpgsql. This area is not strict and clear - everybody would little bit different functionality - and implementation can be different if someone use a psql as main client or some GUI. So I am sceptical to implement assertion directly to plpgsql. Now I finished plpgsql_check_function as extension and I had to use a plpgsql plugin API again.
This API is good enough to enable extensible plpgsql executor. It can be implemened via stmt_beg call, although a special hook can be better.
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;
This API is good enough to enable extensible plpgsql executor. It can be implemened via stmt_beg call, although a special hook can be better.
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;
What is missing is a possibility to invoke some custom functionality (implemented in plugin-extension) from plpgsql code. What example, I can try to print function parameters, local variables at some point, assertions, tracking, ... Usually we would to ignore any action, when plugin is not active. This ensure a minimal slowdown on production when we don´t would to enable assertions, but we don´t would to modify source code.
I propose a enhance the PLpgSQL_plugin struct by a new hook
void (*pragma)(PLpgSQL_execstate *estate, PLpgSQL_pragma *pragma_stmt)
typedef struct
{
{
int cmd_type;
int lineno;
char *refname;
List *exprs;
} pragma_stmt;
PLpgSQL executor should to execute this statement only when pragma hook is defined.
Syntax of PL/pgSQL should be enhanced about statement pragma
Syntax:
pragma pragmaname [ [ ( ] expr, [expr .. ] [ ) ] ]
This syntax should be checked by plpgsql compiler, but and it is important - it is not executed by plpgsql executor ever (minimally in this proposal). So if you don´t would active assertions, then you don´t load a plugin with assertion implementation.
Expressions are executed by plugin, and it is on plugin responsibility.
So with this functionality we can implement tracking, variable dumping, assertions
pragma dump_local_variables;
pragma dump_parameters;
pragma assert(a = 10, ´variable a is not 10´);
pragma warning(a > 100, ´a > 100´);
pragma notice a > 100, format('a = "%s"', a);
...
This proposal should not break any current plpgsql code and it is generic - require only minimal changes in plpgsql runtime. Any current plpgsql plugins should be workable after compilation (but recompilation is required for any new version).
Pragma can be used for other plpgsql plugin as other way how to push a some complex (or simple) parameters to plpgsql related extensions
pragma disable_plpgsql_check;
Opinions, ideas?
Regards
Pavel
Pavel Stehule <pavel.stehule@gmail.com> writes: > I propose a enhance the PLpgSQL_plugin struct by a new hook > void (*pragma)(PLpgSQL_execstate *estate, PLpgSQL_pragma > *pragma_stmt) I repeat what I said a couple of days ago: it's a very bad idea to be enabling more plpgsql plugins as long as the infrastructure can only support one. We need to fix that *first* before we go merrily designing more. I don't like the notion of a pragma statement in this form anyway, because you've essentially made it an executable statement; usually pragmas are compile-time things. It appears to me that you've basically commandeered the word "pragma" for "SET affecting a plugin's variable". If we're inventing new callbacks for plugins, why not one that will extend an existing statement having the right kind of semantics? Or actually, why would it not be better for the plugin to be using a custom GUC for its variable? There's a large amount of infrastructure that custom GUCs can take advantage of, which you'd otherwise have to reinvent piece by piece. regards, tom lane
2014/1/4 Tom Lane <tgl@sss.pgh.pa.us>
Pavel Stehule <pavel.stehule@gmail.com> writes:I repeat what I said a couple of days ago: it's a very bad idea to be
> I propose a enhance the PLpgSQL_plugin struct by a new hook
> void (*pragma)(PLpgSQL_execstate *estate, PLpgSQL_pragma
> *pragma_stmt)
enabling more plpgsql plugins as long as the infrastructure can only
support one. We need to fix that *first* before we go merrily designing
more.
It means a some additional mechanism to find_rendezvous_variable.
What about two new rendezvous variables? One for publishing a PLpgSQL internal API, and second a list of plpgsql_plugin structures? It would be very nice, if we can better access to other plpgsql public functions. A implementation in plpgsql_lint and plpgsql_check is working, but I agree so it is ugly designed (with some disadvantages) - and any change can be better. I minimize these bad references to plpgsq - but plpgsql requires "plpgsql_compile" and "plpgsql_parser_setup" still.
Other possibility is new V1 function for plugin registration.
I don't like the notion of a pragma statement in this form anyway,
because you've essentially made it an executable statement; usually
pragmas are compile-time things. It appears to me that you've basically
commandeered the word "pragma" for "SET affecting a plugin's variable".
It should not be named pragma - I have not better name now. It should not be used as plugin's variable primary.
It should to invoke a external routine - with or without additional parameters. When I would to support tracking, then user should to explicitly set point, where tracking is defined - same is with assertions.
If we're inventing new callbacks for plugins, why not one that will extend
an existing statement having the right kind of semantics?
yes, it is possible - I can to image some like PERFORM assert(exprlist)
and inside callback, we can check a expression and when we find a expected pattern, we can change a semantic. I plan to use this workaround for first plpgsql dumper and tracking extension. But it can have some performance (probably minimal) impact - and it is difficult to implement a mode when this functionality is disabled without any performance impact. So special statement can simplify life to plugin' developers.
Or actually,
why would it not be better for the plugin to be using a custom GUC for
its variable? There's a large amount of infrastructure that custom GUCs
can take advantage of, which you'd otherwise have to reinvent piece
by piece.
GUC doesn't help me with marking some position in source code important for plugin.
Regards
Pavel
regards, tom lane