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 : }
|