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

Reply via email to