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