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