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