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

Generated by: LCOV version 1.11