PostgreSQL Source Code  git master
plsample.c File Reference
#include "postgres.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/event_trigger.h"
#include "commands/trigger.h"
#include "executor/spi.h"
#include "funcapi.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
Include dependency graph for plsample.c:

Go to the source code of this file.

Functions

 PG_FUNCTION_INFO_V1 (plsample_call_handler)
 
static Datum plsample_func_handler (PG_FUNCTION_ARGS)
 
static HeapTuple plsample_trigger_handler (PG_FUNCTION_ARGS)
 
Datum plsample_call_handler (PG_FUNCTION_ARGS)
 

Variables

 PG_MODULE_MAGIC
 

Function Documentation

◆ PG_FUNCTION_INFO_V1()

PG_FUNCTION_INFO_V1 ( plsample_call_handler  )

◆ plsample_call_handler()

Datum plsample_call_handler ( PG_FUNCTION_ARGS  )

Definition at line 39 of file plsample.c.

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 }
#define PG_TRY(...)
Definition: elog.h:371
#define PG_END_TRY(...)
Definition: elog.h:396
#define PG_FINALLY(...)
Definition: elog.h:388
#define CALLED_AS_EVENT_TRIGGER(fcinfo)
Definition: event_trigger.h:43
static Datum plsample_func_handler(PG_FUNCTION_ARGS)
Definition: plsample.c:93
static HeapTuple plsample_trigger_handler(PG_FUNCTION_ARGS)
Definition: plsample.c:205
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
uintptr_t Datum
Definition: postgres.h:64
#define CALLED_AS_TRIGGER(fcinfo)
Definition: trigger.h:26

References CALLED_AS_EVENT_TRIGGER, CALLED_AS_TRIGGER, PG_END_TRY, PG_FINALLY, PG_TRY, plsample_func_handler(), plsample_trigger_handler(), and PointerGetDatum().

◆ plsample_func_handler()

static Datum plsample_func_handler ( PG_FUNCTION_ARGS  )
static

Definition at line 93 of file plsample.c.

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 }
#define NameStr(name)
Definition: c.h:749
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define NOTICE
Definition: elog.h:35
#define ereport(elevel,...)
Definition: elog.h:149
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:641
#define PG_RETURN_NULL()
Definition: fmgr.h:345
#define PG_RETURN_DATUM(x)
Definition: fmgr.h:353
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 @157 value
int i
Definition: isn.c:73
Oid getTypeIOParam(HeapTuple typeTuple)
Definition: lsyscache.c:2303
char * pstrdup(const char *in)
Definition: mcxt.c:1696
MemoryContext TopMemoryContext
Definition: mcxt.c:149
void * palloc0(Size size)
Definition: mcxt.c:1347
#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
static char * DatumGetCString(Datum X)
Definition: postgres.h:335
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
unsigned int Oid
Definition: postgres_ext.h:31
Definition: fmgr.h:57
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:269
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:221
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:596
Datum textout(PG_FUNCTION_ARGS)
Definition: varlena.c:590

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate, DatumGetCString(), DirectFunctionCall1, elog, ereport, errmsg(), ERROR, fmgr_info_cxt(), get_func_arg_info(), GETSTRUCT, getTypeIOParam(), HeapTupleIsValid, i, InputFunctionCall(), NameStr, NOTICE, ObjectIdGetDatum(), OutputFunctionCall(), palloc0(), PG_RETURN_DATUM, PG_RETURN_NULL, proname, pstrdup(), ReleaseSysCache(), SearchSysCache1(), source, SysCacheGetAttr(), textout(), TopMemoryContext, and value.

Referenced by plsample_call_handler().

◆ plsample_trigger_handler()

static HeapTuple plsample_trigger_handler ( PG_FUNCTION_ARGS  )
static

Definition at line 205 of file plsample.c.

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  SPI_connect();
224 
225  rc = SPI_register_trigger_data(trigdata);
226  Assert(rc >= 0);
227 
228  /* Fetch the function's pg_proc entry. */
229  pl_tuple = SearchSysCache1(PROCOID,
230  ObjectIdGetDatum(fcinfo->flinfo->fn_oid));
231  if (!HeapTupleIsValid(pl_tuple))
232  elog(ERROR, "cache lookup failed for function %u",
233  fcinfo->flinfo->fn_oid);
234 
235  /*
236  * Code Retrieval
237  *
238  * Extract and print the source text of the function. This can be used as
239  * a base for the function validation and execution.
240  */
241  pl_struct = (Form_pg_proc) GETSTRUCT(pl_tuple);
242  proname = pstrdup(NameStr(pl_struct->proname));
243  ret = SysCacheGetAttr(PROCOID, pl_tuple, Anum_pg_proc_prosrc, &isnull);
244  if (isnull)
245  elog(ERROR, "could not find source text of function \"%s\"",
246  proname);
248  ereport(NOTICE,
249  (errmsg("source text of function \"%s\": %s",
250  proname, source)));
251 
252  /*
253  * We're done with the pg_proc tuple, so release it. (Note that the
254  * "proname" and "source" strings are now standalone copies.)
255  */
256  ReleaseSysCache(pl_tuple);
257 
258  /*
259  * Code Augmentation
260  *
261  * The source text may be augmented here, such as by wrapping it as the
262  * body of a function in the target language, prefixing a parameter list
263  * with names like TD_name, TD_relid, TD_table_name, TD_table_schema,
264  * TD_event, TD_when, TD_level, TD_NEW, TD_OLD, and args, using whatever
265  * types in the target language are convenient. The augmented text can be
266  * cached in a longer-lived memory context, or, if the target language
267  * uses a compilation step, that can be done here, caching the result of
268  * the compilation.
269  */
270 
271  /*
272  * Code Execution
273  *
274  * Here the function (the possibly-augmented source text, or the result of
275  * compilation if the target language uses such a step) should be
276  * executed, after binding values from the TriggerData struct to the
277  * appropriate parameters.
278  *
279  * In this example we just print a lot of info via ereport.
280  */
281 
282  PG_TRY();
283  {
284  ereport(NOTICE,
285  (errmsg("trigger name: %s", trigdata->tg_trigger->tgname)));
286  string = SPI_getrelname(trigdata->tg_relation);
287  ereport(NOTICE, (errmsg("trigger relation: %s", string)));
288 
289  string = SPI_getnspname(trigdata->tg_relation);
290  ereport(NOTICE, (errmsg("trigger relation schema: %s", string)));
291 
292  /* Example handling of different trigger aspects. */
293 
294  if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
295  {
296  ereport(NOTICE, (errmsg("triggered by INSERT")));
297  rettup = trigdata->tg_trigtuple;
298  }
299  else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
300  {
301  ereport(NOTICE, (errmsg("triggered by DELETE")));
302  rettup = trigdata->tg_trigtuple;
303  }
304  else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
305  {
306  ereport(NOTICE, (errmsg("triggered by UPDATE")));
307  rettup = trigdata->tg_trigtuple;
308  }
309  else if (TRIGGER_FIRED_BY_TRUNCATE(trigdata->tg_event))
310  {
311  ereport(NOTICE, (errmsg("triggered by TRUNCATE")));
312  rettup = trigdata->tg_trigtuple;
313  }
314  else
315  elog(ERROR, "unrecognized event: %u", trigdata->tg_event);
316 
317  if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
318  ereport(NOTICE, (errmsg("triggered BEFORE")));
319  else if (TRIGGER_FIRED_AFTER(trigdata->tg_event))
320  ereport(NOTICE, (errmsg("triggered AFTER")));
321  else if (TRIGGER_FIRED_INSTEAD(trigdata->tg_event))
322  ereport(NOTICE, (errmsg("triggered INSTEAD OF")));
323  else
324  elog(ERROR, "unrecognized when: %u", trigdata->tg_event);
325 
326  if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
327  ereport(NOTICE, (errmsg("triggered per row")));
328  else if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
329  ereport(NOTICE, (errmsg("triggered per statement")));
330  else
331  elog(ERROR, "unrecognized level: %u", trigdata->tg_event);
332 
333  /*
334  * Iterate through all of the trigger arguments, printing each input
335  * value.
336  */
337  for (int i = 0; i < trigdata->tg_trigger->tgnargs; i++)
338  ereport(NOTICE,
339  (errmsg("trigger arg[%i]: %s", i,
340  trigdata->tg_trigger->tgargs[i])));
341  }
342  PG_CATCH();
343  {
344  /* Error cleanup code would go here */
345  PG_RE_THROW();
346  }
347  PG_END_TRY();
348 
349  if (SPI_finish() != SPI_OK_FINISH)
350  elog(ERROR, "SPI_finish() failed");
351 
352  return rettup;
353 }
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:185
#define Assert(condition)
Definition: c.h:861
#define PG_RE_THROW()
Definition: elog.h:412
#define PG_CATCH(...)
Definition: elog.h:381
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
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:3358
char * SPI_getnspname(Relation rel)
Definition: spi.c:1329
#define SPI_OK_FINISH
Definition: spi.h:83
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
#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 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

References Assert, CALLED_AS_TRIGGER, DatumGetCString(), DirectFunctionCall1, elog, ereport, errmsg(), ERROR, GETSTRUCT, HeapTupleIsValid, i, if(), NameStr, NOTICE, ObjectIdGetDatum(), PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, PG_USED_FOR_ASSERTS_ONLY, proname, pstrdup(), ReleaseSysCache(), SearchSysCache1(), source, SPI_connect(), SPI_finish(), SPI_getnspname(), SPI_getrelname(), SPI_OK_FINISH, SPI_register_trigger_data(), SysCacheGetAttr(), textout(), TriggerData::tg_event, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, Trigger::tgargs, Trigger::tgname, Trigger::tgnargs, TRIGGER_FIRED_AFTER, TRIGGER_FIRED_BEFORE, TRIGGER_FIRED_BY_DELETE, TRIGGER_FIRED_BY_INSERT, TRIGGER_FIRED_BY_TRUNCATE, TRIGGER_FIRED_BY_UPDATE, TRIGGER_FIRED_FOR_ROW, TRIGGER_FIRED_FOR_STATEMENT, and TRIGGER_FIRED_INSTEAD.

Referenced by plsample_call_handler().

Variable Documentation

◆ PG_MODULE_MAGIC

PG_MODULE_MAGIC

Definition at line 28 of file plsample.c.