PostgreSQL Source Code  git master
plsample.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * plsample.c
4  * Handler for the PL/Sample procedural language
5  *
6  * Portions Copyright (c) 1996-2024, 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 "executor/spi.h"
23 #include "funcapi.h"
24 #include "utils/builtins.h"
25 #include "utils/lsyscache.h"
26 #include "utils/syscache.h"
27 
29 
31 
34 
35 /*
36  * Handle function, procedure, and trigger calls.
37  */
38 Datum
40 {
41  Datum retval = (Datum) 0;
42 
43  /*
44  * Many languages will require cleanup that happens even in the event of
45  * an error. That can happen in the PG_FINALLY block. If none is needed,
46  * this PG_TRY construct can be omitted.
47  */
48  PG_TRY();
49  {
50  /*
51  * Determine if called as function or trigger and call appropriate
52  * subhandler.
53  */
54  if (CALLED_AS_TRIGGER(fcinfo))
55  {
56  /*
57  * This function has been called as a trigger function, where
58  * (TriggerData *) fcinfo->context includes the information of the
59  * context.
60  */
61  retval = PointerGetDatum(plsample_trigger_handler(fcinfo));
62  }
63  else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
64  {
65  /*
66  * This function is called as an event trigger function, where
67  * (EventTriggerData *) fcinfo->context includes the information
68  * of the context.
69  *
70  * TODO: provide an example handler.
71  */
72  }
73  else
74  {
75  /* Regular function handler */
76  retval = plsample_func_handler(fcinfo);
77  }
78  }
79  PG_FINALLY();
80  {
81  }
82  PG_END_TRY();
83 
84  return retval;
85 }
86 
87 /*
88  * plsample_func_handler
89  *
90  * Function called by the call handler for function execution.
91  */
92 static Datum
94 {
95  HeapTuple pl_tuple;
96  Datum ret;
97  char *source;
98  bool isnull;
99  FmgrInfo *arg_out_func;
100  Form_pg_type type_struct;
101  HeapTuple type_tuple;
102  Form_pg_proc pl_struct;
103  volatile MemoryContext proc_cxt = NULL;
104  Oid *argtypes;
105  char **argnames;
106  char *argmodes;
107  char *proname;
108  Form_pg_type pg_type_entry;
109  Oid result_typioparam;
110  Oid prorettype;
111  FmgrInfo result_in_func;
112  int numargs;
113 
114  /* Fetch the function's pg_proc entry. */
115  pl_tuple = SearchSysCache1(PROCOID,
116  ObjectIdGetDatum(fcinfo->flinfo->fn_oid));
117  if (!HeapTupleIsValid(pl_tuple))
118  elog(ERROR, "cache lookup failed for function %u",
119  fcinfo->flinfo->fn_oid);
120 
121  /*
122  * Extract and print the source text of the function. This can be used as
123  * a base for the function validation and execution.
124  */
125  pl_struct = (Form_pg_proc) GETSTRUCT(pl_tuple);
126  proname = pstrdup(NameStr(pl_struct->proname));
127  ret = SysCacheGetAttr(PROCOID, pl_tuple, Anum_pg_proc_prosrc, &isnull);
128  if (isnull)
129  elog(ERROR, "could not find source text of function \"%s\"",
130  proname);
132  ereport(NOTICE,
133  (errmsg("source text of function \"%s\": %s",
134  proname, source)));
135 
136  /*
137  * Allocate a context that will hold all the Postgres data for the
138  * procedure.
139  */
141  "PL/Sample function",
143 
144  arg_out_func = (FmgrInfo *) palloc0(fcinfo->nargs * sizeof(FmgrInfo));
145  numargs = get_func_arg_info(pl_tuple, &argtypes, &argnames, &argmodes);
146 
147  /*
148  * Iterate through all of the function arguments, printing each input
149  * value.
150  */
151  for (int i = 0; i < numargs; i++)
152  {
153  Oid argtype = pl_struct->proargtypes.values[i];
154  char *value;
155 
156  type_tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(argtype));
157  if (!HeapTupleIsValid(type_tuple))
158  elog(ERROR, "cache lookup failed for type %u", argtype);
159 
160  type_struct = (Form_pg_type) GETSTRUCT(type_tuple);
161  fmgr_info_cxt(type_struct->typoutput, &(arg_out_func[i]), proc_cxt);
162  ReleaseSysCache(type_tuple);
163 
164  value = OutputFunctionCall(&arg_out_func[i], fcinfo->args[i].value);
165  ereport(NOTICE,
166  (errmsg("argument: %d; name: %s; value: %s",
167  i, argnames[i], value)));
168  }
169 
170  /* Type of the result */
171  prorettype = pl_struct->prorettype;
172  ReleaseSysCache(pl_tuple);
173 
174  /*
175  * Get the required information for input conversion of the return value.
176  *
177  * If the function uses VOID as result, it is better to return NULL.
178  * Anyway, let's be honest. This is just a template, so there is not much
179  * we can do here. This returns NULL except if the result type is text,
180  * where the result is the source text of the function.
181  */
182  if (prorettype != TEXTOID)
183  PG_RETURN_NULL();
184 
185  type_tuple = SearchSysCache1(TYPEOID,
186  ObjectIdGetDatum(prorettype));
187  if (!HeapTupleIsValid(type_tuple))
188  elog(ERROR, "cache lookup failed for type %u", prorettype);
189  pg_type_entry = (Form_pg_type) GETSTRUCT(type_tuple);
190  result_typioparam = getTypeIOParam(type_tuple);
191 
192  fmgr_info_cxt(pg_type_entry->typinput, &result_in_func, proc_cxt);
193  ReleaseSysCache(type_tuple);
194 
195  ret = InputFunctionCall(&result_in_func, source, result_typioparam, -1);
196  PG_RETURN_DATUM(ret);
197 }
198 
199 /*
200  * plsample_trigger_handler
201  *
202  * Function called by the call handler for trigger execution.
203  */
204 static HeapTuple
206 {
207  TriggerData *trigdata = (TriggerData *) fcinfo->context;
208  char *string;
209  volatile HeapTuple rettup;
210  HeapTuple pl_tuple;
211  Datum ret;
212  char *source;
213  bool isnull;
214  Form_pg_proc pl_struct;
215  char *proname;
217 
218  /* Make sure this is being called from a trigger. */
219  if (!CALLED_AS_TRIGGER(fcinfo))
220  elog(ERROR, "not called by trigger manager");
221 
222  /* Connect to the SPI manager */
223  if (SPI_connect() != SPI_OK_CONNECT)
224  elog(ERROR, "could not connect to SPI manager");
225 
226  rc = SPI_register_trigger_data(trigdata);
227  Assert(rc >= 0);
228 
229  /* Fetch the function's pg_proc entry. */
230  pl_tuple = SearchSysCache1(PROCOID,
231  ObjectIdGetDatum(fcinfo->flinfo->fn_oid));
232  if (!HeapTupleIsValid(pl_tuple))
233  elog(ERROR, "cache lookup failed for function %u",
234  fcinfo->flinfo->fn_oid);
235 
236  /*
237  * Code Retrieval
238  *
239  * Extract and print the source text of the function. This can be used as
240  * a base for the function validation and execution.
241  */
242  pl_struct = (Form_pg_proc) GETSTRUCT(pl_tuple);
243  proname = pstrdup(NameStr(pl_struct->proname));
244  ret = SysCacheGetAttr(PROCOID, pl_tuple, Anum_pg_proc_prosrc, &isnull);
245  if (isnull)
246  elog(ERROR, "could not find source text of function \"%s\"",
247  proname);
249  ereport(NOTICE,
250  (errmsg("source text of function \"%s\": %s",
251  proname, source)));
252 
253  /*
254  * We're done with the pg_proc tuple, so release it. (Note that the
255  * "proname" and "source" strings are now standalone copies.)
256  */
257  ReleaseSysCache(pl_tuple);
258 
259  /*
260  * Code Augmentation
261  *
262  * The source text may be augmented here, such as by wrapping it as the
263  * body of a function in the target language, prefixing a parameter list
264  * with names like TD_name, TD_relid, TD_table_name, TD_table_schema,
265  * TD_event, TD_when, TD_level, TD_NEW, TD_OLD, and args, using whatever
266  * types in the target language are convenient. The augmented text can be
267  * cached in a longer-lived memory context, or, if the target language
268  * uses a compilation step, that can be done here, caching the result of
269  * the compilation.
270  */
271 
272  /*
273  * Code Execution
274  *
275  * Here the function (the possibly-augmented source text, or the result of
276  * compilation if the target language uses such a step) should be
277  * executed, after binding values from the TriggerData struct to the
278  * appropriate parameters.
279  *
280  * In this example we just print a lot of info via ereport.
281  */
282 
283  PG_TRY();
284  {
285  ereport(NOTICE,
286  (errmsg("trigger name: %s", trigdata->tg_trigger->tgname)));
287  string = SPI_getrelname(trigdata->tg_relation);
288  ereport(NOTICE, (errmsg("trigger relation: %s", string)));
289 
290  string = SPI_getnspname(trigdata->tg_relation);
291  ereport(NOTICE, (errmsg("trigger relation schema: %s", string)));
292 
293  /* Example handling of different trigger aspects. */
294 
295  if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
296  {
297  ereport(NOTICE, (errmsg("triggered by INSERT")));
298  rettup = trigdata->tg_trigtuple;
299  }
300  else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
301  {
302  ereport(NOTICE, (errmsg("triggered by DELETE")));
303  rettup = trigdata->tg_trigtuple;
304  }
305  else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
306  {
307  ereport(NOTICE, (errmsg("triggered by UPDATE")));
308  rettup = trigdata->tg_trigtuple;
309  }
310  else if (TRIGGER_FIRED_BY_TRUNCATE(trigdata->tg_event))
311  {
312  ereport(NOTICE, (errmsg("triggered by TRUNCATE")));
313  rettup = trigdata->tg_trigtuple;
314  }
315  else
316  elog(ERROR, "unrecognized event: %u", trigdata->tg_event);
317 
318  if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
319  ereport(NOTICE, (errmsg("triggered BEFORE")));
320  else if (TRIGGER_FIRED_AFTER(trigdata->tg_event))
321  ereport(NOTICE, (errmsg("triggered AFTER")));
322  else if (TRIGGER_FIRED_INSTEAD(trigdata->tg_event))
323  ereport(NOTICE, (errmsg("triggered INSTEAD OF")));
324  else
325  elog(ERROR, "unrecognized when: %u", trigdata->tg_event);
326 
327  if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
328  ereport(NOTICE, (errmsg("triggered per row")));
329  else if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
330  ereport(NOTICE, (errmsg("triggered per statement")));
331  else
332  elog(ERROR, "unrecognized level: %u", trigdata->tg_event);
333 
334  /*
335  * Iterate through all of the trigger arguments, printing each input
336  * value.
337  */
338  for (int i = 0; i < trigdata->tg_trigger->tgnargs; i++)
339  ereport(NOTICE,
340  (errmsg("trigger arg[%i]: %s", i,
341  trigdata->tg_trigger->tgargs[i])));
342  }
343  PG_CATCH();
344  {
345  /* Error cleanup code would go here */
346  PG_RE_THROW();
347  }
348  PG_END_TRY();
349 
350  if (SPI_finish() != SPI_OK_FINISH)
351  elog(ERROR, "SPI_finish() failed");
352 
353  return rettup;
354 }
#define NameStr(name)
Definition: c.h:746
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:182
#define Assert(condition)
Definition: c.h:858
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define PG_RE_THROW()
Definition: elog.h:411
#define PG_TRY(...)
Definition: elog.h:370
#define PG_END_TRY(...)
Definition: elog.h:395
#define ERROR
Definition: elog.h:39
#define PG_CATCH(...)
Definition: elog.h:380
#define elog(elevel,...)
Definition: elog.h:224
#define NOTICE
Definition: elog.h:35
#define PG_FINALLY(...)
Definition: elog.h:387
#define ereport(elevel,...)
Definition: elog.h:149
#define CALLED_AS_EVENT_TRIGGER(fcinfo)
Definition: event_trigger.h:43
Datum InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1530
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:137
char * OutputFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1683
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:642
#define PG_RETURN_NULL()
Definition: fmgr.h:345
#define PG_RETURN_DATUM(x)
Definition: fmgr.h:353
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
int get_func_arg_info(HeapTuple procTup, Oid **p_argtypes, char ***p_argnames, char **p_argmodes)
Definition: funcapi.c:1379
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
static struct @155 value
int i
Definition: isn.c:73
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
Oid getTypeIOParam(HeapTuple typeTuple)
Definition: lsyscache.c:2303
char * pstrdup(const char *in)
Definition: mcxt.c:1695
MemoryContext TopMemoryContext
Definition: mcxt.c:149
void * palloc0(Size size)
Definition: mcxt.c:1346
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:170
FormData_pg_proc * Form_pg_proc
Definition: pg_proc.h:136
NameData proname
Definition: pg_proc.h:35
static rewind_source * source
Definition: pg_rewind.c:89
FormData_pg_type * Form_pg_type
Definition: pg_type.h:261
PG_MODULE_MAGIC
Definition: plsample.c:28
static Datum plsample_func_handler(PG_FUNCTION_ARGS)
Definition: plsample.c:93
PG_FUNCTION_INFO_V1(plsample_call_handler)
Datum plsample_call_handler(PG_FUNCTION_ARGS)
Definition: plsample.c:39
static HeapTuple plsample_trigger_handler(PG_FUNCTION_ARGS)
Definition: plsample.c:205
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
static char * DatumGetCString(Datum X)
Definition: postgres.h:335
uintptr_t Datum
Definition: postgres.h:64
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
unsigned int Oid
Definition: postgres_ext.h:31
char * SPI_getrelname(Relation rel)
Definition: spi.c:1323
int SPI_connect(void)
Definition: spi.c:94
int SPI_finish(void)
Definition: spi.c:182
int SPI_register_trigger_data(TriggerData *tdata)
Definition: spi.c:3344
char * SPI_getnspname(Relation rel)
Definition: spi.c:1329
#define SPI_OK_CONNECT
Definition: spi.h:82
#define SPI_OK_FINISH
Definition: spi.h:83
Definition: fmgr.h:57
Relation tg_relation
Definition: trigger.h:35
TriggerEvent tg_event
Definition: trigger.h:34
Trigger * tg_trigger
Definition: trigger.h:38
HeapTuple tg_trigtuple
Definition: trigger.h:36
char * tgname
Definition: reltrigger.h:27
int16 tgnargs
Definition: reltrigger.h:38
char ** tgargs
Definition: reltrigger.h:41
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:266
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:218
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:479
#define TRIGGER_FIRED_FOR_STATEMENT(event)
Definition: trigger.h:125
#define TRIGGER_FIRED_BY_DELETE(event)
Definition: trigger.h:113
#define TRIGGER_FIRED_BEFORE(event)
Definition: trigger.h:128
#define CALLED_AS_TRIGGER(fcinfo)
Definition: trigger.h:26
#define TRIGGER_FIRED_FOR_ROW(event)
Definition: trigger.h:122
#define TRIGGER_FIRED_AFTER(event)
Definition: trigger.h:131
#define TRIGGER_FIRED_BY_TRUNCATE(event)
Definition: trigger.h:119
#define TRIGGER_FIRED_BY_INSERT(event)
Definition: trigger.h:110
#define TRIGGER_FIRED_BY_UPDATE(event)
Definition: trigger.h:116
#define TRIGGER_FIRED_INSTEAD(event)
Definition: trigger.h:134
Datum textout(PG_FUNCTION_ARGS)
Definition: varlena.c:590