1 /*------------------------------------------------------------------------- 2 * is_error<T: ::std::error::Error + Send + Sync>()3 * pl_handler.c - Handler for the PL/pgSQL 4 * procedural language 5 * 6 * Portions Copyright (c) 1996-2020, 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 Datum retval; 228 int rc; 229 230 nonatomic = fcinfo->context && 231 IsA(fcinfo->context, CallContext) && 232 !castNode(CallContext, fcinfo->context)->atomic; 233 234 /* 235 * Connect to SPI manager 236 */ 237 if ((rc = SPI_connect_ext(nonatomic ? SPI_OPT_NONATOMIC : 0)) != SPI_OK_CONNECT) 238 elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc)); 239 240 /* Find or compile the function */ 241 func = plpgsql_compile(fcinfo, false); 242 243 /* Must save and restore prior value of cur_estate */ 244 save_cur_estate = func->cur_estate; 245 246 /* Mark the function as busy, so it can't be deleted from under us */ 247 func->use_count++; 248 249 PG_TRY(); 250 { 251 /* 252 * Determine if called as function or trigger and call appropriate 253 * subhandler 254 */ 255 if (CALLED_AS_TRIGGER(fcinfo)) 256 retval = PointerGetDatum(plpgsql_exec_trigger(func, 257 (TriggerData *) fcinfo->context)); 258 else if (CALLED_AS_EVENT_TRIGGER(fcinfo)) 259 { 260 plpgsql_exec_event_trigger(func, 261 (EventTriggerData *) fcinfo->context); 262 retval = (Datum) 0; 263 } 264 else 265 retval = plpgsql_exec_function(func, fcinfo, 266 NULL, NULL, 267 !nonatomic); 268 } 269 PG_FINALLY(); 270 { 271 /* Decrement use-count, restore cur_estate */ 272 func->use_count--; 273 func->cur_estate = save_cur_estate; 274 } 275 PG_END_TRY(); 276 277 /* 278 * Disconnect from SPI manager 279 */ 280 if ((rc = SPI_finish()) != SPI_OK_FINISH) 281 elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc)); 282 283 return retval; 284 } 285 286 /* ---------- 287 * plpgsql_inline_handler 288 * 289 * Called by PostgreSQL to execute an anonymous code block 290 * ---------- 291 */ 292 PG_FUNCTION_INFO_V1(plpgsql_inline_handler); 293 294 Datum 295 plpgsql_inline_handler(PG_FUNCTION_ARGS) 296 { 297 LOCAL_FCINFO(fake_fcinfo, 0); 298 InlineCodeBlock *codeblock = castNode(InlineCodeBlock, DatumGetPointer(PG_GETARG_DATUM(0))); 299 PLpgSQL_function *func; 300 FmgrInfo flinfo; 301 EState *simple_eval_estate; 302 ResourceOwner simple_eval_resowner; 303 Datum retval; 304 int rc; 305 306 /* 307 * Connect to SPI manager 308 */ 309 if ((rc = SPI_connect_ext(codeblock->atomic ? 0 : SPI_OPT_NONATOMIC)) != SPI_OK_CONNECT) 310 elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc)); 311 312 /* Compile the anonymous code block */ 313 func = plpgsql_compile_inline(codeblock->source_text); 314 315 /* Mark the function as busy, just pro forma */ 316 func->use_count++; 317 318 /* 319 * Set up a fake fcinfo with just enough info to satisfy 320 * plpgsql_exec_function(). In particular note that this sets things up 321 * with no arguments passed. 322 */ 323 MemSet(fake_fcinfo, 0, SizeForFunctionCallInfo(0)); 324 MemSet(&flinfo, 0, sizeof(flinfo)); 325 fake_fcinfo->flinfo = &flinfo; 326 flinfo.fn_oid = InvalidOid; 327 flinfo.fn_mcxt = CurrentMemoryContext; 328 329 /* 330 * Create a private EState and resowner for simple-expression execution. 331 * Notice that these are NOT tied to transaction-level resources; they 332 * must survive any COMMIT/ROLLBACK the DO block executes, since we will 333 * unconditionally try to clean them up below. (Hence, be wary of adding 334 * anything that could fail between here and the PG_TRY block.) See the 335 * comments for shared_simple_eval_estate. 336 */ 337 simple_eval_estate = CreateExecutorState(); 338 simple_eval_resowner = 339 ResourceOwnerCreate(NULL, "PL/pgSQL DO block simple expressions"); 340 341 /* And run the function */ 342 PG_TRY(); 343 { 344 retval = plpgsql_exec_function(func, fake_fcinfo, 345 simple_eval_estate, 346 simple_eval_resowner, 347 codeblock->atomic); 348 } 349 PG_CATCH(); 350 { 351 /* 352 * We need to clean up what would otherwise be long-lived resources 353 * accumulated by the failed DO block, principally cached plans for 354 * statements (which can be flushed by plpgsql_free_function_memory), 355 * execution trees for simple expressions, which are in the private 356 * EState, and cached-plan refcounts held by the private resowner. 357 * 358 * Before releasing the private EState, we must clean up any 359 * simple_econtext_stack entries pointing into it, which we can do by 360 * invoking the subxact callback. (It will be called again later if 361 * some outer control level does a subtransaction abort, but no harm 362 * is done.) We cheat a bit knowing that plpgsql_subxact_cb does not 363 * pay attention to its parentSubid argument. 364 */ 365 plpgsql_subxact_cb(SUBXACT_EVENT_ABORT_SUB, 366 GetCurrentSubTransactionId(), 367 0, NULL); 368 369 /* Clean up the private EState and resowner */ 370 FreeExecutorState(simple_eval_estate); 371 ResourceOwnerReleaseAllPlanCacheRefs(simple_eval_resowner); 372 ResourceOwnerDelete(simple_eval_resowner); 373 374 /* Function should now have no remaining use-counts ... */ 375 func->use_count--; 376 Assert(func->use_count == 0); 377 378 /* ... so we can free subsidiary storage */ 379 plpgsql_free_function_memory(func); 380 381 /* And propagate the error */ 382 PG_RE_THROW(); 383 } 384 PG_END_TRY(); 385 386 /* Clean up the private EState and resowner */ 387 FreeExecutorState(simple_eval_estate); 388 ResourceOwnerReleaseAllPlanCacheRefs(simple_eval_resowner); 389 ResourceOwnerDelete(simple_eval_resowner); 390 391 /* Function should now have no remaining use-counts ... */ 392 func->use_count--; 393 Assert(func->use_count == 0); 394 395 /* ... so we can free subsidiary storage */ 396 plpgsql_free_function_memory(func); 397 398 /* 399 * Disconnect from SPI manager 400 */ 401 if ((rc = SPI_finish()) != SPI_OK_FINISH) 402 elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc)); 403 404 return retval; 405 } 406 407 /* ---------- 408 * plpgsql_validator 409 * 410 * This function attempts to validate a PL/pgSQL function at 411 * CREATE FUNCTION time. 412 * ---------- 413 */ 414 PG_FUNCTION_INFO_V1(plpgsql_validator); 415 416 Datum 417 plpgsql_validator(PG_FUNCTION_ARGS) 418 { 419 Oid funcoid = PG_GETARG_OID(0); 420 HeapTuple tuple; 421 Form_pg_proc proc; 422 char functyptype; 423 int numargs; 424 Oid *argtypes; 425 char **argnames; 426 char *argmodes; 427 bool is_dml_trigger = false; 428 bool is_event_trigger = false; 429 int i; 430 431 if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid)) 432 PG_RETURN_VOID(); 433 434 /* Get the new function's pg_proc entry */ 435 tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid)); 436 if (!HeapTupleIsValid(tuple)) 437 elog(ERROR, "cache lookup failed for function %u", funcoid); 438 proc = (Form_pg_proc) GETSTRUCT(tuple); 439 440 functyptype = get_typtype(proc->prorettype); 441 442 /* Disallow pseudotype result */ 443 /* except for TRIGGER, EVTTRIGGER, RECORD, VOID, or polymorphic */ 444 if (functyptype == TYPTYPE_PSEUDO) 445 { 446 if (proc->prorettype == TRIGGEROID) 447 is_dml_trigger = true; 448 else if (proc->prorettype == EVTTRIGGEROID) 449 is_event_trigger = true; 450 else if (proc->prorettype != RECORDOID && 451 proc->prorettype != VOIDOID && 452 !IsPolymorphicType(proc->prorettype)) 453 ereport(ERROR, 454 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), 455 errmsg("PL/pgSQL functions cannot return type %s", 456 format_type_be(proc->prorettype)))); 457 } 458 459 /* Disallow pseudotypes in arguments (either IN or OUT) */ 460 /* except for RECORD and polymorphic */ 461 numargs = get_func_arg_info(tuple, 462 &argtypes, &argnames, &argmodes); 463 for (i = 0; i < numargs; i++) 464 { 465 if (get_typtype(argtypes[i]) == TYPTYPE_PSEUDO) 466 { 467 if (argtypes[i] != RECORDOID && 468 !IsPolymorphicType(argtypes[i])) 469 ereport(ERROR, 470 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), 471 errmsg("PL/pgSQL functions cannot accept type %s", 472 format_type_be(argtypes[i])))); 473 } 474 } 475 476 /* Postpone body checks if !check_function_bodies */ 477 if (check_function_bodies) 478 { 479 LOCAL_FCINFO(fake_fcinfo, 0); 480 FmgrInfo flinfo; 481 int rc; 482 TriggerData trigdata; 483 EventTriggerData etrigdata; 484 485 /* 486 * Connect to SPI manager (is this needed for compilation?) 487 */ 488 if ((rc = SPI_connect()) != SPI_OK_CONNECT) 489 elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc)); 490 491 /* 492 * Set up a fake fcinfo with just enough info to satisfy 493 * plpgsql_compile(). 494 */ 495 MemSet(fake_fcinfo, 0, SizeForFunctionCallInfo(0)); 496 MemSet(&flinfo, 0, sizeof(flinfo)); 497 fake_fcinfo->flinfo = &flinfo; 498 flinfo.fn_oid = funcoid; 499 flinfo.fn_mcxt = CurrentMemoryContext; 500 if (is_dml_trigger) 501 { 502 MemSet(&trigdata, 0, sizeof(trigdata)); 503 trigdata.type = T_TriggerData; 504 fake_fcinfo->context = (Node *) &trigdata; 505 } 506 else if (is_event_trigger) 507 { 508 MemSet(&etrigdata, 0, sizeof(etrigdata)); 509 etrigdata.type = T_EventTriggerData; 510 fake_fcinfo->context = (Node *) &etrigdata; 511 } 512 513 /* Test-compile the function */ 514 plpgsql_compile(fake_fcinfo, true); 515 516 /* 517 * Disconnect from SPI manager 518 */ 519 if ((rc = SPI_finish()) != SPI_OK_FINISH) 520 elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc)); 521 } 522 523 ReleaseSysCache(tuple); 524 525 PG_RETURN_VOID(); 526 } 527