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

Generated by: LCOV version 1.13