LCOV - differential code coverage report
Current view: top level - src/test/modules/plsample - plsample.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage HEAD vs 15 Lines: 78.2 % 110 86 24 86
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 5 5 5
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

Generated by: LCOV version v1.16-55-g56c0a2a