Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit adbe62d

Browse files
committed
Add PL/Sample to src/test/modules/
PL/Sample is an example template of procedural-language handler. This can be used as a base to implement a custom PL, or as a facility to test APIs dedicated to PLs. Much more could be done in this module, like adding a simple validator, but this is left as future work. The documentation included originally some C code to understand the basics of PL handler implementation, but it was outdated, and not really helpful either if trying to implement a new procedural language, particularly when it came to the integration of a PL installation with CREATE EXTENSION. Author: Mark Wong Reviewed-by: Tom Lane, Michael Paquier Discussion: https://postgr.es/m/20200612172648.GA3327@2ndQuadrant.com
1 parent 6e70443 commit adbe62d

File tree

10 files changed

+290
-56
lines changed

10 files changed

+290
-56
lines changed

doc/src/sgml/plhandler.sgml

+4-56
Original file line numberDiff line numberDiff line change
@@ -96,62 +96,10 @@
9696
</para>
9797

9898
<para>
99-
This is a template for a procedural-language handler written in C:
100-
<programlisting>
101-
#include "postgres.h"
102-
#include "executor/spi.h"
103-
#include "commands/trigger.h"
104-
#include "fmgr.h"
105-
#include "access/heapam.h"
106-
#include "utils/syscache.h"
107-
#include "catalog/pg_proc.h"
108-
#include "catalog/pg_type.h"
109-
110-
PG_MODULE_MAGIC;
111-
112-
PG_FUNCTION_INFO_V1(plsample_call_handler);
113-
114-
Datum
115-
plsample_call_handler(PG_FUNCTION_ARGS)
116-
{
117-
Datum retval;
118-
119-
if (CALLED_AS_TRIGGER(fcinfo))
120-
{
121-
/*
122-
* Called as a trigger function
123-
*/
124-
TriggerData *trigdata = (TriggerData *) fcinfo-&gt;context;
125-
126-
retval = ...
127-
}
128-
else
129-
{
130-
/*
131-
* Called as a function
132-
*/
133-
134-
retval = ...
135-
}
136-
137-
return retval;
138-
}
139-
</programlisting>
140-
Only a few thousand lines of code have to be added instead of the
141-
dots to complete the call handler.
142-
</para>
143-
144-
<para>
145-
After having compiled the handler function into a loadable module
146-
(see <xref linkend="dfunc"/>), the following commands then
147-
register the sample procedural language:
148-
<programlisting>
149-
CREATE FUNCTION plsample_call_handler() RETURNS language_handler
150-
AS '<replaceable>filename</replaceable>'
151-
LANGUAGE C;
152-
CREATE LANGUAGE plsample
153-
HANDLER plsample_call_handler;
154-
</programlisting>
99+
A template for a procedural-language handler written as a C extension is
100+
provided in <literal>src/test/modules/plsample</literal>. This is a
101+
working sample demonstrating one way to create a procedural-language
102+
handler, process parameters, and return a value.
155103
</para>
156104

157105
<para>

src/test/modules/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ SUBDIRS = \
1010
delay_execution \
1111
dummy_index_am \
1212
dummy_seclabel \
13+
plsample \
1314
snapshot_too_old \
1415
test_bloomfilter \
1516
test_ddl_deparse \

src/test/modules/plsample/.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Generated subdirectories
2+
/log/
3+
/results/

src/test/modules/plsample/Makefile

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# src/test/modules/plsample/Makefile
2+
3+
MODULES = plsample
4+
5+
EXTENSION = plsample
6+
DATA = plsample--1.0.sql
7+
PGFILEDESC = "PL/Sample - template for procedural language"
8+
9+
REGRESS = plsample
10+
11+
ifdef USE_PGXS
12+
PG_CONFIG = pg_config
13+
PGXS := $(shell $(PG_CONFIG) --pgxs)
14+
include $(PGXS)
15+
else
16+
subdir = src/test/modules/plsample
17+
top_builddir = ../../../..
18+
include $(top_builddir)/src/Makefile.global
19+
include $(top_srcdir)/contrib/contrib-global.mk
20+
endif

src/test/modules/plsample/README

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
PL/Sample
2+
=========
3+
4+
PL/Sample is an example template of procedural-language handler. It is
5+
a simple implementation, yet demonstrates some of the things that can be done
6+
to build a fully functional procedural-language handler.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
CREATE EXTENSION plsample;
2+
-- Create and test some dummy functions
3+
CREATE FUNCTION plsample_result_text(a1 numeric, a2 text, a3 integer[])
4+
RETURNS TEXT
5+
AS $$
6+
Example of source with text result.
7+
$$ LANGUAGE plsample;
8+
SELECT plsample_result_text(1.23, 'abc', '{4, 5, 6}');
9+
NOTICE: source text of function "plsample_result_text":
10+
Example of source with text result.
11+
12+
NOTICE: argument: 0; name: a1; value: 1.23
13+
NOTICE: argument: 1; name: a2; value: abc
14+
NOTICE: argument: 2; name: a3; value: {4,5,6}
15+
plsample_result_text
16+
---------------------------------------
17+
+
18+
Example of source with text result.+
19+
20+
(1 row)
21+
22+
CREATE FUNCTION plsample_result_void(a1 text[])
23+
RETURNS VOID
24+
AS $$
25+
Example of source with void result.
26+
$$ LANGUAGE plsample;
27+
SELECT plsample_result_void('{foo, bar, hoge}');
28+
NOTICE: source text of function "plsample_result_void":
29+
Example of source with void result.
30+
31+
NOTICE: argument: 0; name: a1; value: {foo,bar,hoge}
32+
plsample_result_void
33+
----------------------
34+
35+
(1 row)
36+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/* src/test/modules/plsample/plsample--1.0.sql */
2+
3+
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
4+
\echo Use "CREATE EXTENSION plsample" to load this file. \quit
5+
6+
CREATE FUNCTION plsample_call_handler() RETURNS language_handler
7+
AS 'MODULE_PATHNAME' LANGUAGE C;
8+
9+
CREATE TRUSTED LANGUAGE plsample
10+
HANDLER plsample_call_handler;
11+
12+
ALTER LANGUAGE plsample OWNER TO @extowner@;
13+
14+
COMMENT ON LANGUAGE plsample IS 'PL/Sample procedural language';

src/test/modules/plsample/plsample.c

+183
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/*-------------------------------------------------------------------------
2+
*
3+
* plsample.c
4+
* Handler for the PL/Sample procedural language
5+
*
6+
* Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
7+
* Portions Copyright (c) 1994, Regents of the University of California
8+
*
9+
*
10+
* IDENTIFICATION
11+
* src/test/modules/plsample/plsample.c
12+
*
13+
*-------------------------------------------------------------------------
14+
*/
15+
16+
#include "postgres.h"
17+
18+
#include "catalog/pg_proc.h"
19+
#include "catalog/pg_type.h"
20+
#include "commands/event_trigger.h"
21+
#include "commands/trigger.h"
22+
#include "funcapi.h"
23+
#include "utils/builtins.h"
24+
#include "utils/lsyscache.h"
25+
#include "utils/syscache.h"
26+
27+
PG_MODULE_MAGIC;
28+
29+
PG_FUNCTION_INFO_V1(plsample_call_handler);
30+
31+
static Datum plsample_func_handler(PG_FUNCTION_ARGS);
32+
33+
/*
34+
* Handle function, procedure, and trigger calls.
35+
*/
36+
Datum
37+
plsample_call_handler(PG_FUNCTION_ARGS)
38+
{
39+
Datum retval = (Datum) 0;
40+
41+
PG_TRY();
42+
{
43+
/*
44+
* Determine if called as function or trigger and call appropriate
45+
* subhandler.
46+
*/
47+
if (CALLED_AS_TRIGGER(fcinfo))
48+
{
49+
/*
50+
* This function has been called as a trigger function, where
51+
* (TriggerData *) fcinfo->context includes the information of the
52+
* context.
53+
*/
54+
}
55+
else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
56+
{
57+
/*
58+
* This function is called as an event trigger function, where
59+
* (EventTriggerData *) fcinfo->context includes the information
60+
* of the context.
61+
*/
62+
}
63+
else
64+
{
65+
/* Regular function handler */
66+
retval = plsample_func_handler(fcinfo);
67+
}
68+
}
69+
PG_FINALLY();
70+
{
71+
}
72+
PG_END_TRY();
73+
74+
return retval;
75+
}
76+
77+
/*
78+
* plsample_func_handler
79+
*
80+
* Function called by the call handler for function execution.
81+
*/
82+
static Datum
83+
plsample_func_handler(PG_FUNCTION_ARGS)
84+
{
85+
HeapTuple pl_tuple;
86+
Datum ret;
87+
char *source;
88+
bool isnull;
89+
FmgrInfo *arg_out_func;
90+
Form_pg_type type_struct;
91+
HeapTuple type_tuple;
92+
Form_pg_proc pl_struct;
93+
volatile MemoryContext proc_cxt = NULL;
94+
Oid *argtypes;
95+
char **argnames;
96+
char *argmodes;
97+
char *proname;
98+
Form_pg_type pg_type_entry;
99+
Oid result_typioparam;
100+
FmgrInfo result_in_func;
101+
int numargs;
102+
103+
/* Fetch the source text of the function. */
104+
pl_tuple = SearchSysCache(PROCOID,
105+
ObjectIdGetDatum(fcinfo->flinfo->fn_oid), 0, 0, 0);
106+
if (!HeapTupleIsValid(pl_tuple))
107+
elog(ERROR, "cache lookup failed for function %u",
108+
fcinfo->flinfo->fn_oid);
109+
110+
/*
111+
* Extract and print the source text of the function. This can be used as
112+
* a base for the function validation and execution.
113+
*/
114+
pl_struct = (Form_pg_proc) GETSTRUCT(pl_tuple);
115+
proname = pstrdup(NameStr(pl_struct->proname));
116+
ret = SysCacheGetAttr(PROCOID, pl_tuple, Anum_pg_proc_prosrc, &isnull);
117+
if (isnull)
118+
elog(ERROR, "could not find source text of function \"%s\"",
119+
proname);
120+
ReleaseSysCache(pl_tuple);
121+
source = DatumGetCString(DirectFunctionCall1(textout, ret));
122+
ereport(NOTICE,
123+
(errmsg("source text of function \"%s\": %s",
124+
proname, source)));
125+
126+
/*
127+
* Allocate a context that will hold all the Postgres data for the
128+
* procedure.
129+
*/
130+
proc_cxt = AllocSetContextCreate(TopMemoryContext,
131+
"PL/Sample function",
132+
ALLOCSET_SMALL_SIZES);
133+
134+
arg_out_func = (FmgrInfo *) palloc0(fcinfo->nargs * sizeof(FmgrInfo));
135+
numargs = get_func_arg_info(pl_tuple, &argtypes, &argnames, &argmodes);
136+
137+
/*
138+
* Iterate through all of the function arguments, printing each input
139+
* value.
140+
*/
141+
for (int i = 0; i < numargs; i++)
142+
{
143+
Oid argtype = pl_struct->proargtypes.values[i];
144+
char *value;
145+
146+
type_tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(argtype));
147+
if (!HeapTupleIsValid(type_tuple))
148+
elog(ERROR, "cache lookup failed for type %u", argtype);
149+
150+
type_struct = (Form_pg_type) GETSTRUCT(type_tuple);
151+
fmgr_info_cxt(type_struct->typoutput, &(arg_out_func[i]), proc_cxt);
152+
ReleaseSysCache(type_tuple);
153+
154+
value = OutputFunctionCall(&arg_out_func[i], fcinfo->args[i].value);
155+
ereport(NOTICE,
156+
(errmsg("argument: %d; name: %s; value: %s",
157+
i, argnames[i], value)));
158+
}
159+
160+
/*
161+
* Get the required information for input conversion of the return value.
162+
*
163+
* If the function uses VOID as result, it is better to return NULL.
164+
* Anyway, let's be honest. This is just a template, so there is not much
165+
* we can do here. This returns NULL except if the result type is text,
166+
* where the result is the source text of the function.
167+
*/
168+
if (pl_struct->prorettype != TEXTOID)
169+
PG_RETURN_NULL();
170+
171+
type_tuple = SearchSysCache1(TYPEOID,
172+
ObjectIdGetDatum(pl_struct->prorettype));
173+
if (!HeapTupleIsValid(type_tuple))
174+
elog(ERROR, "cache lookup failed for type %u", pl_struct->prorettype);
175+
pg_type_entry = (Form_pg_type) GETSTRUCT(type_tuple);
176+
result_typioparam = getTypeIOParam(type_tuple);
177+
178+
fmgr_info_cxt(pg_type_entry->typinput, &result_in_func, proc_cxt);
179+
ReleaseSysCache(type_tuple);
180+
181+
ret = InputFunctionCall(&result_in_func, source, result_typioparam, -1);
182+
PG_RETURN_DATUM(ret);
183+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# plsample extension
2+
comment = 'PL/Sample'
3+
default_version = '1.0'
4+
module_pathname = '$libdir/plsample'
5+
relocatable = false
6+
schema = pg_catalog
7+
superuser = false
8+
trusted = true
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CREATE EXTENSION plsample;
2+
-- Create and test some dummy functions
3+
CREATE FUNCTION plsample_result_text(a1 numeric, a2 text, a3 integer[])
4+
RETURNS TEXT
5+
AS $$
6+
Example of source with text result.
7+
$$ LANGUAGE plsample;
8+
SELECT plsample_result_text(1.23, 'abc', '{4, 5, 6}');
9+
10+
CREATE FUNCTION plsample_result_void(a1 text[])
11+
RETURNS VOID
12+
AS $$
13+
Example of source with void result.
14+
$$ LANGUAGE plsample;
15+
SELECT plsample_result_void('{foo, bar, hoge}');

0 commit comments

Comments
 (0)