Simplify SRFs using materialize mode in contrib/ modules
authorMichael Paquier <michael@paquier.xyz>
Tue, 8 Mar 2022 01:12:22 +0000 (10:12 +0900)
committerMichael Paquier <michael@paquier.xyz>
Tue, 8 Mar 2022 01:12:22 +0000 (10:12 +0900)
9e98583 introduced a helper to centralize building their needed state
(tuplestore, tuple descriptors, etc.), checking for any errors.  This
commit updates all places of contrib/ that can be switched to use
SetSingleFuncCall() as a drop-in replacement, resulting in the removal
of a lot of boilerplate code in all the modules updated by this commit.

Per analysis, some places remain as they are:
- pg_logdir_ls() in adminpack/ uses historically TYPEFUNC_RECORD as
return type, and I suspect that changing it may cause issues at run-time
with some of its past versions, down to 1.0.
- dblink/ uses a wrapper function doing exactly the work of
SetSingleFuncCall().  Here the switch should be possible, but rather
invasive so it does not seem the extra backpatch maintenance cost.
- tablefunc/, similarly, uses multiple helper functions with portions of
SetSingleFuncCall() spread across the code paths of this module.

Author: Melanie Plageman
Discussion: https://postgr.es/m/CAAKRu_bvDPJoL9mH6eYwvBpPtTGQwbDzfJbCM-OjkSZDu5yTPg@mail.gmail.com

contrib/amcheck/verify_heapam.c
contrib/dblink/dblink.c
contrib/pageinspect/brinfuncs.c
contrib/pageinspect/gistfuncs.c
contrib/pg_stat_statements/pg_stat_statements.c
contrib/pgrowlocks/pgrowlocks.c
contrib/postgres_fdw/connection.c
contrib/xml2/xpath.c

index f996f9a5727b1eb4b0541d22840ddb87d97e5d05..e5f7355dcb81ed04869d676908f01bc9f60ed2d3 100644 (file)
@@ -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,10 @@ 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 +612,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.
  *
index efc4c94301845183d8c9779c9bdfb30e0bee0143..a06d4bd12db83c234737b0749491fce87a79ea2c 100644 (file)
@@ -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);
index 683749a1505f7fa3208207749a5b6b4c3a374bb7..b7c83652180da592f91d9996594a0dbac3362a76 100644 (file)
@@ -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,
index 96e3cab1cc346d8046fd413b4e48eb5f8c068f12..10d6dd44d4e083ef06b16b1543a8413996124ad0 100644 (file)
@@ -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);
index d803253ceaf74c2781473d703a01ace8993fbb76..9e525a6ad3b5ef3c83f8643bfdac65f90f571450 100644 (file)
@@ -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);
index d8946dc5107ac1a617fcec52af44b390cf5caab6..713a165203e3c16d14a1ff343cf28e47d93af48e 100644 (file)
@@ -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
                {
index 8c64d42dda2465373f6771a1be27fc902b89c9cb..74d3e732055ac2c8a397baaa392c9f843ef14c1b 100644 (file)
@@ -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);
        }
 
 
index a2e5fb54e2052ad5a16b93289967ea6898635788..b8ee757674d48a999dc45d6520d9bfeb55920bbf 100644 (file)
@@ -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.