* exec_move_row Move one tuple's values into a record or row * * tup and tupdesc may both be NULL if we're just assigning an indeterminate * composite NULL to the target. Alternatively, can have tup be NULL and * tupdesc not NULL, in which case we assign a row of NULLs to the target. * * Since this uses the mcontext for workspace, caller should eventually call * exec_eval_cleanup to prev
| 6674 | * exec_eval_cleanup to prevent long-term memory leaks. |
| 6675 | */ |
| 6676 | static void |
| 6677 | exec_move_row(PLpgSQL_execstate *estate, |
| 6678 | PLpgSQL_variable *target, |
| 6679 | HeapTuple tup, TupleDesc tupdesc) |
| 6680 | { |
| 6681 | ExpandedRecordHeader *newerh = NULL; |
| 6682 | |
| 6683 | /* |
| 6684 | * If target is RECORD, we may be able to avoid field-by-field processing. |
| 6685 | */ |
| 6686 | if (target->dtype == PLPGSQL_DTYPE_REC) |
| 6687 | { |
| 6688 | PLpgSQL_rec *rec = (PLpgSQL_rec *) target; |
| 6689 | |
| 6690 | /* |
| 6691 | * If we have no source tupdesc, just set the record variable to NULL. |
| 6692 | * (If we have a source tupdesc but not a tuple, we'll set the |
| 6693 | * variable to a row of nulls, instead. This is odd perhaps, but |
| 6694 | * backwards compatible.) |
| 6695 | */ |
| 6696 | if (tupdesc == NULL) |
| 6697 | { |
| 6698 | if (rec->datatype && |
| 6699 | rec->datatype->typtype == TYPTYPE_DOMAIN) |
| 6700 | { |
| 6701 | /* |
| 6702 | * If it's a composite domain, NULL might not be a legal |
| 6703 | * value, so we instead need to make an empty expanded record |
| 6704 | * and ensure that domain type checking gets done. If there |
| 6705 | * is already an expanded record, piggyback on its lookups. |
| 6706 | */ |
| 6707 | newerh = make_expanded_record_for_rec(estate, rec, |
| 6708 | NULL, rec->erh); |
| 6709 | expanded_record_set_tuple(newerh, NULL, false, false); |
| 6710 | assign_record_var(estate, rec, newerh); |
| 6711 | } |
| 6712 | else |
| 6713 | { |
| 6714 | /* Just clear it to NULL */ |
| 6715 | if (rec->erh) |
| 6716 | DeleteExpandedObject(ExpandedRecordGetDatum(rec->erh)); |
| 6717 | rec->erh = NULL; |
| 6718 | } |
| 6719 | return; |
| 6720 | } |
| 6721 | |
| 6722 | /* |
| 6723 | * Build a new expanded record with appropriate tupdesc. |
| 6724 | */ |
| 6725 | newerh = make_expanded_record_for_rec(estate, rec, tupdesc, NULL); |
| 6726 | |
| 6727 | /* |
| 6728 | * If the rowtypes match, or if we have no tuple anyway, we can |
| 6729 | * complete the assignment without field-by-field processing. |
| 6730 | * |
| 6731 | * The tests here are ordered more or less in order of cheapness. We |
| 6732 | * can easily detect it will work if the target is declared RECORD or |
| 6733 | * has the same typeid as the source. But when assigning from a query |
no test coverage detected