On Sat, Jun 13, 2020 at 01:19:17PM +0900, Michael Paquier wrote:
> On Fri, Jun 12, 2020 at 10:13:41PM -0400, Tom Lane wrote:
> > On second thought, contrib/ is not quite the right place, because we
> > typically expect modules there to actually get installed, meaning they
> > have to have at least some end-user usefulness.  The right place for
> > a toy PL handler is probably src/test/modules/; compare for example
> > src/test/modules/test_parser/, which is serving quite the same sort
> > of purpose as a skeleton text search parser.
> 
> +1 for src/test/modules/, and if you can provide some low-level API
> coverage through this module, that's even better.

Sounds good to me.  Something more like the attached patch?

Regards,
Mark
-- 
Mark Wong
2ndQuadrant - PostgreSQL Solutions for the Enterprise
https://www.2ndQuadrant.com/
diff --git a/doc/src/sgml/plhandler.sgml b/doc/src/sgml/plhandler.sgml
index e1b0af7a60..7b2c5624c0 100644
--- a/doc/src/sgml/plhandler.sgml
+++ b/doc/src/sgml/plhandler.sgml
@@ -96,62 +96,12 @@
    </para>
 
    <para>
-    This is a template for a procedural-language handler written in C:
-<programlisting>
-#include "postgres.h"
-#include "executor/spi.h"
-#include "commands/trigger.h"
-#include "fmgr.h"
-#include "access/heapam.h"
-#include "utils/syscache.h"
-#include "catalog/pg_proc.h"
-#include "catalog/pg_type.h"
-
-PG_MODULE_MAGIC;
-
-PG_FUNCTION_INFO_V1(plsample_call_handler);
-
-Datum
-plsample_call_handler(PG_FUNCTION_ARGS)
-{
-    Datum          retval;
-
-    if (CALLED_AS_TRIGGER(fcinfo))
-    {
-        /*
-         * Called as a trigger function
-         */
-        TriggerData    *trigdata = (TriggerData *) fcinfo-&gt;context;
-
-        retval = ...
-    }
-    else
-    {
-        /*
-         * Called as a function
-         */
-
-        retval = ...
-    }
-
-    return retval;
-}
-</programlisting>
-    Only a few thousand lines of code have to be added instead of the
-    dots to complete the call handler.
-   </para>
-
-   <para>
-    After having compiled the handler function into a loadable module
-    (see <xref linkend="dfunc"/>), the following commands then
-    register the sample procedural language:
-<programlisting>
-CREATE FUNCTION plsample_call_handler() RETURNS language_handler
-    AS '<replaceable>filename</replaceable>'
-    LANGUAGE C;
-CREATE LANGUAGE plsample
-    HANDLER plsample_call_handler;
-</programlisting>
+    A template for a procedural-language handler written as a C extension is
+    provided in <literal>src/test/modules/plsample</literal>.  This is a
+    working sample demonstrating one way to create a procedural-language
+    handler, process parameters, and return a value.  A few thousand lines of
+    additional code may have to be added to complete a fully functional
+    handler.
    </para>
 
    <para>
diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index 29de73c060..95144d8d7c 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -9,6 +9,7 @@ SUBDIRS = \
 		  commit_ts \
 		  dummy_index_am \
 		  dummy_seclabel \
+		  plsample \
 		  snapshot_too_old \
 		  test_bloomfilter \
 		  test_ddl_deparse \
diff --git a/src/test/modules/plsample/Makefile b/src/test/modules/plsample/Makefile
new file mode 100644
index 0000000000..757b47c785
--- /dev/null
+++ b/src/test/modules/plsample/Makefile
@@ -0,0 +1,20 @@
+# src/test/modules/plsample/Makefile
+
+PGFILEDESC = "PL/Sample - procedural language"
+
+REGRESS = create_pl create_func select_func
+
+EXTENSION = plsample
+EXTVERSION = 0.1
+
+MODULE_big = plsample
+
+OBJS = plsample.o
+
+DATA = plsample.control plsample--0.1.sql
+
+plsample.o: plsample.c
+
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
diff --git a/src/test/modules/plsample/README b/src/test/modules/plsample/README
new file mode 100644
index 0000000000..7ee213700b
--- /dev/null
+++ b/src/test/modules/plsample/README
@@ -0,0 +1,3 @@
+plsample is an example procedural-language handler.  It is a simple functional
+template that demonstrates some of the things that need to be done in order to
+build a fully functional procedural-language handler.
diff --git a/src/test/modules/plsample/expected/create_func.out b/src/test/modules/plsample/expected/create_func.out
new file mode 100644
index 0000000000..df2b915a97
--- /dev/null
+++ b/src/test/modules/plsample/expected/create_func.out
@@ -0,0 +1,5 @@
+CREATE FUNCTION plsample_func(a1 NUMERIC, a2 TEXT, a3 INTEGER[])
+RETURNS TEXT
+AS $$
+  This is function's source text.
+$$ LANGUAGE plsample;
diff --git a/src/test/modules/plsample/expected/create_pl.out b/src/test/modules/plsample/expected/create_pl.out
new file mode 100644
index 0000000000..5365391284
--- /dev/null
+++ b/src/test/modules/plsample/expected/create_pl.out
@@ -0,0 +1,8 @@
+CREATE FUNCTION plsample_call_handler()
+RETURNS language_handler
+AS '$libdir/plsample'
+LANGUAGE C;
+CREATE LANGUAGE plsample
+HANDLER plsample_call_handler;
+COMMENT ON LANGUAGE plsample
+IS 'PL/Sample procedural language';
diff --git a/src/test/modules/plsample/expected/select_func.out b/src/test/modules/plsample/expected/select_func.out
new file mode 100644
index 0000000000..dc396cbc04
--- /dev/null
+++ b/src/test/modules/plsample/expected/select_func.out
@@ -0,0 +1,8 @@
+SELECT plsample_func(1.23, 'abc', '{4, 5, 6}');
+           plsample_func           
+-----------------------------------
+                                  +
+   This is function's source text.+
+ 
+(1 row)
+
diff --git a/src/test/modules/plsample/plsample--0.1.sql b/src/test/modules/plsample/plsample--0.1.sql
new file mode 100644
index 0000000000..b429b83ceb
--- /dev/null
+++ b/src/test/modules/plsample/plsample--0.1.sql
@@ -0,0 +1,9 @@
+CREATE FUNCTION plsample_call_handler()
+RETURNS language_handler
+AS 'MODULE_PATHNAME'
+LANGUAGE C;
+
+CREATE LANGUAGE plsample
+HANDLER plsample_call_handler;
+
+COMMENT ON LANGUAGE plsample IS 'PL/Sample procedural language';
diff --git a/src/test/modules/plsample/plsample.c b/src/test/modules/plsample/plsample.c
new file mode 100644
index 0000000000..15f7d1c55a
--- /dev/null
+++ b/src/test/modules/plsample/plsample.c
@@ -0,0 +1,120 @@
+/*-------------------------------------------------------------------------
+ *
+ * plsample.c
+ *      Handler for the PL/Sample procedural language
+ *
+ * Copyright (c) 2020, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *		src/test/modules/plsample.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include <postgres.h>
+#include <fmgr.h>
+#include <funcapi.h>
+#include <access/htup_details.h>
+#include <catalog/pg_proc.h>
+#include <catalog/pg_type.h>
+#include <utils/memutils.h>
+#include <utils/builtins.h>
+#include <utils/lsyscache.h>
+#include <utils/syscache.h>
+
+MemoryContext TopMemoryContext = NULL;
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(plsample_call_handler);
+
+/*
+ * Handle function, procedure, and trigger calls.
+ */
+Datum
+plsample_call_handler(PG_FUNCTION_ARGS)
+{
+	HeapTuple pl_tuple;
+	Datum ret;
+	char *source;
+	bool isnull;
+
+	int i;
+	FmgrInfo *arg_out_func;
+	Form_pg_type type_struct;
+	HeapTuple type_tuple;
+	Form_pg_proc pl_struct;
+	volatile MemoryContext proc_cxt = NULL;
+	Oid *argtypes;
+	char **argnames;
+	char *argmodes;
+	char *value;
+
+	Form_pg_type pg_type_entry;
+	Oid result_typioparam;
+	FmgrInfo result_in_func;
+
+	/* Fetch the source of the function. */
+
+	pl_tuple = SearchSysCache(PROCOID,
+			ObjectIdGetDatum(fcinfo->flinfo->fn_oid), 0, 0, 0);
+	if (!HeapTupleIsValid(pl_tuple))
+		elog(ERROR, "cache lookup failed for function %u",
+				fcinfo->flinfo->fn_oid);
+	pl_struct = (Form_pg_proc) GETSTRUCT(pl_tuple);
+
+	ret = SysCacheGetAttr(PROCOID, pl_tuple, Anum_pg_proc_prosrc, &isnull);
+	if (isnull)
+		elog(ERROR, "null prosrc");
+	ReleaseSysCache(pl_tuple);
+
+	source = DatumGetCString(DirectFunctionCall1(textout, ret));
+	elog(LOG, "source text:\n%s", source);
+		
+	arg_out_func = (FmgrInfo *) palloc0(fcinfo->nargs * sizeof(FmgrInfo));
+	proc_cxt = AllocSetContextCreate(TopMemoryContext,
+			"PL/Sample function", 0, (1 * 1024), (8 * 1024));
+	get_func_arg_info(pl_tuple, &argtypes, &argnames, &argmodes);
+
+	/* Iterate through all of the function arguments. */
+	elog(LOG, "number of arguments : %d", fcinfo->nargs);
+	for (i = 0; i < fcinfo->nargs; i++)
+	{
+		Oid argtype = pl_struct->proargtypes.values[i];
+		type_tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(argtype));
+		if (!HeapTupleIsValid(type_tuple))
+			elog(ERROR, "cache lookup failed for type %u", argtype);
+
+		type_struct = (Form_pg_type) GETSTRUCT(type_tuple);
+		fmgr_info_cxt(type_struct->typoutput, &(arg_out_func[i]), proc_cxt);
+		ReleaseSysCache(type_tuple);
+
+		value = OutputFunctionCall(&arg_out_func[i], fcinfo->args[i].value);
+
+		elog(LOG, "argument position: %d; name: %s; value: %s", i, argnames[i],
+				value);
+	}
+
+	/* Fetch the return type of the function. */
+
+	type_tuple = SearchSysCache1(TYPEOID,
+			ObjectIdGetDatum(pl_struct->prorettype));
+	if (!HeapTupleIsValid(type_tuple))
+		elog(ERROR, "cache lookup failed for type %u", pl_struct->prorettype);
+
+	pg_type_entry = (Form_pg_type) GETSTRUCT(type_tuple);
+
+	proc_cxt = AllocSetContextCreate(TopMemoryContext, "PL/Sample function",
+			ALLOCSET_SMALL_SIZES);
+
+	result_typioparam = getTypeIOParam(type_tuple);
+
+	fmgr_info_cxt(pg_type_entry->typinput, &result_in_func, proc_cxt);
+	ReleaseSysCache(type_tuple);
+
+	/* Simply return the function source text. */
+	ret = InputFunctionCall(&result_in_func, source, result_typioparam, -1);
+	PG_RETURN_DATUM(ret);
+
+	return 0;
+}
diff --git a/src/test/modules/plsample/plsample.control b/src/test/modules/plsample/plsample.control
new file mode 100644
index 0000000000..dba58e5abf
--- /dev/null
+++ b/src/test/modules/plsample/plsample.control
@@ -0,0 +1,7 @@
+# plsample extension
+comment = 'PL/Sample'
+default_version = '0.1'
+module_pathname = '$libdir/plsample'
+relocatable = false
+schema = pg_catalog
+superuser = false
diff --git a/src/test/modules/plsample/sql/create_func.sql b/src/test/modules/plsample/sql/create_func.sql
new file mode 100644
index 0000000000..df2b915a97
--- /dev/null
+++ b/src/test/modules/plsample/sql/create_func.sql
@@ -0,0 +1,5 @@
+CREATE FUNCTION plsample_func(a1 NUMERIC, a2 TEXT, a3 INTEGER[])
+RETURNS TEXT
+AS $$
+  This is function's source text.
+$$ LANGUAGE plsample;
diff --git a/src/test/modules/plsample/sql/create_pl.sql b/src/test/modules/plsample/sql/create_pl.sql
new file mode 100644
index 0000000000..c3ace0f1aa
--- /dev/null
+++ b/src/test/modules/plsample/sql/create_pl.sql
@@ -0,0 +1,10 @@
+CREATE FUNCTION plsample_call_handler()
+RETURNS language_handler
+AS '$libdir/plsample'
+LANGUAGE C;
+
+CREATE LANGUAGE plsample
+HANDLER plsample_call_handler;
+
+COMMENT ON LANGUAGE plsample
+IS 'PL/Sample procedural language';
diff --git a/src/test/modules/plsample/sql/select_func.sql b/src/test/modules/plsample/sql/select_func.sql
new file mode 100644
index 0000000000..5ded186984
--- /dev/null
+++ b/src/test/modules/plsample/sql/select_func.sql
@@ -0,0 +1 @@
+SELECT plsample_func(1.23, 'abc', '{4, 5, 6}');

Reply via email to