Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit31a8918

Browse files
committed
Improve pl/pgsql to support composite-type expressions in RETURN.
For some reason lost in the mists of prehistory, RETURN was only coded toallow a simple reference to a composite variable when the function's returntype is composite. Allow an expression instead, while preserving theefficiency of the original code path in the case where the expression isindeed just a composite variable's name. Likewise for RETURN NEXT.As is true in various other places, the supplied expression must yieldexactly the number and data types of the required columns. There was somediscussion of relaxing that for pl/pgsql, but no consensus yet, so thispatch doesn't address that.Asif Rehman, reviewed by Pavel Stehule
1 parentda07a1e commit31a8918

File tree

7 files changed

+508
-138
lines changed

7 files changed

+508
-138
lines changed

‎doc/src/sgml/plpgsql.sgml

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1571,11 +1571,11 @@ RETURN <replaceable>expression</replaceable>;
15711571
</para>
15721572

15731573
<para>
1574-
When returninga scalar type,any expression can be used. The
1575-
expression's result willbeautomaticallycast into the
1576-
function'sreturntype as described for assignments. To return a
1577-
composite (row) value, you must write a record or row variable
1578-
as the <replaceable>expression</replaceable>.
1574+
In a function that returnsa scalar type,the expression's result will
1575+
automaticallybe cast into the function's return type as described for
1576+
assignments. But toreturna composite (row) value, you must write an
1577+
expression delivering exactly the requested column set. This may
1578+
require use of explicit casting.
15791579
</para>
15801580

15811581
<para>
@@ -1600,6 +1600,20 @@ RETURN <replaceable>expression</replaceable>;
16001600
however. In those cases a <command>RETURN</command> statement is
16011601
automatically executed if the top-level block finishes.
16021602
</para>
1603+
1604+
<para>
1605+
Some examples:
1606+
1607+
<programlisting>
1608+
-- functions returning a scalar type
1609+
RETURN 1 + 2;
1610+
RETURN scalar_var;
1611+
1612+
-- functions returning a composite type
1613+
RETURN composite_type_var;
1614+
RETURN (1, 2, 'three'::text); -- must cast columns to correct types
1615+
</programlisting>
1616+
</para>
16031617
</sect3>
16041618

16051619
<sect3>

‎src/pl/plpgsql/src/pl_exec.c

Lines changed: 187 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,12 @@ static void exec_move_row(PLpgSQL_execstate *estate,
189189
staticHeapTuplemake_tuple_from_row(PLpgSQL_execstate*estate,
190190
PLpgSQL_row*row,
191191
TupleDesctupdesc);
192+
staticHeapTupleget_tuple_from_datum(Datumvalue);
193+
staticTupleDescget_tupdesc_from_datum(Datumvalue);
194+
staticvoidexec_move_row_from_datum(PLpgSQL_execstate*estate,
195+
PLpgSQL_rec*rec,
196+
PLpgSQL_row*row,
197+
Datumvalue);
192198
staticchar*convert_value_to_string(PLpgSQL_execstate*estate,
193199
Datumvalue,Oidvaltype);
194200
staticDatumexec_cast_value(PLpgSQL_execstate*estate,
@@ -275,24 +281,9 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo)
275281

276282
if (!fcinfo->argnull[i])
277283
{
278-
HeapTupleHeadertd;
279-
OidtupType;
280-
int32tupTypmod;
281-
TupleDesctupdesc;
282-
HeapTupleDatatmptup;
283-
284-
td=DatumGetHeapTupleHeader(fcinfo->arg[i]);
285-
/* Extract rowtype info and find a tupdesc */
286-
tupType=HeapTupleHeaderGetTypeId(td);
287-
tupTypmod=HeapTupleHeaderGetTypMod(td);
288-
tupdesc=lookup_rowtype_tupdesc(tupType,tupTypmod);
289-
/* Build a temporary HeapTuple control structure */
290-
tmptup.t_len=HeapTupleHeaderGetDatumLength(td);
291-
ItemPointerSetInvalid(&(tmptup.t_self));
292-
tmptup.t_tableOid=InvalidOid;
293-
tmptup.t_data=td;
294-
exec_move_row(&estate,NULL,row,&tmptup,tupdesc);
295-
ReleaseTupleDesc(tupdesc);
284+
/* Assign row value from composite datum */
285+
exec_move_row_from_datum(&estate,NULL,row,
286+
fcinfo->arg[i]);
296287
}
297288
else
298289
{
@@ -2396,6 +2387,10 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
23962387
estate->rettupdesc=NULL;
23972388
estate->retisnull= true;
23982389

2390+
/*
2391+
* This special-case path covers record/row variables in fn_retistuple
2392+
* functions, as well as functions with one or more OUT parameters.
2393+
*/
23992394
if (stmt->retvarno >=0)
24002395
{
24012396
PLpgSQL_datum*retvar=estate->datums[stmt->retvarno];
@@ -2449,22 +2444,26 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
24492444

24502445
if (stmt->expr!=NULL)
24512446
{
2452-
if (estate->retistuple)
2453-
{
2454-
exec_run_select(estate,stmt->expr,1,NULL);
2455-
if (estate->eval_processed>0)
2456-
{
2457-
estate->retval=PointerGetDatum(estate->eval_tuptable->vals[0]);
2458-
estate->rettupdesc=estate->eval_tuptable->tupdesc;
2459-
estate->retisnull= false;
2460-
}
2461-
}
2462-
else
2447+
estate->retval=exec_eval_expr(estate,stmt->expr,
2448+
&(estate->retisnull),
2449+
&(estate->rettype));
2450+
2451+
if (estate->retistuple&& !estate->retisnull)
24632452
{
2464-
/* Normal case for scalar results */
2465-
estate->retval=exec_eval_expr(estate,stmt->expr,
2466-
&(estate->retisnull),
2467-
&(estate->rettype));
2453+
/* Convert composite datum to a HeapTuple and TupleDesc */
2454+
HeapTupletuple;
2455+
TupleDesctupdesc;
2456+
2457+
/* Source must be of RECORD or composite type */
2458+
if (!type_is_rowtype(estate->rettype))
2459+
ereport(ERROR,
2460+
(errcode(ERRCODE_DATATYPE_MISMATCH),
2461+
errmsg("cannot return non-composite value from function returning composite type")));
2462+
tuple=get_tuple_from_datum(estate->retval);
2463+
tupdesc=get_tupdesc_from_datum(estate->retval);
2464+
estate->retval=PointerGetDatum(tuple);
2465+
estate->rettupdesc=CreateTupleDescCopy(tupdesc);
2466+
ReleaseTupleDesc(tupdesc);
24682467
}
24692468

24702469
returnPLPGSQL_RC_RETURN;
@@ -2473,8 +2472,7 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
24732472
/*
24742473
* Special hack for function returning VOID: instead of NULL, return a
24752474
* non-null VOID value. This is of dubious importance but is kept for
2476-
* backwards compatibility. Note that the only other way to get here is
2477-
* to have written "RETURN NULL" in a function returning tuple.
2475+
* backwards compatibility.
24782476
*/
24792477
if (estate->fn_rettype==VOIDOID)
24802478
{
@@ -2513,6 +2511,10 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
25132511
tupdesc=estate->rettupdesc;
25142512
natts=tupdesc->natts;
25152513

2514+
/*
2515+
* This special-case path covers record/row variables in fn_retistuple
2516+
* functions, as well as functions with one or more OUT parameters.
2517+
*/
25162518
if (stmt->retvarno >=0)
25172519
{
25182520
PLpgSQL_datum*retvar=estate->datums[stmt->retvarno];
@@ -2593,26 +2595,77 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
25932595
boolisNull;
25942596
Oidrettype;
25952597

2596-
if (natts!=1)
2597-
ereport(ERROR,
2598-
(errcode(ERRCODE_DATATYPE_MISMATCH),
2599-
errmsg("wrong result type supplied in RETURN NEXT")));
2600-
26012598
retval=exec_eval_expr(estate,
26022599
stmt->expr,
26032600
&isNull,
26042601
&rettype);
26052602

2606-
/* coerce type if needed */
2607-
retval=exec_simple_cast_value(estate,
2608-
retval,
2609-
rettype,
2610-
tupdesc->attrs[0]->atttypid,
2611-
tupdesc->attrs[0]->atttypmod,
2612-
isNull);
2603+
if (estate->retistuple)
2604+
{
2605+
/* Expression should be of RECORD or composite type */
2606+
if (!isNull)
2607+
{
2608+
TupleDescretvaldesc;
2609+
TupleConversionMap*tupmap;
2610+
2611+
if (!type_is_rowtype(rettype))
2612+
ereport(ERROR,
2613+
(errcode(ERRCODE_DATATYPE_MISMATCH),
2614+
errmsg("cannot return non-composite value from function returning composite type")));
26132615

2614-
tuplestore_putvalues(estate->tuple_store,tupdesc,
2615-
&retval,&isNull);
2616+
tuple=get_tuple_from_datum(retval);
2617+
free_tuple= true;/* tuple is always freshly palloc'd */
2618+
2619+
/* it might need conversion */
2620+
retvaldesc=get_tupdesc_from_datum(retval);
2621+
tupmap=convert_tuples_by_position(retvaldesc,tupdesc,
2622+
gettext_noop("returned record type does not match expected record type"));
2623+
if (tupmap)
2624+
{
2625+
HeapTuplenewtuple;
2626+
2627+
newtuple=do_convert_tuple(tuple,tupmap);
2628+
free_conversion_map(tupmap);
2629+
heap_freetuple(tuple);
2630+
tuple=newtuple;
2631+
}
2632+
ReleaseTupleDesc(retvaldesc);
2633+
/* tuple will be stored into tuplestore below */
2634+
}
2635+
else
2636+
{
2637+
/* Composite NULL --- store a row of nulls */
2638+
Datum*nulldatums;
2639+
bool*nullflags;
2640+
2641+
nulldatums= (Datum*)palloc0(natts*sizeof(Datum));
2642+
nullflags= (bool*)palloc(natts*sizeof(bool));
2643+
memset(nullflags, true,natts*sizeof(bool));
2644+
tuplestore_putvalues(estate->tuple_store,tupdesc,
2645+
nulldatums,nullflags);
2646+
pfree(nulldatums);
2647+
pfree(nullflags);
2648+
}
2649+
}
2650+
else
2651+
{
2652+
/* Simple scalar result */
2653+
if (natts!=1)
2654+
ereport(ERROR,
2655+
(errcode(ERRCODE_DATATYPE_MISMATCH),
2656+
errmsg("wrong result type supplied in RETURN NEXT")));
2657+
2658+
/* coerce type if needed */
2659+
retval=exec_simple_cast_value(estate,
2660+
retval,
2661+
rettype,
2662+
tupdesc->attrs[0]->atttypid,
2663+
tupdesc->attrs[0]->atttypmod,
2664+
isNull);
2665+
2666+
tuplestore_putvalues(estate->tuple_store,tupdesc,
2667+
&retval,&isNull);
2668+
}
26162669
}
26172670
else
26182671
{
@@ -3901,30 +3954,12 @@ exec_assign_value(PLpgSQL_execstate *estate,
39013954
}
39023955
else
39033956
{
3904-
HeapTupleHeadertd;
3905-
OidtupType;
3906-
int32tupTypmod;
3907-
TupleDesctupdesc;
3908-
HeapTupleDatatmptup;
3909-
39103957
/* Source must be of RECORD or composite type */
39113958
if (!type_is_rowtype(valtype))
39123959
ereport(ERROR,
39133960
(errcode(ERRCODE_DATATYPE_MISMATCH),
39143961
errmsg("cannot assign non-composite value to a row variable")));
3915-
/* Source is a tuple Datum, so safe to do this: */
3916-
td=DatumGetHeapTupleHeader(value);
3917-
/* Extract rowtype info and find a tupdesc */
3918-
tupType=HeapTupleHeaderGetTypeId(td);
3919-
tupTypmod=HeapTupleHeaderGetTypMod(td);
3920-
tupdesc=lookup_rowtype_tupdesc(tupType,tupTypmod);
3921-
/* Build a temporary HeapTuple control structure */
3922-
tmptup.t_len=HeapTupleHeaderGetDatumLength(td);
3923-
ItemPointerSetInvalid(&(tmptup.t_self));
3924-
tmptup.t_tableOid=InvalidOid;
3925-
tmptup.t_data=td;
3926-
exec_move_row(estate,NULL,row,&tmptup,tupdesc);
3927-
ReleaseTupleDesc(tupdesc);
3962+
exec_move_row_from_datum(estate,NULL,row,value);
39283963
}
39293964
break;
39303965
}
@@ -3943,31 +3978,12 @@ exec_assign_value(PLpgSQL_execstate *estate,
39433978
}
39443979
else
39453980
{
3946-
HeapTupleHeadertd;
3947-
OidtupType;
3948-
int32tupTypmod;
3949-
TupleDesctupdesc;
3950-
HeapTupleDatatmptup;
3951-
39523981
/* Source must be of RECORD or composite type */
39533982
if (!type_is_rowtype(valtype))
39543983
ereport(ERROR,
39553984
(errcode(ERRCODE_DATATYPE_MISMATCH),
39563985
errmsg("cannot assign non-composite value to a record variable")));
3957-
3958-
/* Source is a tuple Datum, so safe to do this: */
3959-
td=DatumGetHeapTupleHeader(value);
3960-
/* Extract rowtype info and find a tupdesc */
3961-
tupType=HeapTupleHeaderGetTypeId(td);
3962-
tupTypmod=HeapTupleHeaderGetTypMod(td);
3963-
tupdesc=lookup_rowtype_tupdesc(tupType,tupTypmod);
3964-
/* Build a temporary HeapTuple control structure */
3965-
tmptup.t_len=HeapTupleHeaderGetDatumLength(td);
3966-
ItemPointerSetInvalid(&(tmptup.t_self));
3967-
tmptup.t_tableOid=InvalidOid;
3968-
tmptup.t_data=td;
3969-
exec_move_row(estate,rec,NULL,&tmptup,tupdesc);
3970-
ReleaseTupleDesc(tupdesc);
3986+
exec_move_row_from_datum(estate,rec,NULL,value);
39713987
}
39723988
break;
39733989
}
@@ -5416,6 +5432,89 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
54165432
returntuple;
54175433
}
54185434

5435+
/* ----------
5436+
* get_tuple_from_datumextract a tuple from a composite Datum
5437+
*
5438+
* Returns a freshly palloc'd HeapTuple.
5439+
*
5440+
* Note: it's caller's responsibility to be sure value is of composite type.
5441+
* ----------
5442+
*/
5443+
staticHeapTuple
5444+
get_tuple_from_datum(Datumvalue)
5445+
{
5446+
HeapTupleHeadertd=DatumGetHeapTupleHeader(value);
5447+
HeapTupleDatatmptup;
5448+
5449+
/* Build a temporary HeapTuple control structure */
5450+
tmptup.t_len=HeapTupleHeaderGetDatumLength(td);
5451+
ItemPointerSetInvalid(&(tmptup.t_self));
5452+
tmptup.t_tableOid=InvalidOid;
5453+
tmptup.t_data=td;
5454+
5455+
/* Build a copy and return it */
5456+
returnheap_copytuple(&tmptup);
5457+
}
5458+
5459+
/* ----------
5460+
* get_tupdesc_from_datumget a tuple descriptor for a composite Datum
5461+
*
5462+
* Returns a pointer to the TupleDesc of the tuple's rowtype.
5463+
* Caller is responsible for calling ReleaseTupleDesc when done with it.
5464+
*
5465+
* Note: it's caller's responsibility to be sure value is of composite type.
5466+
* ----------
5467+
*/
5468+
staticTupleDesc
5469+
get_tupdesc_from_datum(Datumvalue)
5470+
{
5471+
HeapTupleHeadertd=DatumGetHeapTupleHeader(value);
5472+
OidtupType;
5473+
int32tupTypmod;
5474+
5475+
/* Extract rowtype info and find a tupdesc */
5476+
tupType=HeapTupleHeaderGetTypeId(td);
5477+
tupTypmod=HeapTupleHeaderGetTypMod(td);
5478+
returnlookup_rowtype_tupdesc(tupType,tupTypmod);
5479+
}
5480+
5481+
/* ----------
5482+
* exec_move_row_from_datumMove a composite Datum into a record or row
5483+
*
5484+
* This is equivalent to get_tuple_from_datum() followed by exec_move_row(),
5485+
* but we avoid constructing an intermediate physical copy of the tuple.
5486+
* ----------
5487+
*/
5488+
staticvoid
5489+
exec_move_row_from_datum(PLpgSQL_execstate*estate,
5490+
PLpgSQL_rec*rec,
5491+
PLpgSQL_row*row,
5492+
Datumvalue)
5493+
{
5494+
HeapTupleHeadertd=DatumGetHeapTupleHeader(value);
5495+
OidtupType;
5496+
int32tupTypmod;
5497+
TupleDesctupdesc;
5498+
HeapTupleDatatmptup;
5499+
5500+
/* Extract rowtype info and find a tupdesc */
5501+
tupType=HeapTupleHeaderGetTypeId(td);
5502+
tupTypmod=HeapTupleHeaderGetTypMod(td);
5503+
tupdesc=lookup_rowtype_tupdesc(tupType,tupTypmod);
5504+
5505+
/* Build a temporary HeapTuple control structure */
5506+
tmptup.t_len=HeapTupleHeaderGetDatumLength(td);
5507+
ItemPointerSetInvalid(&(tmptup.t_self));
5508+
tmptup.t_tableOid=InvalidOid;
5509+
tmptup.t_data=td;
5510+
5511+
/* Do the move */
5512+
exec_move_row(estate,rec,row,&tmptup,tupdesc);
5513+
5514+
/* Release tupdesc usage count */
5515+
ReleaseTupleDesc(tupdesc);
5516+
}
5517+
54195518
/* ----------
54205519
* convert_value_to_stringConvert a non-null Datum to C string
54215520
*

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp