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