LCOV - code coverage report
Current view: top level - src/pl/plpgsql/src - pl_handler.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 87.1 % 171 149
Test Date: 2026-09-14 18:15:48 Functions: 100.0 % 11 11
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 68.3 % 202 138

             Branch data     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                 :        2344 : 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                 :        4732 : plpgsql_extra_checks_check_hook(char **newvalue, void **extra, GucSource source)
      64                 :             : {
      65                 :             :     char       *rawstring;
      66                 :             :     List       *elemlist;
      67                 :             :     ListCell   *l;
      68                 :        4732 :     int         extrachecks = 0;
      69                 :             :     int        *myextra;
      70                 :             : 
      71         [ +  + ]:        4732 :     if (pg_strcasecmp(*newvalue, "all") == 0)
      72                 :           8 :         extrachecks = PLPGSQL_XCHECK_ALL;
      73         [ +  + ]:        4724 :     else if (pg_strcasecmp(*newvalue, "none") == 0)
      74                 :        4696 :         extrachecks = PLPGSQL_XCHECK_NONE;
      75                 :             :     else
      76                 :             :     {
      77                 :             :         /* Need a modifiable copy of string */
      78                 :          28 :         rawstring = pstrdup(*newvalue);
      79                 :             : 
      80                 :             :         /* Parse string into list of identifiers */
      81         [ -  + ]:          28 :         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   [ +  -  +  +  :          56 :         foreach(l, elemlist)
                   +  + ]
      91                 :             :         {
      92                 :          28 :             char       *tok = (char *) lfirst(l);
      93                 :             : 
      94         [ +  + ]:          28 :             if (pg_strcasecmp(tok, "shadowed_variables") == 0)
      95                 :          12 :                 extrachecks |= PLPGSQL_XCHECK_SHADOWVAR;
      96         [ +  + ]:          16 :             else if (pg_strcasecmp(tok, "too_many_rows") == 0)
      97                 :           8 :                 extrachecks |= PLPGSQL_XCHECK_TOOMANYROWS;
      98         [ +  - ]:           8 :             else if (pg_strcasecmp(tok, "strict_multi_assignment") == 0)
      99                 :           8 :                 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                 :          28 :         pfree(rawstring);
     117                 :          28 :         list_free(elemlist);
     118                 :             :     }
     119                 :             : 
     120                 :        4732 :     myextra = (int *) guc_malloc(LOG, sizeof(int));
     121         [ -  + ]:        4732 :     if (!myextra)
     122                 :           0 :         return false;
     123                 :        4732 :     *myextra = extrachecks;
     124                 :        4732 :     *extra = myextra;
     125                 :             : 
     126                 :        4732 :     return true;
     127                 :             : }
     128                 :             : 
     129                 :             : static void
     130                 :        2380 : plpgsql_extra_warnings_assign_hook(const char *newvalue, void *extra)
     131                 :             : {
     132                 :        2380 :     plpgsql_extra_warnings = *((int *) extra);
     133                 :        2380 : }
     134                 :             : 
     135                 :             : static void
     136                 :        2376 : plpgsql_extra_errors_assign_hook(const char *newvalue, void *extra)
     137                 :             : {
     138                 :        2376 :     plpgsql_extra_errors = *((int *) extra);
     139                 :        2376 : }
     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                 :        2344 : _PG_init(void)
     149                 :             : {
     150                 :             :     /* Be sure we do initialization only once (should be redundant now) */
     151                 :             :     static bool inited = false;
     152                 :             : 
     153         [ -  + ]:        2344 :     if (inited)
     154                 :           0 :         return;
     155                 :             : 
     156                 :        2344 :     pg_bindtextdomain(TEXTDOMAIN);
     157                 :             : 
     158                 :        2344 :     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                 :        2344 :     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                 :        2344 :     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                 :        2344 :     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                 :        2344 :     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                 :        2344 :     MarkGUCPrefixReserved("plpgsql");
     204                 :             : 
     205                 :        2344 :     RegisterXactCallback(plpgsql_xact_cb, NULL);
     206                 :        2344 :     RegisterSubXactCallback(plpgsql_subxact_cb, NULL);
     207                 :             : 
     208                 :             :     /* Set up a rendezvous point with optional instrumentation plugin */
     209                 :        2344 :     plpgsql_plugin_ptr = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin");
     210                 :             : 
     211                 :        2344 :     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                 :         653 : PG_FUNCTION_INFO_V1(plpgsql_call_handler);
     222                 :             : 
     223                 :             : Datum
     224                 :       56482 : 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                 :       56482 :     volatile Datum retval = (Datum) 0;
     231                 :             :     int         rc;
     232                 :             : 
     233                 :      125037 :     nonatomic = fcinfo->context &&
     234   [ +  +  +  + ]:       56595 :         IsA(fcinfo->context, CallContext) &&
     235         [ +  + ]:         113 :         !castNode(CallContext, fcinfo->context)->atomic;
     236                 :             : 
     237                 :             :     /*
     238                 :             :      * Connect to SPI manager
     239                 :             :      */
     240                 :       56482 :     SPI_connect_ext(nonatomic ? SPI_OPT_NONATOMIC : 0);
     241                 :             : 
     242                 :             :     /* Find or compile the function */
     243                 :       56482 :     func = plpgsql_compile(fcinfo, false);
     244                 :             : 
     245                 :             :     /* Must save and restore prior value of cur_estate */
     246                 :       56473 :     save_cur_estate = func->cur_estate;
     247                 :             : 
     248                 :             :     /* Mark the function as busy, so it can't be deleted from under us */
     249                 :       56473 :     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                 :       56473 :     procedure_resowner =
     259         [ +  + ]:         101 :         (nonatomic && func->requires_procedure_resowner) ?
     260         [ +  + ]:       56574 :         ResourceOwnerCreate(NULL, "PL/pgSQL procedure resources") : NULL;
     261                 :             : 
     262         [ +  + ]:       56473 :     PG_TRY();
     263                 :             :     {
     264                 :             :         /*
     265                 :             :          * Determine if called as function or trigger and call appropriate
     266                 :             :          * subhandler
     267                 :             :          */
     268   [ +  +  +  + ]:       56473 :         if (CALLED_AS_TRIGGER(fcinfo))
     269                 :       10614 :             retval = PointerGetDatum(plpgsql_exec_trigger(func,
     270                 :             :                                                           (TriggerData *) fcinfo->context));
     271   [ +  +  +  + ]:       45859 :         else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
     272                 :             :         {
     273                 :        1138 :             plpgsql_exec_event_trigger(func,
     274                 :        1138 :                                        (EventTriggerData *) fcinfo->context);
     275                 :             :             /* there's no return value in this case */
     276                 :             :         }
     277                 :             :         else
     278                 :       44313 :             retval = plpgsql_exec_function(func, fcinfo,
     279                 :             :                                            NULL, NULL,
     280                 :             :                                            procedure_resowner,
     281                 :       44721 :                                            !nonatomic);
     282                 :             :     }
     283                 :         566 :     PG_FINALLY();
     284                 :             :     {
     285                 :             :         /* Decrement use-count, restore cur_estate */
     286                 :       56473 :         func->cfunc.use_count--;
     287                 :       56473 :         func->cur_estate = save_cur_estate;
     288                 :             : 
     289                 :             :         /* Be sure to release the procedure resowner if any */
     290         [ +  + ]:       56473 :         if (procedure_resowner)
     291                 :             :         {
     292                 :          14 :             ReleaseAllPlanCacheRefsInOwner(procedure_resowner);
     293                 :          14 :             ResourceOwnerDelete(procedure_resowner);
     294                 :             :         }
     295                 :             :     }
     296         [ +  + ]:       56473 :     PG_END_TRY();
     297                 :             : 
     298                 :             :     /*
     299                 :             :      * Disconnect from SPI manager
     300                 :             :      */
     301         [ -  + ]:       55907 :     if ((rc = SPI_finish()) != SPI_OK_FINISH)
     302         [ #  # ]:           0 :         elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
     303                 :             : 
     304                 :       55907 :     return retval;
     305                 :             : }
     306                 :             : 
     307                 :             : /* ----------
     308                 :             :  * plpgsql_inline_handler
     309                 :             :  *
     310                 :             :  * Called by PostgreSQL to execute an anonymous code block
     311                 :             :  * ----------
     312                 :             :  */
     313                 :         237 : PG_FUNCTION_INFO_V1(plpgsql_inline_handler);
     314                 :             : 
     315                 :             : Datum
     316                 :         885 : plpgsql_inline_handler(PG_FUNCTION_ARGS)
     317                 :             : {
     318                 :         885 :     LOCAL_FCINFO(fake_fcinfo, 0);
     319                 :         885 :     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                 :         885 :     SPI_connect_ext(codeblock->atomic ? 0 : SPI_OPT_NONATOMIC);
     331                 :             : 
     332                 :             :     /* Compile the anonymous code block */
     333                 :         885 :     func = plpgsql_compile_inline(codeblock->source_text);
     334                 :             : 
     335                 :             :     /* Mark the function as busy, just pro forma */
     336                 :         856 :     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   [ +  -  +  -  :        4280 :     MemSet(fake_fcinfo, 0, SizeForFunctionCallInfo(0));
          +  -  +  -  +  
                      + ]
     344   [ +  -  +  -  :        5992 :     MemSet(&flinfo, 0, sizeof(flinfo));
          +  -  +  -  +  
                      + ]
     345                 :         856 :     fake_fcinfo->flinfo = &flinfo;
     346                 :         856 :     flinfo.fn_oid = InvalidOid;
     347                 :         856 :     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                 :         856 :     simple_eval_estate = CreateExecutorState();
     362                 :             :     simple_eval_resowner =
     363                 :         856 :         ResourceOwnerCreate(NULL, "PL/pgSQL DO block simple expressions");
     364                 :             : 
     365                 :             :     /* And run the function */
     366         [ +  + ]:         856 :     PG_TRY();
     367                 :             :     {
     368                 :         664 :         retval = plpgsql_exec_function(func, fake_fcinfo,
     369                 :             :                                        simple_eval_estate,
     370                 :             :                                        simple_eval_resowner,
     371                 :             :                                        simple_eval_resowner,    /* see above */
     372                 :         856 :                                        codeblock->atomic);
     373                 :             :     }
     374                 :         192 :     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                 :         192 :         plpgsql_subxact_cb(SUBXACT_EVENT_ABORT_SUB,
     391                 :             :                            GetCurrentSubTransactionId(),
     392                 :             :                            0, NULL);
     393                 :             : 
     394                 :             :         /* Clean up the private EState and resowner */
     395                 :         192 :         FreeExecutorState(simple_eval_estate);
     396                 :         192 :         ReleaseAllPlanCacheRefsInOwner(simple_eval_resowner);
     397                 :         192 :         ResourceOwnerDelete(simple_eval_resowner);
     398                 :             : 
     399                 :             :         /* Function should now have no remaining use-counts ... */
     400                 :         192 :         func->cfunc.use_count--;
     401                 :             :         Assert(func->cfunc.use_count == 0);
     402                 :             : 
     403                 :             :         /* ... so we can free subsidiary storage */
     404                 :         192 :         plpgsql_free_function_memory(func);
     405                 :             : 
     406                 :             :         /* And propagate the error */
     407                 :         192 :         PG_RE_THROW();
     408                 :             :     }
     409         [ -  + ]:         664 :     PG_END_TRY();
     410                 :             : 
     411                 :             :     /* Clean up the private EState and resowner */
     412                 :         664 :     FreeExecutorState(simple_eval_estate);
     413                 :         664 :     ReleaseAllPlanCacheRefsInOwner(simple_eval_resowner);
     414                 :         664 :     ResourceOwnerDelete(simple_eval_resowner);
     415                 :             : 
     416                 :             :     /* Function should now have no remaining use-counts ... */
     417                 :         664 :     func->cfunc.use_count--;
     418                 :             :     Assert(func->cfunc.use_count == 0);
     419                 :             : 
     420                 :             :     /* ... so we can free subsidiary storage */
     421                 :         664 :     plpgsql_free_function_memory(func);
     422                 :             : 
     423                 :             :     /*
     424                 :             :      * Disconnect from SPI manager
     425                 :             :      */
     426         [ -  + ]:         664 :     if ((rc = SPI_finish()) != SPI_OK_FINISH)
     427         [ #  # ]:           0 :         elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
     428                 :             : 
     429                 :         664 :     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                 :         461 : PG_FUNCTION_INFO_V1(plpgsql_validator);
     440                 :             : 
     441                 :             : Datum
     442                 :        2974 : plpgsql_validator(PG_FUNCTION_ARGS)
     443                 :             : {
     444                 :        2974 :     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                 :        2974 :     bool        is_dml_trigger = false;
     453                 :        2974 :     bool        is_event_trigger = false;
     454                 :             :     int         i;
     455                 :             : 
     456         [ -  + ]:        2974 :     if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
     457                 :           0 :         PG_RETURN_VOID();
     458                 :             : 
     459                 :             :     /* Get the new function's pg_proc entry */
     460                 :        2974 :     tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
     461         [ -  + ]:        2974 :     if (!HeapTupleIsValid(tuple))
     462         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for function %u", funcoid);
     463                 :        2974 :     proc = (Form_pg_proc) GETSTRUCT(tuple);
     464                 :             : 
     465                 :        2974 :     functyptype = get_typtype(proc->prorettype);
     466                 :             : 
     467                 :             :     /* Disallow pseudotype result */
     468                 :             :     /* except for TRIGGER, EVTTRIGGER, RECORD, VOID, or polymorphic */
     469         [ +  + ]:        2974 :     if (functyptype == TYPTYPE_PSEUDO)
     470                 :             :     {
     471         [ +  + ]:        1780 :         if (proc->prorettype == TRIGGEROID)
     472                 :         909 :             is_dml_trigger = true;
     473         [ +  + ]:         871 :         else if (proc->prorettype == EVENT_TRIGGEROID)
     474                 :          92 :             is_event_trigger = true;
     475         [ +  + ]:         779 :         else if (proc->prorettype != RECORDOID &&
     476         [ +  + ]:         660 :                  proc->prorettype != VOIDOID &&
     477   [ +  +  +  +  :          50 :                  !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                 :        2974 :     numargs = get_func_arg_info(tuple,
     487                 :             :                                 &argtypes, &argnames, &argmodes);
     488         [ +  + ]:        5019 :     for (i = 0; i < numargs; i++)
     489                 :             :     {
     490         [ +  + ]:        2045 :         if (get_typtype(argtypes[i]) == TYPTYPE_PSEUDO)
     491                 :             :         {
     492         [ +  + ]:         370 :             if (argtypes[i] != RECORDOID &&
     493   [ +  +  +  +  :         356 :                 !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         [ +  + ]:        2974 :     if (check_function_bodies)
     503                 :             :     {
     504                 :        2822 :         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                 :        2822 :         SPI_connect();
     514                 :             : 
     515                 :             :         /*
     516                 :             :          * Set up a fake fcinfo with just enough info to satisfy
     517                 :             :          * plpgsql_compile().
     518                 :             :          */
     519   [ +  -  +  -  :       14110 :         MemSet(fake_fcinfo, 0, SizeForFunctionCallInfo(0));
          +  -  +  -  +  
                      + ]
     520   [ +  -  +  -  :       19754 :         MemSet(&flinfo, 0, sizeof(flinfo));
          +  -  +  -  +  
                      + ]
     521                 :        2822 :         fake_fcinfo->flinfo = &flinfo;
     522                 :        2822 :         flinfo.fn_oid = funcoid;
     523                 :        2822 :         flinfo.fn_mcxt = CurrentMemoryContext;
     524         [ +  + ]:        2822 :         if (is_dml_trigger)
     525                 :             :         {
     526   [ +  -  +  -  :        9350 :             MemSet(&trigdata, 0, sizeof(trigdata));
          +  -  +  -  +  
                      + ]
     527                 :         850 :             trigdata.type = T_TriggerData;
     528                 :         850 :             fake_fcinfo->context = (Node *) &trigdata;
     529                 :             :         }
     530         [ +  + ]:        1972 :         else if (is_event_trigger)
     531                 :             :         {
     532   [ +  -  +  -  :         410 :             MemSet(&etrigdata, 0, sizeof(etrigdata));
          +  -  +  -  +  
                      + ]
     533                 :          82 :             etrigdata.type = T_EventTriggerData;
     534                 :          82 :             fake_fcinfo->context = (Node *) &etrigdata;
     535                 :             :         }
     536                 :             : 
     537                 :             :         /* Test-compile the function */
     538                 :        2822 :         plpgsql_compile(fake_fcinfo, true);
     539                 :             : 
     540                 :             :         /*
     541                 :             :          * Disconnect from SPI manager
     542                 :             :          */
     543         [ -  + ]:        2753 :         if ((rc = SPI_finish()) != SPI_OK_FINISH)
     544         [ #  # ]:           0 :             elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
     545                 :             :     }
     546                 :             : 
     547                 :        2905 :     ReleaseSysCache(tuple);
     548                 :             : 
     549                 :        2905 :     PG_RETURN_VOID();
     550                 :             : }
        

Generated by: LCOV version 2.0-1