LCOV - code coverage report
Current view: top level - src/pl/plpgsql/src - pl_handler.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 150 172 87.2 %
Date: 2026-02-07 12:18:09 Functions: 11 11 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * pl_handler.c     - Handler for the PL/pgSQL
       4             :  *            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/pl/plpgsql/src/pl_handler.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : 
      16             : #include "postgres.h"
      17             : 
      18             : #include "access/htup_details.h"
      19             : #include "catalog/pg_proc.h"
      20             : #include "catalog/pg_type.h"
      21             : #include "funcapi.h"
      22             : #include "miscadmin.h"
      23             : #include "plpgsql.h"
      24             : #include "utils/builtins.h"
      25             : #include "utils/guc.h"
      26             : #include "utils/lsyscache.h"
      27             : #include "utils/syscache.h"
      28             : #include "utils/varlena.h"
      29             : 
      30             : static bool plpgsql_extra_checks_check_hook(char **newvalue, void **extra, GucSource source);
      31             : static void plpgsql_extra_warnings_assign_hook(const char *newvalue, void *extra);
      32             : static void plpgsql_extra_errors_assign_hook(const char *newvalue, void *extra);
      33             : 
      34        3634 : PG_MODULE_MAGIC_EXT(
      35             :                     .name = "plpgsql",
      36             :                     .version = PG_VERSION
      37             : );
      38             : 
      39             : /* Custom GUC variable */
      40             : static const struct config_enum_entry variable_conflict_options[] = {
      41             :     {"error", PLPGSQL_RESOLVE_ERROR, false},
      42             :     {"use_variable", PLPGSQL_RESOLVE_VARIABLE, false},
      43             :     {"use_column", PLPGSQL_RESOLVE_COLUMN, false},
      44             :     {NULL, 0, false}
      45             : };
      46             : 
      47             : int         plpgsql_variable_conflict = PLPGSQL_RESOLVE_ERROR;
      48             : 
      49             : bool        plpgsql_print_strict_params = false;
      50             : 
      51             : bool        plpgsql_check_asserts = true;
      52             : 
      53             : static char *plpgsql_extra_warnings_string = NULL;
      54             : static char *plpgsql_extra_errors_string = NULL;
      55             : int         plpgsql_extra_warnings;
      56             : int         plpgsql_extra_errors;
      57             : 
      58             : /* Hook for plugins */
      59             : PLpgSQL_plugin **plpgsql_plugin_ptr = NULL;
      60             : 
      61             : 
      62             : static bool
      63        7334 : plpgsql_extra_checks_check_hook(char **newvalue, void **extra, GucSource source)
      64             : {
      65             :     char       *rawstring;
      66             :     List       *elemlist;
      67             :     ListCell   *l;
      68        7334 :     int         extrachecks = 0;
      69             :     int        *myextra;
      70             : 
      71        7334 :     if (pg_strcasecmp(*newvalue, "all") == 0)
      72          12 :         extrachecks = PLPGSQL_XCHECK_ALL;
      73        7322 :     else if (pg_strcasecmp(*newvalue, "none") == 0)
      74        7280 :         extrachecks = PLPGSQL_XCHECK_NONE;
      75             :     else
      76             :     {
      77             :         /* Need a modifiable copy of string */
      78          42 :         rawstring = pstrdup(*newvalue);
      79             : 
      80             :         /* Parse string into list of identifiers */
      81          42 :         if (!SplitIdentifierString(rawstring, ',', &elemlist))
      82             :         {
      83             :             /* syntax error in list */
      84           0 :             GUC_check_errdetail("List syntax is invalid.");
      85           0 :             pfree(rawstring);
      86           0 :             list_free(elemlist);
      87           0 :             return false;
      88             :         }
      89             : 
      90          84 :         foreach(l, elemlist)
      91             :         {
      92          42 :             char       *tok = (char *) lfirst(l);
      93             : 
      94          42 :             if (pg_strcasecmp(tok, "shadowed_variables") == 0)
      95          18 :                 extrachecks |= PLPGSQL_XCHECK_SHADOWVAR;
      96          24 :             else if (pg_strcasecmp(tok, "too_many_rows") == 0)
      97          12 :                 extrachecks |= PLPGSQL_XCHECK_TOOMANYROWS;
      98          12 :             else if (pg_strcasecmp(tok, "strict_multi_assignment") == 0)
      99          12 :                 extrachecks |= PLPGSQL_XCHECK_STRICTMULTIASSIGNMENT;
     100           0 :             else if (pg_strcasecmp(tok, "all") == 0 || pg_strcasecmp(tok, "none") == 0)
     101             :             {
     102           0 :                 GUC_check_errdetail("Key word \"%s\" cannot be combined with other key words.", tok);
     103           0 :                 pfree(rawstring);
     104           0 :                 list_free(elemlist);
     105           0 :                 return false;
     106             :             }
     107             :             else
     108             :             {
     109           0 :                 GUC_check_errdetail("Unrecognized key word: \"%s\".", tok);
     110           0 :                 pfree(rawstring);
     111           0 :                 list_free(elemlist);
     112           0 :                 return false;
     113             :             }
     114             :         }
     115             : 
     116          42 :         pfree(rawstring);
     117          42 :         list_free(elemlist);
     118             :     }
     119             : 
     120        7334 :     myextra = (int *) guc_malloc(LOG, sizeof(int));
     121        7334 :     if (!myextra)
     122           0 :         return false;
     123        7334 :     *myextra = extrachecks;
     124        7334 :     *extra = myextra;
     125             : 
     126        7334 :     return true;
     127             : }
     128             : 
     129             : static void
     130        3688 : plpgsql_extra_warnings_assign_hook(const char *newvalue, void *extra)
     131             : {
     132        3688 :     plpgsql_extra_warnings = *((int *) extra);
     133        3688 : }
     134             : 
     135             : static void
     136        3682 : plpgsql_extra_errors_assign_hook(const char *newvalue, void *extra)
     137             : {
     138        3682 :     plpgsql_extra_errors = *((int *) extra);
     139        3682 : }
     140             : 
     141             : 
     142             : /*
     143             :  * _PG_init()           - library load-time initialization
     144             :  *
     145             :  * DO NOT make this static nor change its name!
     146             :  */
     147             : void
     148        3634 : _PG_init(void)
     149             : {
     150             :     /* Be sure we do initialization only once (should be redundant now) */
     151             :     static bool inited = false;
     152             : 
     153        3634 :     if (inited)
     154           0 :         return;
     155             : 
     156        3634 :     pg_bindtextdomain(TEXTDOMAIN);
     157             : 
     158        3634 :     DefineCustomEnumVariable("plpgsql.variable_conflict",
     159             :                              gettext_noop("Sets handling of conflicts between PL/pgSQL variable names and table column names."),
     160             :                              NULL,
     161             :                              &plpgsql_variable_conflict,
     162             :                              PLPGSQL_RESOLVE_ERROR,
     163             :                              variable_conflict_options,
     164             :                              PGC_SUSET, 0,
     165             :                              NULL, NULL, NULL);
     166             : 
     167        3634 :     DefineCustomBoolVariable("plpgsql.print_strict_params",
     168             :                              gettext_noop("Print information about parameters in the DETAIL part of the error messages generated on INTO ... STRICT failures."),
     169             :                              NULL,
     170             :                              &plpgsql_print_strict_params,
     171             :                              false,
     172             :                              PGC_USERSET, 0,
     173             :                              NULL, NULL, NULL);
     174             : 
     175        3634 :     DefineCustomBoolVariable("plpgsql.check_asserts",
     176             :                              gettext_noop("Perform checks given in ASSERT statements."),
     177             :                              NULL,
     178             :                              &plpgsql_check_asserts,
     179             :                              true,
     180             :                              PGC_USERSET, 0,
     181             :                              NULL, NULL, NULL);
     182             : 
     183        3634 :     DefineCustomStringVariable("plpgsql.extra_warnings",
     184             :                                gettext_noop("List of programming constructs that should produce a warning."),
     185             :                                NULL,
     186             :                                &plpgsql_extra_warnings_string,
     187             :                                "none",
     188             :                                PGC_USERSET, GUC_LIST_INPUT,
     189             :                                plpgsql_extra_checks_check_hook,
     190             :                                plpgsql_extra_warnings_assign_hook,
     191             :                                NULL);
     192             : 
     193        3634 :     DefineCustomStringVariable("plpgsql.extra_errors",
     194             :                                gettext_noop("List of programming constructs that should produce an error."),
     195             :                                NULL,
     196             :                                &plpgsql_extra_errors_string,
     197             :                                "none",
     198             :                                PGC_USERSET, GUC_LIST_INPUT,
     199             :                                plpgsql_extra_checks_check_hook,
     200             :                                plpgsql_extra_errors_assign_hook,
     201             :                                NULL);
     202             : 
     203        3634 :     MarkGUCPrefixReserved("plpgsql");
     204             : 
     205        3634 :     RegisterXactCallback(plpgsql_xact_cb, NULL);
     206        3634 :     RegisterSubXactCallback(plpgsql_subxact_cb, NULL);
     207             : 
     208             :     /* Set up a rendezvous point with optional instrumentation plugin */
     209        3634 :     plpgsql_plugin_ptr = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin");
     210             : 
     211        3634 :     inited = true;
     212             : }
     213             : 
     214             : /* ----------
     215             :  * plpgsql_call_handler
     216             :  *
     217             :  * The PostgreSQL function manager and trigger manager
     218             :  * call this function for execution of PL/pgSQL procedures.
     219             :  * ----------
     220             :  */
     221        1116 : PG_FUNCTION_INFO_V1(plpgsql_call_handler);
     222             : 
     223             : Datum
     224       85468 : plpgsql_call_handler(PG_FUNCTION_ARGS)
     225             : {
     226             :     bool        nonatomic;
     227             :     PLpgSQL_function *func;
     228             :     PLpgSQL_execstate *save_cur_estate;
     229             :     ResourceOwner procedure_resowner;
     230       85468 :     volatile Datum retval = (Datum) 0;
     231             :     int         rc;
     232             : 
     233      188766 :     nonatomic = fcinfo->context &&
     234       85680 :         IsA(fcinfo->context, CallContext) &&
     235         212 :         !castNode(CallContext, fcinfo->context)->atomic;
     236             : 
     237             :     /*
     238             :      * Connect to SPI manager
     239             :      */
     240       85468 :     SPI_connect_ext(nonatomic ? SPI_OPT_NONATOMIC : 0);
     241             : 
     242             :     /* Find or compile the function */
     243       85468 :     func = plpgsql_compile(fcinfo, false);
     244             : 
     245             :     /* Must save and restore prior value of cur_estate */
     246       85454 :     save_cur_estate = func->cur_estate;
     247             : 
     248             :     /* Mark the function as busy, so it can't be deleted from under us */
     249       85454 :     func->cfunc.use_count++;
     250             : 
     251             :     /*
     252             :      * If we'll need a procedure-lifespan resowner to execute any CALL or DO
     253             :      * statements, create it now.  Since this resowner is not tied to any
     254             :      * parent, failing to free it would result in process-lifespan leaks.
     255             :      * Therefore, be very wary of adding any code between here and the PG_TRY
     256             :      * block.
     257             :      */
     258       85454 :     procedure_resowner =
     259         188 :         (nonatomic && func->requires_procedure_resowner) ?
     260       85642 :         ResourceOwnerCreate(NULL, "PL/pgSQL procedure resources") : NULL;
     261             : 
     262       85454 :     PG_TRY();
     263             :     {
     264             :         /*
     265             :          * Determine if called as function or trigger and call appropriate
     266             :          * subhandler
     267             :          */
     268       85454 :         if (CALLED_AS_TRIGGER(fcinfo))
     269       15194 :             retval = PointerGetDatum(plpgsql_exec_trigger(func,
     270       15402 :                                                           (TriggerData *) fcinfo->context));
     271       70052 :         else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
     272             :         {
     273        1904 :             plpgsql_exec_event_trigger(func,
     274        1904 :                                        (EventTriggerData *) fcinfo->context);
     275             :             /* there's no return value in this case */
     276             :         }
     277             :         else
     278       67502 :             retval = plpgsql_exec_function(func, fcinfo,
     279             :                                            NULL, NULL,
     280             :                                            procedure_resowner,
     281       68148 :                                            !nonatomic);
     282             :     }
     283         878 :     PG_FINALLY();
     284             :     {
     285             :         /* Decrement use-count, restore cur_estate */
     286       85454 :         func->cfunc.use_count--;
     287       85454 :         func->cur_estate = save_cur_estate;
     288             : 
     289             :         /* Be sure to release the procedure resowner if any */
     290       85454 :         if (procedure_resowner)
     291             :         {
     292          28 :             ReleaseAllPlanCacheRefsInOwner(procedure_resowner);
     293          28 :             ResourceOwnerDelete(procedure_resowner);
     294             :         }
     295             :     }
     296       85454 :     PG_END_TRY();
     297             : 
     298             :     /*
     299             :      * Disconnect from SPI manager
     300             :      */
     301       84576 :     if ((rc = SPI_finish()) != SPI_OK_FINISH)
     302           0 :         elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
     303             : 
     304       84576 :     return retval;
     305             : }
     306             : 
     307             : /* ----------
     308             :  * plpgsql_inline_handler
     309             :  *
     310             :  * Called by PostgreSQL to execute an anonymous code block
     311             :  * ----------
     312             :  */
     313         348 : PG_FUNCTION_INFO_V1(plpgsql_inline_handler);
     314             : 
     315             : Datum
     316        1506 : plpgsql_inline_handler(PG_FUNCTION_ARGS)
     317             : {
     318        1506 :     LOCAL_FCINFO(fake_fcinfo, 0);
     319        1506 :     InlineCodeBlock *codeblock = castNode(InlineCodeBlock, DatumGetPointer(PG_GETARG_DATUM(0)));
     320             :     PLpgSQL_function *func;
     321             :     FmgrInfo    flinfo;
     322             :     EState     *simple_eval_estate;
     323             :     ResourceOwner simple_eval_resowner;
     324             :     Datum       retval;
     325             :     int         rc;
     326             : 
     327             :     /*
     328             :      * Connect to SPI manager
     329             :      */
     330        1506 :     SPI_connect_ext(codeblock->atomic ? 0 : SPI_OPT_NONATOMIC);
     331             : 
     332             :     /* Compile the anonymous code block */
     333        1506 :     func = plpgsql_compile_inline(codeblock->source_text);
     334             : 
     335             :     /* Mark the function as busy, just pro forma */
     336        1450 :     func->cfunc.use_count++;
     337             : 
     338             :     /*
     339             :      * Set up a fake fcinfo with just enough info to satisfy
     340             :      * plpgsql_exec_function().  In particular note that this sets things up
     341             :      * with no arguments passed.
     342             :      */
     343        7250 :     MemSet(fake_fcinfo, 0, SizeForFunctionCallInfo(0));
     344       10150 :     MemSet(&flinfo, 0, sizeof(flinfo));
     345        1450 :     fake_fcinfo->flinfo = &flinfo;
     346        1450 :     flinfo.fn_oid = InvalidOid;
     347        1450 :     flinfo.fn_mcxt = CurrentMemoryContext;
     348             : 
     349             :     /*
     350             :      * Create a private EState and resowner for simple-expression execution.
     351             :      * Notice that these are NOT tied to transaction-level resources; they
     352             :      * must survive any COMMIT/ROLLBACK the DO block executes, since we will
     353             :      * unconditionally try to clean them up below.  (Hence, be wary of adding
     354             :      * anything that could fail between here and the PG_TRY block.)  See the
     355             :      * comments for shared_simple_eval_estate.
     356             :      *
     357             :      * Because this resowner isn't tied to the calling transaction, we can
     358             :      * also use it as the "procedure" resowner for any CALL statements.  That
     359             :      * helps reduce the opportunities for failure here.
     360             :      */
     361        1450 :     simple_eval_estate = CreateExecutorState();
     362             :     simple_eval_resowner =
     363        1450 :         ResourceOwnerCreate(NULL, "PL/pgSQL DO block simple expressions");
     364             : 
     365             :     /* And run the function */
     366        1450 :     PG_TRY();
     367             :     {
     368        1142 :         retval = plpgsql_exec_function(func, fake_fcinfo,
     369             :                                        simple_eval_estate,
     370             :                                        simple_eval_resowner,
     371             :                                        simple_eval_resowner,    /* see above */
     372        1450 :                                        codeblock->atomic);
     373             :     }
     374         308 :     PG_CATCH();
     375             :     {
     376             :         /*
     377             :          * We need to clean up what would otherwise be long-lived resources
     378             :          * accumulated by the failed DO block, principally cached plans for
     379             :          * statements (which can be flushed by plpgsql_free_function_memory),
     380             :          * execution trees for simple expressions, which are in the private
     381             :          * EState, and cached-plan refcounts held by the private resowner.
     382             :          *
     383             :          * Before releasing the private EState, we must clean up any
     384             :          * simple_econtext_stack entries pointing into it, which we can do by
     385             :          * invoking the subxact callback.  (It will be called again later if
     386             :          * some outer control level does a subtransaction abort, but no harm
     387             :          * is done.)  We cheat a bit knowing that plpgsql_subxact_cb does not
     388             :          * pay attention to its parentSubid argument.
     389             :          */
     390         308 :         plpgsql_subxact_cb(SUBXACT_EVENT_ABORT_SUB,
     391             :                            GetCurrentSubTransactionId(),
     392             :                            0, NULL);
     393             : 
     394             :         /* Clean up the private EState and resowner */
     395         308 :         FreeExecutorState(simple_eval_estate);
     396         308 :         ReleaseAllPlanCacheRefsInOwner(simple_eval_resowner);
     397         308 :         ResourceOwnerDelete(simple_eval_resowner);
     398             : 
     399             :         /* Function should now have no remaining use-counts ... */
     400         308 :         func->cfunc.use_count--;
     401             :         Assert(func->cfunc.use_count == 0);
     402             : 
     403             :         /* ... so we can free subsidiary storage */
     404         308 :         plpgsql_free_function_memory(func);
     405             : 
     406             :         /* And propagate the error */
     407         308 :         PG_RE_THROW();
     408             :     }
     409        1142 :     PG_END_TRY();
     410             : 
     411             :     /* Clean up the private EState and resowner */
     412        1142 :     FreeExecutorState(simple_eval_estate);
     413        1142 :     ReleaseAllPlanCacheRefsInOwner(simple_eval_resowner);
     414        1142 :     ResourceOwnerDelete(simple_eval_resowner);
     415             : 
     416             :     /* Function should now have no remaining use-counts ... */
     417        1142 :     func->cfunc.use_count--;
     418             :     Assert(func->cfunc.use_count == 0);
     419             : 
     420             :     /* ... so we can free subsidiary storage */
     421        1142 :     plpgsql_free_function_memory(func);
     422             : 
     423             :     /*
     424             :      * Disconnect from SPI manager
     425             :      */
     426        1142 :     if ((rc = SPI_finish()) != SPI_OK_FINISH)
     427           0 :         elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
     428             : 
     429        1142 :     return retval;
     430             : }
     431             : 
     432             : /* ----------
     433             :  * plpgsql_validator
     434             :  *
     435             :  * This function attempts to validate a PL/pgSQL function at
     436             :  * CREATE FUNCTION time.
     437             :  * ----------
     438             :  */
     439         720 : PG_FUNCTION_INFO_V1(plpgsql_validator);
     440             : 
     441             : Datum
     442        4706 : plpgsql_validator(PG_FUNCTION_ARGS)
     443             : {
     444        4706 :     Oid         funcoid = PG_GETARG_OID(0);
     445             :     HeapTuple   tuple;
     446             :     Form_pg_proc proc;
     447             :     char        functyptype;
     448             :     int         numargs;
     449             :     Oid        *argtypes;
     450             :     char      **argnames;
     451             :     char       *argmodes;
     452        4706 :     bool        is_dml_trigger = false;
     453        4706 :     bool        is_event_trigger = false;
     454             :     int         i;
     455             : 
     456        4706 :     if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
     457           0 :         PG_RETURN_VOID();
     458             : 
     459             :     /* Get the new function's pg_proc entry */
     460        4706 :     tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
     461        4706 :     if (!HeapTupleIsValid(tuple))
     462           0 :         elog(ERROR, "cache lookup failed for function %u", funcoid);
     463        4706 :     proc = (Form_pg_proc) GETSTRUCT(tuple);
     464             : 
     465        4706 :     functyptype = get_typtype(proc->prorettype);
     466             : 
     467             :     /* Disallow pseudotype result */
     468             :     /* except for TRIGGER, EVTTRIGGER, RECORD, VOID, or polymorphic */
     469        4706 :     if (functyptype == TYPTYPE_PSEUDO)
     470             :     {
     471        2838 :         if (proc->prorettype == TRIGGEROID)
     472        1418 :             is_dml_trigger = true;
     473        1420 :         else if (proc->prorettype == EVENT_TRIGGEROID)
     474         144 :             is_event_trigger = true;
     475        1276 :         else if (proc->prorettype != RECORDOID &&
     476        1084 :                  proc->prorettype != VOIDOID &&
     477          76 :                  !IsPolymorphicType(proc->prorettype))
     478           0 :             ereport(ERROR,
     479             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     480             :                      errmsg("PL/pgSQL functions cannot return type %s",
     481             :                             format_type_be(proc->prorettype))));
     482             :     }
     483             : 
     484             :     /* Disallow pseudotypes in arguments (either IN or OUT) */
     485             :     /* except for RECORD and polymorphic */
     486        4706 :     numargs = get_func_arg_info(tuple,
     487             :                                 &argtypes, &argnames, &argmodes);
     488        7994 :     for (i = 0; i < numargs; i++)
     489             :     {
     490        3288 :         if (get_typtype(argtypes[i]) == TYPTYPE_PSEUDO)
     491             :         {
     492         654 :             if (argtypes[i] != RECORDOID &&
     493         632 :                 !IsPolymorphicType(argtypes[i]))
     494           0 :                 ereport(ERROR,
     495             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     496             :                          errmsg("PL/pgSQL functions cannot accept type %s",
     497             :                                 format_type_be(argtypes[i]))));
     498             :         }
     499             :     }
     500             : 
     501             :     /* Postpone body checks if !check_function_bodies */
     502        4706 :     if (check_function_bodies)
     503             :     {
     504        4412 :         LOCAL_FCINFO(fake_fcinfo, 0);
     505             :         FmgrInfo    flinfo;
     506             :         int         rc;
     507             :         TriggerData trigdata;
     508             :         EventTriggerData etrigdata;
     509             : 
     510             :         /*
     511             :          * Connect to SPI manager (is this needed for compilation?)
     512             :          */
     513        4412 :         SPI_connect();
     514             : 
     515             :         /*
     516             :          * Set up a fake fcinfo with just enough info to satisfy
     517             :          * plpgsql_compile().
     518             :          */
     519       22060 :         MemSet(fake_fcinfo, 0, SizeForFunctionCallInfo(0));
     520       30884 :         MemSet(&flinfo, 0, sizeof(flinfo));
     521        4412 :         fake_fcinfo->flinfo = &flinfo;
     522        4412 :         flinfo.fn_oid = funcoid;
     523        4412 :         flinfo.fn_mcxt = CurrentMemoryContext;
     524        4412 :         if (is_dml_trigger)
     525             :         {
     526       14366 :             MemSet(&trigdata, 0, sizeof(trigdata));
     527        1306 :             trigdata.type = T_TriggerData;
     528        1306 :             fake_fcinfo->context = (Node *) &trigdata;
     529             :         }
     530        3106 :         else if (is_event_trigger)
     531             :         {
     532         620 :             MemSet(&etrigdata, 0, sizeof(etrigdata));
     533         124 :             etrigdata.type = T_EventTriggerData;
     534         124 :             fake_fcinfo->context = (Node *) &etrigdata;
     535             :         }
     536             : 
     537             :         /* Test-compile the function */
     538        4412 :         plpgsql_compile(fake_fcinfo, true);
     539             : 
     540             :         /*
     541             :          * Disconnect from SPI manager
     542             :          */
     543        4304 :         if ((rc = SPI_finish()) != SPI_OK_FINISH)
     544           0 :             elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
     545             :     }
     546             : 
     547        4598 :     ReleaseSysCache(tuple);
     548             : 
     549        4598 :     PG_RETURN_VOID();
     550             : }

Generated by: LCOV version 1.16