On Sun, Mar 6, 2022 at 9:29 PM Michael Paquier <mich...@paquier.xyz> wrote:
>
> On Wed, Mar 02, 2022 at 03:43:17PM +0900, Michael Paquier wrote:
> > This is actually setting up a function in the context of a single call
> > where we fill the tuplestore with all its values, so instead I have
> > settled down to name that SetSingleFuncCall(), to make a parallel with
> > the existing MultiFuncCall*().  funcapi.c is the right place for
> > that, and I have added more documentation in the fmgr's README as well
> > as funcapi.h.
>
> I have tortured all those code paths for the last couple of days, and
> the new function name, as well as its options, still seemed fine to
> me, so I have applied the patch.  The buildfarm is cool with it.  It
> is worth noting that there are more code paths in contrib/ that could
> be simplified with this new routine.

Wow! Thanks so much for taking the time to review, refine, and commit
this work.

I've attached a patch using the helper in most locations in contrib
modules that seemed useful.

The following I don't think we can use the helper or it is not worth it:

- pg_logdir_ls() in contrib/adminpack has return type TYPEFUNC_RECORD
  and expectedDesc is not already created, so the helper can't be used.

  But basically, since it doesn't specify OUT argument names, it has to
  do TupleDescInitEntry() itself anyway, I think.

- contrib/tablefunc.c was also not simple to refactor. the various parts
  of SetSingleFuncCall are spread throughout different functions in the
  file.

- contrib/dblink has one function which returns a tuplestore that was
  simple to change (dblink_get_notify()) and I've done so.

  However, most of the other creation of tuplestore and tupledescriptors
  is in helper functions (like materializeResult()) which separate the
  tuplestore creation from the tuple descriptor initialization in a way
  that made it hard to do a drop-in replacement with the helper function.

- Melanie
From a734a4dbf33230242f53bdc4ab6d23a98d6def8c Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplage...@gmail.com>
Date: Mon, 7 Mar 2022 14:22:22 -0500
Subject: [PATCH] contrib SRFs use helper

9e98583898c introduced a helper to centralize building their needed
state (tuplestore, tuple descriptors, etc) and checking for any errors.
Use this helper in contrib modules in which it is a simple drop-in
replacement.
---
 contrib/amcheck/verify_heapam.c               | 48 ++-----------
 contrib/dblink/dblink.c                       | 26 +------
 contrib/pageinspect/brinfuncs.c               | 31 +-------
 contrib/pageinspect/gistfuncs.c               | 60 ++--------------
 .../pg_stat_statements/pg_stat_statements.c   | 33 +--------
 contrib/pgrowlocks/pgrowlocks.c               | 34 ++-------
 contrib/postgres_fdw/connection.c             | 31 +-------
 contrib/xml2/xpath.c                          | 72 +++----------------
 8 files changed, 34 insertions(+), 301 deletions(-)

diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c
index f996f9a572..60e2f36183 100644
--- a/contrib/amcheck/verify_heapam.c
+++ b/contrib/amcheck/verify_heapam.c
@@ -165,7 +165,6 @@ static bool check_tuple_visibility(HeapCheckContext *ctx);
 static void report_corruption(HeapCheckContext *ctx, char *msg);
 static void report_toast_corruption(HeapCheckContext *ctx,
 									ToastedAttribute *ta, char *msg);
-static TupleDesc verify_heapam_tupdesc(void);
 static FullTransactionId FullTransactionIdFromXidAndCtx(TransactionId xid,
 														const HeapCheckContext *ctx);
 static void update_cached_xid_range(HeapCheckContext *ctx);
@@ -214,8 +213,6 @@ Datum
 verify_heapam(PG_FUNCTION_ARGS)
 {
 	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
-	MemoryContext old_context;
-	bool		random_access;
 	HeapCheckContext ctx;
 	Buffer		vmbuffer = InvalidBuffer;
 	Oid			relid;
@@ -227,16 +224,6 @@ verify_heapam(PG_FUNCTION_ARGS)
 	BlockNumber nblocks;
 	const char *skip;
 
-	/* Check to see if caller supports us returning a tuplestore */
-	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("set-valued function called in context that cannot accept a set")));
-	if (!(rsinfo->allowedModes & SFRM_Materialize))
-		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
-				 errmsg("materialize mode required, but it is not allowed in this context")));
-
 	/* Check supplied arguments */
 	if (PG_ARGISNULL(0))
 		ereport(ERROR,
@@ -290,15 +277,12 @@ verify_heapam(PG_FUNCTION_ARGS)
 	 */
 	ctx.attnum = -1;
 
-	/* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
-	old_context = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
-	random_access = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
-	ctx.tupdesc = verify_heapam_tupdesc();
-	ctx.tupstore = tuplestore_begin_heap(random_access, false, work_mem);
-	rsinfo->returnMode = SFRM_Materialize;
-	rsinfo->setResult = ctx.tupstore;
-	rsinfo->setDesc = ctx.tupdesc;
-	MemoryContextSwitchTo(old_context);
+	/*
+	 * Construct the tuplestore and tuple descriptor
+	 */
+	SetSingleFuncCall(fcinfo, 0);
+	ctx.tupdesc = rsinfo->setDesc;
+	ctx.tupstore = rsinfo->setResult;
 
 	/* Open relation, check relkind and access method */
 	ctx.rel = relation_open(relid, AccessShareLock);
@@ -630,26 +614,6 @@ report_toast_corruption(HeapCheckContext *ctx, ToastedAttribute *ta,
 	ctx->is_corrupt = true;
 }
 
-/*
- * Construct the TupleDesc used to report messages about corruptions found
- * while scanning the heap.
- */
-static TupleDesc
-verify_heapam_tupdesc(void)
-{
-	TupleDesc	tupdesc;
-	AttrNumber	a = 0;
-
-	tupdesc = CreateTemplateTupleDesc(HEAPCHECK_RELATION_COLS);
-	TupleDescInitEntry(tupdesc, ++a, "blkno", INT8OID, -1, 0);
-	TupleDescInitEntry(tupdesc, ++a, "offnum", INT4OID, -1, 0);
-	TupleDescInitEntry(tupdesc, ++a, "attnum", INT4OID, -1, 0);
-	TupleDescInitEntry(tupdesc, ++a, "msg", TEXTOID, -1, 0);
-	Assert(a == HEAPCHECK_RELATION_COLS);
-
-	return BlessTupleDesc(tupdesc);
-}
-
 /*
  * Check for tuple header corruption.
  *
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index efc4c94301..a06d4bd12d 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1928,12 +1928,6 @@ dblink_get_notify(PG_FUNCTION_ARGS)
 	PGconn	   *conn;
 	PGnotify   *notify;
 	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
-	TupleDesc	tupdesc;
-	Tuplestorestate *tupstore;
-	MemoryContext per_query_ctx;
-	MemoryContext oldcontext;
-
-	prepTuplestoreResult(fcinfo);
 
 	dblink_init();
 	if (PG_NARGS() == 1)
@@ -1941,23 +1935,7 @@ dblink_get_notify(PG_FUNCTION_ARGS)
 	else
 		conn = pconn->conn;
 
-	/* create the tuplestore in per-query memory */
-	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
-	oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
-	tupdesc = CreateTemplateTupleDesc(DBLINK_NOTIFY_COLS);
-	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "notify_name",
-					   TEXTOID, -1, 0);
-	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "be_pid",
-					   INT4OID, -1, 0);
-	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "extra",
-					   TEXTOID, -1, 0);
-
-	tupstore = tuplestore_begin_heap(true, false, work_mem);
-	rsinfo->setResult = tupstore;
-	rsinfo->setDesc = tupdesc;
-
-	MemoryContextSwitchTo(oldcontext);
+	SetSingleFuncCall(fcinfo, 0);
 
 	PQconsumeInput(conn);
 	while ((notify = PQnotifies(conn)) != NULL)
@@ -1980,7 +1958,7 @@ dblink_get_notify(PG_FUNCTION_ARGS)
 		else
 			nulls[2] = true;
 
-		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 		PQfreemem(notify);
 		PQconsumeInput(conn);
diff --git a/contrib/pageinspect/brinfuncs.c b/contrib/pageinspect/brinfuncs.c
index 683749a150..b7c8365218 100644
--- a/contrib/pageinspect/brinfuncs.c
+++ b/contrib/pageinspect/brinfuncs.c
@@ -126,9 +126,6 @@ brin_page_items(PG_FUNCTION_ARGS)
 	bytea	   *raw_page = PG_GETARG_BYTEA_P(0);
 	Oid			indexRelid = PG_GETARG_OID(1);
 	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
-	TupleDesc	tupdesc;
-	MemoryContext oldcontext;
-	Tuplestorestate *tupstore;
 	Relation	indexRel;
 	brin_column_state **columns;
 	BrinDesc   *bdesc;
@@ -143,29 +140,7 @@ brin_page_items(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("must be superuser to use raw page functions")));
 
-	/* check to see if caller supports us returning a tuplestore */
-	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("set-valued function called in context that cannot accept a set")));
-	if (!(rsinfo->allowedModes & SFRM_Materialize))
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("materialize mode required, but it is not allowed in this context")));
-
-	/* Build a tuple descriptor for our result type */
-	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
-		elog(ERROR, "return type must be a row type");
-
-	/* Build tuplestore to hold the result rows */
-	oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
-
-	tupstore = tuplestore_begin_heap(true, false, work_mem);
-	rsinfo->returnMode = SFRM_Materialize;
-	rsinfo->setResult = tupstore;
-	rsinfo->setDesc = tupdesc;
-
-	MemoryContextSwitchTo(oldcontext);
+	SetSingleFuncCall(fcinfo, 0);
 
 	indexRel = index_open(indexRelid, AccessShareLock);
 	bdesc = brin_build_desc(indexRel);
@@ -251,7 +226,7 @@ brin_page_items(PG_FUNCTION_ARGS)
 			int			att = attno - 1;
 
 			values[0] = UInt16GetDatum(offset);
-			switch (TupleDescAttr(tupdesc, 1)->atttypid)
+			switch (TupleDescAttr(rsinfo->setDesc, 1)->atttypid)
 			{
 				case INT8OID:
 					values[1] = Int64GetDatum((int64) dtup->bt_blkno);
@@ -301,7 +276,7 @@ brin_page_items(PG_FUNCTION_ARGS)
 			}
 		}
 
-		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 		/*
 		 * If the item was unused, jump straight to the next one; otherwise,
diff --git a/contrib/pageinspect/gistfuncs.c b/contrib/pageinspect/gistfuncs.c
index 96e3cab1cc..10d6dd44d4 100644
--- a/contrib/pageinspect/gistfuncs.c
+++ b/contrib/pageinspect/gistfuncs.c
@@ -97,10 +97,6 @@ gist_page_items_bytea(PG_FUNCTION_ARGS)
 {
 	bytea	   *raw_page = PG_GETARG_BYTEA_P(0);
 	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
-	bool		randomAccess;
-	TupleDesc	tupdesc;
-	Tuplestorestate *tupstore;
-	MemoryContext oldcontext;
 	Page		page;
 	OffsetNumber offset;
 	OffsetNumber maxoff = InvalidOffsetNumber;
@@ -110,29 +106,7 @@ gist_page_items_bytea(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("must be superuser to use raw page functions")));
 
-	/* check to see if caller supports us returning a tuplestore */
-	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("set-valued function called in context that cannot accept a set")));
-	if (!(rsinfo->allowedModes & SFRM_Materialize))
-		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
-				 errmsg("materialize mode required, but it is not allowed in this context")));
-
-	/* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
-	oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
-
-	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
-		elog(ERROR, "return type must be a row type");
-
-	randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
-	tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
-	rsinfo->returnMode = SFRM_Materialize;
-	rsinfo->setResult = tupstore;
-	rsinfo->setDesc = tupdesc;
-
-	MemoryContextSwitchTo(oldcontext);
+	SetSingleFuncCall(fcinfo, 0);
 
 	page = get_page_from_raw(raw_page);
 
@@ -173,7 +147,7 @@ gist_page_items_bytea(PG_FUNCTION_ARGS)
 		values[3] = BoolGetDatum(ItemIdIsDead(id));
 		values[4] = PointerGetDatum(tuple_bytea);
 
-		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 	}
 
 	return (Datum) 0;
@@ -185,11 +159,7 @@ gist_page_items(PG_FUNCTION_ARGS)
 	bytea	   *raw_page = PG_GETARG_BYTEA_P(0);
 	Oid			indexRelid = PG_GETARG_OID(1);
 	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
-	bool		randomAccess;
 	Relation	indexRel;
-	TupleDesc	tupdesc;
-	Tuplestorestate *tupstore;
-	MemoryContext oldcontext;
 	Page		page;
 	OffsetNumber offset;
 	OffsetNumber maxoff = InvalidOffsetNumber;
@@ -199,29 +169,7 @@ gist_page_items(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("must be superuser to use raw page functions")));
 
-	/* check to see if caller supports us returning a tuplestore */
-	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("set-valued function called in context that cannot accept a set")));
-	if (!(rsinfo->allowedModes & SFRM_Materialize))
-		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
-				 errmsg("materialize mode required, but it is not allowed in this context")));
-
-	/* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
-	oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
-
-	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
-		elog(ERROR, "return type must be a row type");
-
-	randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
-	tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
-	rsinfo->returnMode = SFRM_Materialize;
-	rsinfo->setResult = tupstore;
-	rsinfo->setDesc = tupdesc;
-
-	MemoryContextSwitchTo(oldcontext);
+	SetSingleFuncCall(fcinfo, 0);
 
 	/* Open the relation */
 	indexRel = index_open(indexRelid, AccessShareLock);
@@ -272,7 +220,7 @@ gist_page_items(PG_FUNCTION_ARGS)
 			nulls[4] = true;
 		}
 
-		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 	}
 
 	relation_close(indexRel, AccessShareLock);
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index d803253cea..9e525a6ad3 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -1494,10 +1494,6 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
 							bool showtext)
 {
 	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
-	TupleDesc	tupdesc;
-	Tuplestorestate *tupstore;
-	MemoryContext per_query_ctx;
-	MemoryContext oldcontext;
 	Oid			userid = GetUserId();
 	bool		is_allowed_role = false;
 	char	   *qbuffer = NULL;
@@ -1516,30 +1512,14 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 				 errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
 
-	/* check to see if caller supports us returning a tuplestore */
-	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("set-valued function called in context that cannot accept a set")));
-	if (!(rsinfo->allowedModes & SFRM_Materialize))
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("materialize mode required, but it is not allowed in this context")));
-
-	/* Switch into long-lived context to construct returned data structures */
-	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
-	oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
-	/* Build a tuple descriptor for our result type */
-	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
-		elog(ERROR, "return type must be a row type");
+	SetSingleFuncCall(fcinfo, 0);
 
 	/*
 	 * Check we have the expected number of output arguments.  Aside from
 	 * being a good safety check, we need a kluge here to detect API version
 	 * 1.1, which was wedged into the code in an ill-considered way.
 	 */
-	switch (tupdesc->natts)
+	switch (rsinfo->setDesc->natts)
 	{
 		case PG_STAT_STATEMENTS_COLS_V1_0:
 			if (api_version != PGSS_V1_0)
@@ -1571,13 +1551,6 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
 			elog(ERROR, "incorrect number of output arguments");
 	}
 
-	tupstore = tuplestore_begin_heap(true, false, work_mem);
-	rsinfo->returnMode = SFRM_Materialize;
-	rsinfo->setResult = tupstore;
-	rsinfo->setDesc = tupdesc;
-
-	MemoryContextSwitchTo(oldcontext);
-
 	/*
 	 * We'd like to load the query text file (if needed) while not holding any
 	 * lock on pgss->lock.  In the worst case we'll have to do this again
@@ -1800,7 +1773,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
 					 api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
 					 -1 /* fail if you forget to update this assert */ ));
 
-		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 	}
 
 	LWLockRelease(pgss->lock);
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index d8946dc510..713a165203 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -66,42 +66,16 @@ pgrowlocks(PG_FUNCTION_ARGS)
 {
 	text	   *relname = PG_GETARG_TEXT_PP(0);
 	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
-	bool		randomAccess;
-	TupleDesc	tupdesc;
-	Tuplestorestate *tupstore;
 	AttInMetadata *attinmeta;
 	Relation	rel;
 	RangeVar   *relrv;
 	TableScanDesc scan;
 	HeapScanDesc hscan;
 	HeapTuple	tuple;
-	MemoryContext oldcontext;
 	AclResult	aclresult;
 	char	  **values;
 
-	/* check to see if caller supports us returning a tuplestore */
-	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("set-valued function called in context that cannot accept a set")));
-	if (!(rsinfo->allowedModes & SFRM_Materialize))
-		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
-				 errmsg("materialize mode required, but it is not allowed in this context")));
-
-	/* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
-	oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
-
-	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
-		elog(ERROR, "return type must be a row type");
-
-	randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
-	tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
-	rsinfo->returnMode = SFRM_Materialize;
-	rsinfo->setResult = tupstore;
-	rsinfo->setDesc = tupdesc;
-
-	MemoryContextSwitchTo(oldcontext);
+	SetSingleFuncCall(fcinfo, 0);
 
 	/* Access the table */
 	relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
@@ -140,9 +114,9 @@ pgrowlocks(PG_FUNCTION_ARGS)
 	scan = table_beginscan(rel, GetActiveSnapshot(), 0, NULL);
 	hscan = (HeapScanDesc) scan;
 
-	attinmeta = TupleDescGetAttInMetadata(tupdesc);
+	attinmeta = TupleDescGetAttInMetadata(rsinfo->setDesc);
 
-	values = (char **) palloc(tupdesc->natts * sizeof(char *));
+	values = (char **) palloc(rsinfo->setDesc->natts * sizeof(char *));
 
 	while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
 	{
@@ -288,7 +262,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
 
 			/* build a tuple */
 			tuple = BuildTupleFromCStrings(attinmeta, values);
-			tuplestore_puttuple(tupstore, tuple);
+			tuplestore_puttuple(rsinfo->setResult, tuple);
 		}
 		else
 		{
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 8c64d42dda..74d3e73205 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -1661,37 +1661,10 @@ postgres_fdw_get_connections(PG_FUNCTION_ARGS)
 {
 #define POSTGRES_FDW_GET_CONNECTIONS_COLS	2
 	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
-	TupleDesc	tupdesc;
-	Tuplestorestate *tupstore;
-	MemoryContext per_query_ctx;
-	MemoryContext oldcontext;
 	HASH_SEQ_STATUS scan;
 	ConnCacheEntry *entry;
 
-	/* check to see if caller supports us returning a tuplestore */
-	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("set-valued function called in context that cannot accept a set")));
-	if (!(rsinfo->allowedModes & SFRM_Materialize))
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("materialize mode required, but it is not allowed in this context")));
-
-	/* Build a tuple descriptor for our result type */
-	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
-		elog(ERROR, "return type must be a row type");
-
-	/* Build tuplestore to hold the result rows */
-	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
-	oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
-	tupstore = tuplestore_begin_heap(true, false, work_mem);
-	rsinfo->returnMode = SFRM_Materialize;
-	rsinfo->setResult = tupstore;
-	rsinfo->setDesc = tupdesc;
-
-	MemoryContextSwitchTo(oldcontext);
+	SetSingleFuncCall(fcinfo, 0);
 
 	/* If cache doesn't exist, we return no records */
 	if (!ConnectionHash)
@@ -1757,7 +1730,7 @@ postgres_fdw_get_connections(PG_FUNCTION_ARGS)
 
 		values[1] = BoolGetDatum(!entry->invalidated);
 
-		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 	}
 
 
diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c
index a2e5fb54e2..b8ee757674 100644
--- a/contrib/xml2/xpath.c
+++ b/contrib/xml2/xpath.c
@@ -491,15 +491,9 @@ xpath_table(PG_FUNCTION_ARGS)
 	HeapTuple	spi_tuple;
 	TupleDesc	spi_tupdesc;
 
-	/* Output tuple (tuplestore) support */
-	Tuplestorestate *tupstore = NULL;
-	TupleDesc	ret_tupdesc;
-	HeapTuple	ret_tuple;
 
 	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
 	AttInMetadata *attinmeta;
-	MemoryContext per_query_ctx;
-	MemoryContext oldcontext;
 
 	char	  **values;
 	xmlChar   **xpaths;
@@ -517,48 +511,10 @@ xpath_table(PG_FUNCTION_ARGS)
 	PgXmlErrorContext *xmlerrcxt;
 	volatile xmlDocPtr doctree = NULL;
 
-	/* We only have a valid tuple description in table function mode */
-	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("set-valued function called in context that cannot accept a set")));
-	if (rsinfo->expectedDesc == NULL)
-		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
-				 errmsg("xpath_table must be called as a table function")));
-
-	/*
-	 * We want to materialise because it means that we don't have to carry
-	 * libxml2 parser state between invocations of this function
-	 */
-	if (!(rsinfo->allowedModes & SFRM_Materialize))
-		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
-				 errmsg("xpath_table requires Materialize mode, but it is not "
-						"allowed in this context")));
-
-	/*
-	 * The tuplestore must exist in a higher context than this function call
-	 * (per_query_ctx is used)
-	 */
-	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
-	oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
-	/*
-	 * Create the tuplestore - work_mem is the max in-memory size before a
-	 * file is created on disk to hold it.
-	 */
-	tupstore =
-		tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random,
-							  false, work_mem);
-
-	MemoryContextSwitchTo(oldcontext);
-
-	/* get the requested return tuple description */
-	ret_tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
+	SetSingleFuncCall(fcinfo, SRF_SINGLE_USE_EXPECTED);
 
 	/* must have at least one output column (for the pkey) */
-	if (ret_tupdesc->natts < 1)
+	if (rsinfo->setDesc->natts < 1)
 		ereport(ERROR,
 				(errcode(ERRCODE_SYNTAX_ERROR),
 				 errmsg("xpath_table must have at least one output column")));
@@ -571,14 +527,10 @@ xpath_table(PG_FUNCTION_ARGS)
 	 * representation.
 	 */
 
-	attinmeta = TupleDescGetAttInMetadata(ret_tupdesc);
-
-	/* Set return mode and allocate value space. */
-	rsinfo->returnMode = SFRM_Materialize;
-	rsinfo->setDesc = ret_tupdesc;
+	attinmeta = TupleDescGetAttInMetadata(rsinfo->setDesc);
 
-	values = (char **) palloc(ret_tupdesc->natts * sizeof(char *));
-	xpaths = (xmlChar **) palloc(ret_tupdesc->natts * sizeof(xmlChar *));
+	values = (char **) palloc(rsinfo->setDesc->natts * sizeof(char *));
+	xpaths = (xmlChar **) palloc(rsinfo->setDesc->natts * sizeof(xmlChar *));
 
 	/*
 	 * Split XPaths. xpathset is a writable CString.
@@ -587,7 +539,7 @@ xpath_table(PG_FUNCTION_ARGS)
 	 */
 	numpaths = 0;
 	pos = xpathset;
-	while (numpaths < (ret_tupdesc->natts - 1))
+	while (numpaths < (rsinfo->setDesc->natts - 1))
 	{
 		xpaths[numpaths++] = (xmlChar *) pos;
 		pos = strstr(pos, pathsep);
@@ -621,9 +573,6 @@ xpath_table(PG_FUNCTION_ARGS)
 	tuptable = SPI_tuptable;
 	spi_tupdesc = tuptable->tupdesc;
 
-	/* Switch out of SPI context */
-	MemoryContextSwitchTo(oldcontext);
-
 	/*
 	 * Check that SPI returned correct result. If you put a comma into one of
 	 * the function parameters, this will catch it when the SPI query returns
@@ -655,6 +604,7 @@ xpath_table(PG_FUNCTION_ARGS)
 			xmlXPathObjectPtr res;
 			xmlChar    *resstr;
 			xmlXPathCompExprPtr comppath;
+			HeapTuple	ret_tuple;
 
 			/* Extract the row data as C Strings */
 			spi_tuple = tuptable->vals[i];
@@ -666,7 +616,7 @@ xpath_table(PG_FUNCTION_ARGS)
 			 * return NULL in all columns.  Note that this also means that
 			 * spare columns will be NULL.
 			 */
-			for (j = 0; j < ret_tupdesc->natts; j++)
+			for (j = 0; j < rsinfo->setDesc->natts; j++)
 				values[j] = NULL;
 
 			/* Insert primary key */
@@ -682,7 +632,7 @@ xpath_table(PG_FUNCTION_ARGS)
 			{
 				/* not well-formed, so output all-NULL tuple */
 				ret_tuple = BuildTupleFromCStrings(attinmeta, values);
-				tuplestore_puttuple(tupstore, ret_tuple);
+				tuplestore_puttuple(rsinfo->setResult, ret_tuple);
 				heap_freetuple(ret_tuple);
 			}
 			else
@@ -749,7 +699,7 @@ xpath_table(PG_FUNCTION_ARGS)
 					if (had_values)
 					{
 						ret_tuple = BuildTupleFromCStrings(attinmeta, values);
-						tuplestore_puttuple(tupstore, ret_tuple);
+						tuplestore_puttuple(rsinfo->setResult, ret_tuple);
 						heap_freetuple(ret_tuple);
 					}
 
@@ -785,8 +735,6 @@ xpath_table(PG_FUNCTION_ARGS)
 
 	SPI_finish();
 
-	rsinfo->setResult = tupstore;
-
 	/*
 	 * SFRM_Materialize mode expects us to return a NULL Datum. The actual
 	 * tuples are in our tuplestore and passed back through rsinfo->setResult.
-- 
2.30.2

Reply via email to