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

Lines changed: 4 additions & 56 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Generated subdirectories
2+
/log/
3+
/results/

src/test/modules/plsample/Makefile

Lines changed: 20 additions & 0 deletions
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

Lines changed: 6 additions & 0 deletions
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.
Lines changed: 36 additions & 0 deletions
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+
Lines changed: 14 additions & 0 deletions
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

Lines changed: 183 additions & 0 deletions
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+
}
Lines changed: 8 additions & 0 deletions
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
Lines changed: 15 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy