Hello We talked about enhancing a plpgsql plugin API to support more active plugins.
I wrote a prototype based on function plpgsql_register_plugin instead rendezvous variable. There are two basic questions: a) will we support rendezvous variable still? b) will we support same API still - a reference on plugin_info in exec state is a issue - described in patch. without a) a d b) we will break a current plugins little bit more than is usual - not terrible hard to fix it. But without a) and b) a implementation can be significantly cleaner. Comments, notes? Regards Pavel
commit 406ee9d32dbb09385ec38bb6d89e8531cac1cd5f Author: Pavel Stehule <pavel.steh...@gooddata.com> Date: Thu Jan 9 23:32:30 2014 +0100 initial diff --git a/src/pl/plpgsql/src/Makefile b/src/pl/plpgsql/src/Makefile index 852b0c7..37d17a8 100644 --- a/src/pl/plpgsql/src/Makefile +++ b/src/pl/plpgsql/src/Makefile @@ -19,7 +19,7 @@ rpath = OBJS = pl_gram.o pl_handler.o pl_comp.o pl_exec.o pl_funcs.o pl_scanner.o -DATA = plpgsql.control plpgsql--1.0.sql plpgsql--unpackaged--1.0.sql +DATA = plpgsql.control plpgsql--1.1.sql plpgsql--1.0--1.1.sql plpgsql--unpackaged--1.0.sql all: all-lib diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 3749fac..fc7158e 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -86,6 +86,21 @@ typedef struct SimpleEcontextStackEntry static EState *shared_simple_eval_estate = NULL; static SimpleEcontextStackEntry *simple_econtext_stack = NULL; +/* + * List of pointers and info of registered plugins. + */ +typedef struct PluginPtrEntry +{ + PLpgSQL_plugin *plugin_ptr; + void *plugin_info; /* reserved for use by optional plugin */ + struct PluginPtrEntry *next; +} PluginPtrEntry; + +/* + * Allocated in TopMemoryContext + */ +static PluginPtrEntry *plugins = NULL; + /************************************************************ * Local function forward declarations ************************************************************/ @@ -236,6 +251,11 @@ static char *format_expr_params(PLpgSQL_execstate *estate, static char *format_preparedparamsdata(PLpgSQL_execstate *estate, const PreparedParamsData *ppd); +bool multi_plugin_func_setup = false; +bool multi_plugin_func_beg = false; +bool multi_plugin_func_end = false; +bool multi_plugin_stmt_beg = false; +bool multi_plugin_stmt_end = false; /* ---------- * plpgsql_exec_function Called by the call handler for @@ -336,6 +356,39 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo, if (*plugin_ptr && (*plugin_ptr)->func_beg) ((*plugin_ptr)->func_beg) (&estate, func); + if (multi_plugin_func_beg) + { + PluginPtrEntry *plugin_entry; + + Assert(plugins != NULL); + + for (plugin_entry = plugins; plugin_entry != NULL; + plugin_entry = plugin_entry->next) + { + PLpgSQL_plugin *plugin_ptr = plugin_entry->plugin_ptr; + + if (plugin_entry->plugin_ptr->func_beg) + { + void *plugin_info = estate.plugin_info; + + /* save a plugin_info related single only plpgsql plugin */ + PG_TRY(); + { + estate.plugin_info = plugin_entry->plugin_info; + (plugin_ptr->func_beg) (&estate, func); + plugin_entry->plugin_info = estate.plugin_info; + estate.plugin_info = plugin_info; + } + PG_CATCH(); + { + estate.plugin_info = plugin_info; + PG_RE_THROW(); + } + PG_END_TRY(); + } + } + } + /* * Now call the toplevel block of statements */ @@ -484,6 +537,39 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo, if (*plugin_ptr && (*plugin_ptr)->func_end) ((*plugin_ptr)->func_end) (&estate, func); + if (multi_plugin_func_end) + { + PluginPtrEntry *plugin_entry; + + Assert(plugins != NULL); + + for (plugin_entry = plugins; plugin_entry != NULL; + plugin_entry = plugin_entry->next) + { + PLpgSQL_plugin *plugin_ptr = plugin_entry->plugin_ptr; + + if (plugin_entry->plugin_ptr->func_end) + { + void *plugin_info = estate.plugin_info; + + /* save a plugin_info related single only plpgsql plugin */ + PG_TRY(); + { + estate.plugin_info = plugin_entry->plugin_info; + (plugin_ptr->func_end) (&estate, func); + plugin_entry->plugin_info = estate.plugin_info; + estate.plugin_info = plugin_info; + } + PG_CATCH(); + { + estate.plugin_info = plugin_info; + PG_RE_THROW(); + } + PG_END_TRY(); + } + } + } + /* Clean up any leftover temporary memory */ plpgsql_destroy_econtext(&estate); exec_eval_cleanup(&estate); @@ -1393,6 +1479,39 @@ exec_stmt(PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt) if (*plugin_ptr && (*plugin_ptr)->stmt_beg) ((*plugin_ptr)->stmt_beg) (estate, stmt); + if (multi_plugin_stmt_beg) + { + PluginPtrEntry *plugin_entry; + + Assert(plugins != NULL); + + for (plugin_entry = plugins; plugin_entry != NULL; + plugin_entry = plugin_entry->next) + { + PLpgSQL_plugin *plugin_ptr = plugin_entry->plugin_ptr; + + if (plugin_entry->plugin_ptr->stmt_beg) + { + void *plugin_info = estate->plugin_info; + + /* save a plugin_info related single only plpgsql plugin */ + PG_TRY(); + { + estate->plugin_info = plugin_entry->plugin_info; + (plugin_ptr->stmt_beg) (estate, stmt); + plugin_entry->plugin_info = estate->plugin_info; + estate->plugin_info = plugin_info; + } + PG_CATCH(); + { + estate->plugin_info = plugin_info; + PG_RE_THROW(); + } + PG_END_TRY(); + } + } + } + CHECK_FOR_INTERRUPTS(); switch ((enum PLpgSQL_stmt_types) stmt->cmd_type) @@ -1498,6 +1617,39 @@ exec_stmt(PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt) if (*plugin_ptr && (*plugin_ptr)->stmt_end) ((*plugin_ptr)->stmt_end) (estate, stmt); + if (multi_plugin_stmt_end) + { + PluginPtrEntry *plugin_entry; + + Assert(plugins != NULL); + + for (plugin_entry = plugins; plugin_entry != NULL; + plugin_entry = plugin_entry->next) + { + PLpgSQL_plugin *plugin_ptr = plugin_entry->plugin_ptr; + + if (plugin_entry->plugin_ptr->stmt_end) + { + void *plugin_info = estate->plugin_info; + + /* save a plugin_info related single only plpgsql plugin */ + PG_TRY(); + { + estate->plugin_info = plugin_entry->plugin_info; + (plugin_ptr->stmt_end) (estate, stmt); + plugin_entry->plugin_info = estate->plugin_info; + estate->plugin_info = plugin_info; + } + PG_CATCH(); + { + estate->plugin_info = plugin_info; + PG_RE_THROW(); + } + PG_END_TRY(); + } + } + } + estate->err_stmt = save_estmt; return rc; @@ -3181,6 +3333,42 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate, if ((*plugin_ptr)->func_setup) ((*plugin_ptr)->func_setup) (estate, func); } + + if (plugins != NULL) + { + PluginPtrEntry *plugin_entry; + + for (plugin_entry = plugins; plugin_entry != NULL; + plugin_entry = plugin_entry->next) + { + PLpgSQL_plugin *plugin_ptr = plugin_entry->plugin_ptr; + + plugin_ptr->error_callback = plpgsql_exec_error_callback; + plugin_ptr->assign_expr = exec_assign_expr; + + if (multi_plugin_func_setup) + { + void *plugin_info = estate->plugin_info; + + Assert(plugin_ptr->func_setup != NULL); + + /* save a plugin_info related single only plpgsql plugin */ + PG_TRY(); + { + estate->plugin_info = NULL; + (plugin_ptr->func_setup) (estate, func); + plugin_entry->plugin_info = plugin_info; + estate->plugin_info = plugin_info; + } + PG_CATCH(); + { + estate->plugin_info = plugin_info; + PG_RE_THROW(); + } + PG_END_TRY(); + } + } + } } /* ---------- @@ -6632,3 +6820,38 @@ format_preparedparamsdata(PLpgSQL_execstate *estate, return paramstr.data; } + +/* + * Register a next plugin info + */ +void +plpgsql_register_plugin_internal(PLpgSQL_plugin *plugin_ptr) +{ + PluginPtrEntry *new_entry; + PluginPtrEntry *last_entry; + MemoryContext old_cxt; + + old_cxt = MemoryContextSwitchTo(TopMemoryContext); + new_entry = palloc0(sizeof(PluginPtrEntry)); + MemoryContextSwitchTo(old_cxt); + + /* search last registered plugin */ + if (plugins != NULL) + { + for (last_entry = plugins; last_entry->next != NULL; last_entry = last_entry->next); + last_entry->next = new_entry; + } + else + plugins = new_entry; + + if (plugin_ptr->func_setup != NULL) + multi_plugin_func_setup = true; + if (plugin_ptr->func_beg != NULL) + multi_plugin_func_beg = true; + if (plugin_ptr->func_end != NULL) + multi_plugin_func_end = true; + if (plugin_ptr->stmt_beg != NULL) + multi_plugin_stmt_beg = true; + if (plugin_ptr->stmt_end != NULL) + multi_plugin_stmt_end = true; +} diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c index f02203a..6f18711 100644 --- a/src/pl/plpgsql/src/pl_handler.c +++ b/src/pl/plpgsql/src/pl_handler.c @@ -384,3 +384,21 @@ plpgsql_validator(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } + +/* ---------- + * + * register any plpgsql plugin + * + * ---------- + */ +PG_FUNCTION_INFO_V1(plpgsql_register_plugin); + +Datum +plpgsql_register_plugin(PG_FUNCTION_ARGS) +{ + PLpgSQL_plugin *plugin_ptr = (PLpgSQL_plugin *) PG_GETARG_POINTER(0); + + plpgsql_register_plugin_internal(plugin_ptr); + + PG_RETURN_VOID(); +} diff --git a/src/pl/plpgsql/src/plpgsql--1.0--1.1.sql b/src/pl/plpgsql/src/plpgsql--1.0--1.1.sql new file mode 100644 index 0000000..c7e9a62 --- /dev/null +++ b/src/pl/plpgsql/src/plpgsql--1.0--1.1.sql @@ -0,0 +1,6 @@ +/* src/pl/plpgsql/src/plpgsql--1.0--1.1.sql */ + +CREATE FUNCTION plpgsql_register_plugin(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; diff --git a/src/pl/plpgsql/src/plpgsql--1.0.sql b/src/pl/plpgsql/src/plpgsql--1.0.sql deleted file mode 100644 index 5eeea56..0000000 --- a/src/pl/plpgsql/src/plpgsql--1.0.sql +++ /dev/null @@ -1,11 +0,0 @@ -/* src/pl/plpgsql/src/plpgsql--1.0.sql */ - -/* - * Currently, all the interesting stuff is done by CREATE LANGUAGE. - * Later we will probably "dumb down" that command and put more of the - * knowledge into this script. - */ - -CREATE PROCEDURAL LANGUAGE plpgsql; - -COMMENT ON PROCEDURAL LANGUAGE plpgsql IS 'PL/pgSQL procedural language'; diff --git a/src/pl/plpgsql/src/plpgsql--1.1.sql b/src/pl/plpgsql/src/plpgsql--1.1.sql new file mode 100644 index 0000000..74aa4b9 --- /dev/null +++ b/src/pl/plpgsql/src/plpgsql--1.1.sql @@ -0,0 +1,16 @@ +/* src/pl/plpgsql/src/plpgsql--1.1.sql */ + +/* + * Currently, all the interesting stuff is done by CREATE LANGUAGE. + * Later we will probably "dumb down" that command and put more of the + * knowledge into this script. + */ + +CREATE PROCEDURAL LANGUAGE plpgsql; + +COMMENT ON PROCEDURAL LANGUAGE plpgsql IS 'PL/pgSQL procedural language'; + +CREATE FUNCTION plpgsql_register_plugin(internal) +RETURNS void +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; diff --git a/src/pl/plpgsql/src/plpgsql.control b/src/pl/plpgsql/src/plpgsql.control index b320227..4c75c93 100644 --- a/src/pl/plpgsql/src/plpgsql.control +++ b/src/pl/plpgsql/src/plpgsql.control @@ -1,6 +1,6 @@ # plpgsql extension comment = 'PL/pgSQL procedural language' -default_version = '1.0' +default_version = '1.1' module_pathname = '$libdir/plpgsql' relocatable = false schema = pg_catalog diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index f3d1283..2d0e6f4 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -941,6 +941,7 @@ extern void _PG_init(void); extern Datum plpgsql_call_handler(PG_FUNCTION_ARGS); extern Datum plpgsql_inline_handler(PG_FUNCTION_ARGS); extern Datum plpgsql_validator(PG_FUNCTION_ARGS); +extern Datum plpgsql_register_plugin(PG_FUNCTION_ARGS); /* ---------- * Functions in pl_exec.c @@ -961,6 +962,7 @@ extern Oid exec_get_datum_type(PLpgSQL_execstate *estate, extern void exec_get_datum_type_info(PLpgSQL_execstate *estate, PLpgSQL_datum *datum, Oid *typeid, int32 *typmod, Oid *collation); +extern void plpgsql_register_plugin_internal(PLpgSQL_plugin *plugin_ptr); /* ---------- * Functions for namespace handling in pl_funcs.c
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers