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

Generated by: LCOV version 1.13