LCOV - code coverage report
Current view: top level - src/test/modules/plsample - plsample.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 147 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 5 0
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * plsample.c
       4              :  *        Handler for the PL/Sample procedural language
       5              :  *
       6              :  * Portions Copyright (c) 1996-2026, 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/fmgrprotos.h"
      25              : #include "utils/lsyscache.h"
      26              : #include "utils/syscache.h"
      27              : 
      28            0 : PG_MODULE_MAGIC;
      29              : 
      30            0 : PG_FUNCTION_INFO_V1(plsample_call_handler);
      31              : 
      32              : static Datum plsample_func_handler(PG_FUNCTION_ARGS);
      33              : static HeapTuple plsample_trigger_handler(PG_FUNCTION_ARGS);
      34              : 
      35              : /*
      36              :  * Handle function, procedure, and trigger calls.
      37              :  */
      38              : Datum
      39            0 : plsample_call_handler(PG_FUNCTION_ARGS)
      40              : {
      41            0 :         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            0 :         PG_TRY();
      49              :         {
      50              :                 /*
      51              :                  * Determine if called as function or trigger and call appropriate
      52              :                  * subhandler.
      53              :                  */
      54            0 :                 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            0 :                         retval = PointerGetDatum(plsample_trigger_handler(fcinfo));
      62            0 :                 }
      63            0 :                 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            0 :                 }
      73              :                 else
      74              :                 {
      75              :                         /* Regular function handler */
      76            0 :                         retval = plsample_func_handler(fcinfo);
      77              :                 }
      78              :         }
      79            0 :         PG_FINALLY();
      80              :         {
      81              :         }
      82            0 :         PG_END_TRY();
      83              : 
      84            0 :         return retval;
      85            0 : }
      86              : 
      87              : /*
      88              :  * plsample_func_handler
      89              :  *
      90              :  * Function called by the call handler for function execution.
      91              :  */
      92              : static Datum
      93            0 : plsample_func_handler(PG_FUNCTION_ARGS)
      94              : {
      95            0 :         HeapTuple       pl_tuple;
      96            0 :         Datum           ret;
      97            0 :         char       *source;
      98            0 :         bool            isnull;
      99            0 :         FmgrInfo   *arg_out_func;
     100            0 :         Form_pg_type type_struct;
     101            0 :         HeapTuple       type_tuple;
     102            0 :         Form_pg_proc pl_struct;
     103            0 :         volatile MemoryContext proc_cxt = NULL;
     104            0 :         Oid                *argtypes;
     105            0 :         char      **argnames;
     106            0 :         char       *argmodes;
     107            0 :         char       *proname;
     108            0 :         Form_pg_type pg_type_entry;
     109            0 :         Oid                     result_typioparam;
     110            0 :         Oid                     prorettype;
     111            0 :         FmgrInfo        result_in_func;
     112            0 :         int                     numargs;
     113              : 
     114              :         /* Fetch the function's pg_proc entry. */
     115            0 :         pl_tuple = SearchSysCache1(PROCOID,
     116            0 :                                                            ObjectIdGetDatum(fcinfo->flinfo->fn_oid));
     117            0 :         if (!HeapTupleIsValid(pl_tuple))
     118            0 :                 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            0 :         pl_struct = (Form_pg_proc) GETSTRUCT(pl_tuple);
     126            0 :         proname = pstrdup(NameStr(pl_struct->proname));
     127            0 :         ret = SysCacheGetAttr(PROCOID, pl_tuple, Anum_pg_proc_prosrc, &isnull);
     128            0 :         if (isnull)
     129            0 :                 elog(ERROR, "could not find source text of function \"%s\"",
     130              :                          proname);
     131            0 :         source = DatumGetCString(DirectFunctionCall1(textout, ret));
     132            0 :         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              :          */
     140            0 :         proc_cxt = AllocSetContextCreate(TopMemoryContext,
     141              :                                                                          "PL/Sample function",
     142              :                                                                          ALLOCSET_SMALL_SIZES);
     143              : 
     144            0 :         arg_out_func = (FmgrInfo *) palloc0(fcinfo->nargs * sizeof(FmgrInfo));
     145            0 :         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            0 :         for (int i = 0; i < numargs; i++)
     152              :         {
     153            0 :                 Oid                     argtype = pl_struct->proargtypes.values[i];
     154            0 :                 char       *value;
     155              : 
     156            0 :                 type_tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(argtype));
     157            0 :                 if (!HeapTupleIsValid(type_tuple))
     158            0 :                         elog(ERROR, "cache lookup failed for type %u", argtype);
     159              : 
     160            0 :                 type_struct = (Form_pg_type) GETSTRUCT(type_tuple);
     161            0 :                 fmgr_info_cxt(type_struct->typoutput, &(arg_out_func[i]), proc_cxt);
     162            0 :                 ReleaseSysCache(type_tuple);
     163              : 
     164            0 :                 value = OutputFunctionCall(&arg_out_func[i], fcinfo->args[i].value);
     165            0 :                 ereport(NOTICE,
     166              :                                 (errmsg("argument: %d; name: %s; value: %s",
     167              :                                                 i, argnames[i], value)));
     168            0 :         }
     169              : 
     170              :         /* Type of the result */
     171            0 :         prorettype = pl_struct->prorettype;
     172            0 :         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            0 :         if (prorettype != TEXTOID)
     183            0 :                 PG_RETURN_NULL();
     184              : 
     185            0 :         type_tuple = SearchSysCache1(TYPEOID,
     186            0 :                                                                  ObjectIdGetDatum(prorettype));
     187            0 :         if (!HeapTupleIsValid(type_tuple))
     188            0 :                 elog(ERROR, "cache lookup failed for type %u", prorettype);
     189            0 :         pg_type_entry = (Form_pg_type) GETSTRUCT(type_tuple);
     190            0 :         result_typioparam = getTypeIOParam(type_tuple);
     191              : 
     192            0 :         fmgr_info_cxt(pg_type_entry->typinput, &result_in_func, proc_cxt);
     193            0 :         ReleaseSysCache(type_tuple);
     194              : 
     195            0 :         ret = InputFunctionCall(&result_in_func, source, result_typioparam, -1);
     196            0 :         PG_RETURN_DATUM(ret);
     197            0 : }
     198              : 
     199              : /*
     200              :  * plsample_trigger_handler
     201              :  *
     202              :  * Function called by the call handler for trigger execution.
     203              :  */
     204              : static HeapTuple
     205            0 : plsample_trigger_handler(PG_FUNCTION_ARGS)
     206              : {
     207            0 :         TriggerData *trigdata = (TriggerData *) fcinfo->context;
     208            0 :         char       *string;
     209            0 :         volatile HeapTuple rettup;
     210            0 :         HeapTuple       pl_tuple;
     211            0 :         Datum           ret;
     212            0 :         char       *source;
     213            0 :         bool            isnull;
     214            0 :         Form_pg_proc pl_struct;
     215            0 :         char       *proname;
     216            0 :         int                     rc PG_USED_FOR_ASSERTS_ONLY;
     217              : 
     218              :         /* Make sure this is being called from a trigger. */
     219            0 :         if (!CALLED_AS_TRIGGER(fcinfo))
     220            0 :                 elog(ERROR, "not called by trigger manager");
     221              : 
     222              :         /* Connect to the SPI manager */
     223            0 :         SPI_connect();
     224              : 
     225            0 :         rc = SPI_register_trigger_data(trigdata);
     226            0 :         Assert(rc >= 0);
     227              : 
     228              :         /* Fetch the function's pg_proc entry. */
     229            0 :         pl_tuple = SearchSysCache1(PROCOID,
     230            0 :                                                            ObjectIdGetDatum(fcinfo->flinfo->fn_oid));
     231            0 :         if (!HeapTupleIsValid(pl_tuple))
     232            0 :                 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            0 :         pl_struct = (Form_pg_proc) GETSTRUCT(pl_tuple);
     242            0 :         proname = pstrdup(NameStr(pl_struct->proname));
     243            0 :         ret = SysCacheGetAttr(PROCOID, pl_tuple, Anum_pg_proc_prosrc, &isnull);
     244            0 :         if (isnull)
     245            0 :                 elog(ERROR, "could not find source text of function \"%s\"",
     246              :                          proname);
     247            0 :         source = DatumGetCString(DirectFunctionCall1(textout, ret));
     248            0 :         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            0 :         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            0 :         PG_TRY();
     283              :         {
     284            0 :                 ereport(NOTICE,
     285              :                                 (errmsg("trigger name: %s", trigdata->tg_trigger->tgname)));
     286            0 :                 string = SPI_getrelname(trigdata->tg_relation);
     287            0 :                 ereport(NOTICE, (errmsg("trigger relation: %s", string)));
     288              : 
     289            0 :                 string = SPI_getnspname(trigdata->tg_relation);
     290            0 :                 ereport(NOTICE, (errmsg("trigger relation schema: %s", string)));
     291              : 
     292              :                 /* Example handling of different trigger aspects. */
     293              : 
     294            0 :                 if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
     295              :                 {
     296            0 :                         ereport(NOTICE, (errmsg("triggered by INSERT")));
     297            0 :                         rettup = trigdata->tg_trigtuple;
     298            0 :                 }
     299            0 :                 else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
     300              :                 {
     301            0 :                         ereport(NOTICE, (errmsg("triggered by DELETE")));
     302            0 :                         rettup = trigdata->tg_trigtuple;
     303            0 :                 }
     304            0 :                 else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
     305              :                 {
     306            0 :                         ereport(NOTICE, (errmsg("triggered by UPDATE")));
     307            0 :                         rettup = trigdata->tg_trigtuple;
     308            0 :                 }
     309            0 :                 else if (TRIGGER_FIRED_BY_TRUNCATE(trigdata->tg_event))
     310              :                 {
     311            0 :                         ereport(NOTICE, (errmsg("triggered by TRUNCATE")));
     312            0 :                         rettup = trigdata->tg_trigtuple;
     313            0 :                 }
     314              :                 else
     315            0 :                         elog(ERROR, "unrecognized event: %u", trigdata->tg_event);
     316              : 
     317            0 :                 if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
     318            0 :                         ereport(NOTICE, (errmsg("triggered BEFORE")));
     319            0 :                 else if (TRIGGER_FIRED_AFTER(trigdata->tg_event))
     320            0 :                         ereport(NOTICE, (errmsg("triggered AFTER")));
     321            0 :                 else if (TRIGGER_FIRED_INSTEAD(trigdata->tg_event))
     322            0 :                         ereport(NOTICE, (errmsg("triggered INSTEAD OF")));
     323              :                 else
     324            0 :                         elog(ERROR, "unrecognized when: %u", trigdata->tg_event);
     325              : 
     326            0 :                 if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
     327            0 :                         ereport(NOTICE, (errmsg("triggered per row")));
     328            0 :                 else if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
     329            0 :                         ereport(NOTICE, (errmsg("triggered per statement")));
     330              :                 else
     331            0 :                         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            0 :                 for (int i = 0; i < trigdata->tg_trigger->tgnargs; i++)
     338            0 :                         ereport(NOTICE,
     339              :                                         (errmsg("trigger arg[%i]: %s", i,
     340              :                                                         trigdata->tg_trigger->tgargs[i])));
     341              :         }
     342            0 :         PG_CATCH();
     343              :         {
     344              :                 /* Error cleanup code would go here */
     345            0 :                 PG_RE_THROW();
     346              :         }
     347            0 :         PG_END_TRY();
     348              : 
     349            0 :         if (SPI_finish() != SPI_OK_FINISH)
     350            0 :                 elog(ERROR, "SPI_finish() failed");
     351              : 
     352            0 :         return rettup;
     353            0 : }
        

Generated by: LCOV version 2.3.2-1