* assign_simple_var --- assign a new value to any VAR datum. * * This should be the only mechanism for assignment to simple variables, * lest we do the release of the old value incorrectly (not to mention * the detoasting business). */
| 8395 | * the detoasting business). |
| 8396 | */ |
| 8397 | static void |
| 8398 | assign_simple_var(PLpgSQL_execstate *estate, PLpgSQL_var *var, |
| 8399 | Datum newvalue, bool isnull, bool freeable) |
| 8400 | { |
| 8401 | Assert(var->dtype == PLPGSQL_DTYPE_VAR || |
| 8402 | var->dtype == PLPGSQL_DTYPE_PROMISE); |
| 8403 | |
| 8404 | /* |
| 8405 | * In non-atomic contexts, we do not want to store TOAST pointers in |
| 8406 | * variables, because such pointers might become stale after a commit. |
| 8407 | * Forcibly detoast in such cases. We don't want to detoast (flatten) |
| 8408 | * expanded objects, however; those should be OK across a transaction |
| 8409 | * boundary since they're just memory-resident objects. (Elsewhere in |
| 8410 | * this module, operations on expanded records likewise need to request |
| 8411 | * detoasting of record fields when !estate->atomic. Expanded arrays are |
| 8412 | * not a problem since all array entries are always detoasted.) |
| 8413 | */ |
| 8414 | if (!estate->atomic && !isnull && var->datatype->typlen == -1 && |
| 8415 | VARATT_IS_EXTERNAL_NON_EXPANDED(DatumGetPointer(newvalue))) |
| 8416 | { |
| 8417 | MemoryContext oldcxt; |
| 8418 | Datum detoasted; |
| 8419 | |
| 8420 | /* |
| 8421 | * Do the detoasting in the eval_mcontext to avoid long-term leakage |
| 8422 | * of whatever memory toast fetching might leak. Then we have to copy |
| 8423 | * the detoasted datum to the function's main context, which is a |
| 8424 | * pain, but there's little choice. |
| 8425 | */ |
| 8426 | oldcxt = MemoryContextSwitchTo(get_eval_mcontext(estate)); |
| 8427 | detoasted = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newvalue))); |
| 8428 | MemoryContextSwitchTo(oldcxt); |
| 8429 | /* Now's a good time to not leak the input value if it's freeable */ |
| 8430 | if (freeable) |
| 8431 | pfree(DatumGetPointer(newvalue)); |
| 8432 | /* Once we copy the value, it's definitely freeable */ |
| 8433 | newvalue = datumCopy(detoasted, false, -1); |
| 8434 | freeable = true; |
| 8435 | /* Can't clean up eval_mcontext here, but it'll happen before long */ |
| 8436 | } |
| 8437 | |
| 8438 | /* Free the old value if needed */ |
| 8439 | if (var->freeval) |
| 8440 | { |
| 8441 | if (DatumIsReadWriteExpandedObject(var->value, |
| 8442 | var->isnull, |
| 8443 | var->datatype->typlen)) |
| 8444 | DeleteExpandedObject(var->value); |
| 8445 | else |
| 8446 | pfree(DatumGetPointer(var->value)); |
| 8447 | } |
| 8448 | /* Assign new value to datum */ |
| 8449 | var->value = newvalue; |
| 8450 | var->isnull = isnull; |
| 8451 | var->freeval = freeable; |
| 8452 | |
| 8453 | /* |
| 8454 | * If it's a promise variable, then either we just assigned the promised |
no test coverage detected