diff --git a/src/pl/plpgsql/src/expected/plpgsql_record.out b/src/pl/plpgsql/src/expected/plpgsql_record.out index cc36231aef5..b9d76b5c6b7 100644 --- a/src/pl/plpgsql/src/expected/plpgsql_record.out +++ b/src/pl/plpgsql/src/expected/plpgsql_record.out @@ -654,3 +654,16 @@ NOTICE: processing row 2 NOTICE: processing row 3 ERROR: value for domain ordered_texts violates check constraint "ordered_texts_check" CONTEXT: PL/pgSQL function inline_code_block line 8 at assignment +-- check coercion of a record result to named-composite function output type +create function compresult(int8) returns two_int8s language plpgsql as +$$ declare r record; begin r := row($1,$1); return r; end $$; +create table two_int8s_tab (f1 two_int8s); +insert into two_int8s_tab values (compresult(42)); +-- reconnect so we lose any local knowledge of anonymous record types +\c - +table two_int8s_tab; + f1 +--------- + (42,42) +(1 row) + diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 2f49754c5f4..43027e6ca87 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -808,6 +808,31 @@ coerce_function_result_tuple(PLpgSQL_execstate *estate, TupleDesc tupdesc) estate->retval = PointerGetDatum(SPI_returntuple(rettup, tupdesc)); /* no need to free map, we're about to return anyway */ } + else if (!(tupdesc->tdtypeid == erh->er_decltypeid || + (tupdesc->tdtypeid == RECORDOID && + !ExpandedRecordIsDomain(erh)))) + { + /* + * The expanded record has the right physical tupdesc, but the + * wrong type ID. (Typically, the expanded record is RECORDOID + * but the function is declared to return a named composite type. + * As in exec_move_row_from_datum, we don't allow returning a + * composite-domain record from a function declared to return + * RECORD.) So we must flatten the record to a tuple datum and + * overwrite its type fields with the right thing. spi.c doesn't + * provide any easy way to deal with this case, so we end up + * duplicating the guts of datumCopy() :-( + */ + Size resultsize; + HeapTupleHeader tuphdr; + + resultsize = EOH_get_flat_size(&erh->hdr); + tuphdr = (HeapTupleHeader) SPI_palloc(resultsize); + EOH_flatten_into(&erh->hdr, (void *) tuphdr, resultsize); + HeapTupleHeaderSetTypeId(tuphdr, tupdesc->tdtypeid); + HeapTupleHeaderSetTypMod(tuphdr, tupdesc->tdtypmod); + estate->retval = PointerGetDatum(tuphdr); + } else { /* diff --git a/src/pl/plpgsql/src/sql/plpgsql_record.sql b/src/pl/plpgsql/src/sql/plpgsql_record.sql index 88333d45e14..46c6178c73f 100644 --- a/src/pl/plpgsql/src/sql/plpgsql_record.sql +++ b/src/pl/plpgsql/src/sql/plpgsql_record.sql @@ -441,3 +441,13 @@ begin d.f2 := r.b; end loop; end$$; + +-- check coercion of a record result to named-composite function output type +create function compresult(int8) returns two_int8s language plpgsql as +$$ declare r record; begin r := row($1,$1); return r; end $$; + +create table two_int8s_tab (f1 two_int8s); +insert into two_int8s_tab values (compresult(42)); +-- reconnect so we lose any local knowledge of anonymous record types +\c - +table two_int8s_tab;