From 0053cebea5f3803e853accafb1810afd8cb02b0e Mon Sep 17 00:00:00 2001 From: Tom Lane <tgl@sss.pgh.pa.us> Date: Mon, 5 Nov 2001 19:41:56 +0000 Subject: [PATCH] Fix coredump in plpgsql when trying to return a rowtype result. Need to return a TupleTableSlot, not just a bare tuple. --- doc/src/sgml/spi.sgml | 130 +++++++++++++++++++++++++++++++++++ src/backend/executor/spi.c | 36 +++++++++- src/include/executor/spi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 71 ++++++++++--------- 4 files changed, 206 insertions(+), 35 deletions(-) diff --git a/doc/src/sgml/spi.sgml b/doc/src/sgml/spi.sgml index 23ae5dd49be..5f9aa5b6c80 100644 --- a/doc/src/sgml/spi.sgml +++ b/doc/src/sgml/spi.sgml @@ -1272,6 +1272,135 @@ TBD <!-- *********************************************** --> <!-- *********************************************** --> +<REFENTRY ID="SPI-SPICOPYTUPLEINTOSLOT"> +<REFMETA> +<REFENTRYTITLE>SPI_copytupleintoslot</REFENTRYTITLE> +<REFMISCINFO>SPI - Tuple and Descriptor Copy</REFMISCINFO> +</REFMETA> +<REFNAMEDIV> +<REFNAME>SPI_copytupleintoslot +</REFNAME> +<REFPURPOSE> +Makes copy of tuple and descriptor in upper Executor context +</REFPURPOSE> +<INDEXTERM ID="IX-SPI-SPICOPYTUPLEINTOSLOT-1"><PRIMARY>SPI</PRIMARY><SECONDARY>copying tuples</SECONDARY></INDEXTERM> +<INDEXTERM ID="IX-SPI-SPICOPYTUPLEINTOSLOT-2"><PRIMARY>SPI_copytupleintoslot</PRIMARY></INDEXTERM> +</REFNAMEDIV> +<REFSYNOPSISDIV> +<REFSYNOPSISDIVINFO> +<DATE>1997-12-24</DATE> +</REFSYNOPSISDIVINFO> +<SYNOPSIS> +SPI_copytupleintoslot(<REPLACEABLE CLASS="PARAMETER">tuple</REPLACEABLE>, <REPLACEABLE CLASS="PARAMETER">tupdesc</REPLACEABLE>) +</SYNOPSIS> + +<REFSECT2 ID="R2-SPI-SPICOPYTUPLEINTOSLOT-1"> +<REFSECT2INFO> +<DATE>1997-12-24</DATE> +</REFSECT2INFO> +<TITLE>Inputs +</TITLE> +<VARIABLELIST> +<VARLISTENTRY> +<TERM> +HeapTuple <REPLACEABLE CLASS="PARAMETER">tuple</REPLACEABLE> +</TERM> +<LISTITEM> +<PARA> +Input tuple to be copied +</PARA> +</LISTITEM> +</VARLISTENTRY> +<VARLISTENTRY> +<TERM> +TupleDesc <REPLACEABLE CLASS="PARAMETER">tupdesc</REPLACEABLE> +</TERM> +<LISTITEM> +<PARA> +Input tuple descriptor to be copied +</PARA> +</LISTITEM> +</VARLISTENTRY> +</VARIABLELIST> +</REFSECT2> + +<REFSECT2 ID="R2-SPI-SPICOPYTUPLEINTOSLOT-2"> +<REFSECT2INFO> +<DATE>1997-12-24</DATE> +</REFSECT2INFO> +<TITLE>Outputs +</TITLE> +<VARIABLELIST> +<VARLISTENTRY> +<TERM> +TupleTableSlot * +</TERM> +<LISTITEM> +<PARA> +Tuple slot containing copied tuple and descriptor +<SimpleList> +<Member> + <ReturnValue>non-NULL</ReturnValue> + if <REPLACEABLE CLASS="PARAMETER">tuple</REPLACEABLE> + and <REPLACEABLE CLASS="PARAMETER">tupdesc</REPLACEABLE> + are not NULL and the copy was successful +</Member> +<Member> + <ReturnValue>NULL</ReturnValue> + only if <REPLACEABLE CLASS="PARAMETER">tuple</REPLACEABLE> + or <REPLACEABLE CLASS="PARAMETER">tupdesc</REPLACEABLE> + is NULL +</Member> +</SimpleList> +</para> +</LISTITEM> +</VARLISTENTRY> +</VARIABLELIST> +</REFSECT2> +</REFSYNOPSISDIV> + +<REFSECT1 ID="R1-SPI-SPICOPYTUPLEINTOSLOT-1"> +<REFSECT1INFO> +<DATE>1997-12-24</DATE> +</REFSECT1INFO> +<TITLE>Description +</TITLE> +<PARA> +<FUNCTION>SPI_copytupleintoslot</FUNCTION> + makes a copy of tuple in upper Executor context, returning it in the + form of a filled-in TupleTableSlot. + See the section on Memory Management. +</PARA> +</REFSECT1> +<REFSECT1 ID="R1-SPI-SPICOPYTUPLEINTOSLOT-2"> +<TITLE>Usage +</TITLE> +<Para> +TBD +</PARA> +</REFSECT1> +<!-- +<REFSECT1 ID="R1-SPI-SPICOPYTUPLEINTOSLOT-3"> +<TITLE>Algorithm +</TITLE> +<PARA> +</PARA> +</REFSECT1> +--> +<!-- +<REFSECT1 ID="R1-SPI-SPICOPYTUPLEINTOSLOT-4"> +<TITLE>Structures +</TITLE> +<PARA>None +</PARA> +</REFSECT1> +--> +</REFENTRY> + +<!-- *********************************************** --> +<!-- *********************************************** --> +<!-- *********************************************** --> + <REFENTRY ID="SPI-SPIMODIFYTUPLE"> <REFMETA> <REFENTRYTITLE>SPI_modifytuple</REFENTRYTITLE> @@ -2695,6 +2824,7 @@ made in this context. <Function>palloc</Function>/<Function>repalloc</Function> or by SPI utility functions (except for <Function>SPI_copytuple</Function>, <Function>SPI_copytupledesc</Function>, +<Function>SPI_copytupleintoslot</Function>, <Function>SPI_modifytuple</Function>, <Function>SPI_palloc</Function> and <Function>SPI_repalloc</Function>) are made in this context. diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index e4c2dac40db..428a4cb7d3f 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.60 2001/10/25 05:49:29 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.61 2001/11/05 19:41:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -360,6 +360,40 @@ SPI_copytupledesc(TupleDesc tupdesc) return ctupdesc; } +TupleTableSlot * +SPI_copytupleintoslot(HeapTuple tuple, TupleDesc tupdesc) +{ + MemoryContext oldcxt = NULL; + TupleTableSlot *cslot; + HeapTuple ctuple; + TupleDesc ctupdesc; + + if (tuple == NULL || tupdesc == NULL) + { + SPI_result = SPI_ERROR_ARGUMENT; + return NULL; + } + + if (_SPI_curid + 1 == _SPI_connected) /* connected */ + { + if (_SPI_current != &(_SPI_stack[_SPI_curid + 1])) + elog(FATAL, "SPI: stack corrupted"); + oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt); + } + + ctuple = heap_copytuple(tuple); + ctupdesc = CreateTupleDescCopy(tupdesc); + + cslot = MakeTupleTableSlot(); + ExecSetSlotDescriptor(cslot, ctupdesc, true); + cslot = ExecStoreTuple(ctuple, cslot, InvalidBuffer, true); + + if (oldcxt) + MemoryContextSwitchTo(oldcxt); + + return cslot; +} + HeapTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum, Datum *Values, char *Nulls) diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h index fa2ea2f361f..2ea6e9bfc8f 100644 --- a/src/include/executor/spi.h +++ b/src/include/executor/spi.h @@ -2,7 +2,7 @@ * * spi.h * - * $Id: spi.h,v 1.31 2001/11/05 17:46:33 momjian Exp $ + * $Id: spi.h,v 1.32 2001/11/05 19:41:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -89,6 +89,8 @@ extern int SPI_freeplan(void *plan); extern HeapTuple SPI_copytuple(HeapTuple tuple); extern TupleDesc SPI_copytupledesc(TupleDesc tupdesc); +extern TupleTableSlot *SPI_copytupleintoslot(HeapTuple tuple, + TupleDesc tupdesc); extern HeapTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum, Datum *Values, char *Nulls); extern int SPI_fnumber(TupleDesc tupdesc, char *fname); diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 9ad6d5dece3..b0385bf1a6f 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.48 2001/10/25 05:50:20 momjian Exp $ + * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.49 2001/11/05 19:41:56 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -400,32 +400,43 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo) fcinfo->isnull = estate.retisnull; - if (!estate.retistuple) + if (!estate.retisnull) { - estate.retval = exec_cast_value(estate.retval, estate.rettype, - func->fn_rettype, - &(func->fn_retinput), - func->fn_rettypelem, - -1, - &fcinfo->isnull); - - /* - * If the functions return type isn't by value, copy the value - * into upper executor memory context. - */ - if (!fcinfo->isnull && !func->fn_retbyval) + if (estate.retistuple) + { + /* Copy tuple to upper executor memory */ + /* Here we need to return a TupleTableSlot not just a tuple */ + estate.retval = (Datum) + SPI_copytupleintoslot((HeapTuple) (estate.retval), + estate.rettupdesc); + } + else { - int len; - Datum tmp; + /* Cast value to proper type */ + estate.retval = exec_cast_value(estate.retval, estate.rettype, + func->fn_rettype, + &(func->fn_retinput), + func->fn_rettypelem, + -1, + &fcinfo->isnull); + /* + * If the functions return type isn't by value, copy the value + * into upper executor memory context. + */ + if (!fcinfo->isnull && !func->fn_retbyval) + { + int len; + Datum tmp; - if (func->fn_rettyplen < 0) - len = VARSIZE(estate.retval); - else - len = func->fn_rettyplen; + if (func->fn_rettyplen < 0) + len = VARSIZE(estate.retval); + else + len = func->fn_rettyplen; - tmp = (Datum) SPI_palloc(len); - memcpy((void *) tmp, (void *) estate.retval, len); - estate.retval = tmp; + tmp = (Datum) SPI_palloc(len); + memcpy((void *) tmp, (void *) estate.retval, len); + estate.retval = tmp; + } } } @@ -1619,8 +1630,8 @@ exec_stmt_return(PLpgSQL_execstate * estate, PLpgSQL_stmt_return * stmt) if (HeapTupleIsValid(rec->tup)) { - estate->retval = (Datum) SPI_copytuple(rec->tup); - estate->rettupdesc = SPI_copytupledesc(rec->tupdesc); + estate->retval = (Datum) rec->tup; + estate->rettupdesc = rec->tupdesc; estate->retisnull = false; } return PLPGSQL_RC_RETURN; @@ -1631,16 +1642,10 @@ exec_stmt_return(PLpgSQL_execstate * estate, PLpgSQL_stmt_return * stmt) exec_run_select(estate, stmt->expr, 1, NULL); if (estate->eval_processed > 0) { - estate->retval = (Datum) SPI_copytuple(estate->eval_tuptable->vals[0]); - estate->rettupdesc = SPI_copytupledesc(estate->eval_tuptable->tupdesc); + estate->retval = (Datum) estate->eval_tuptable->vals[0]; + estate->rettupdesc = estate->eval_tuptable->tupdesc; estate->retisnull = false; } - - /* - * Okay to clean up here, since we already copied result tuple - * to upper executor. - */ - exec_eval_cleanup(estate); } return PLPGSQL_RC_RETURN; } -- GitLab