First off, you should know that the new SRF interface is simply a wrapper around the old method. Set-returning functions are still invoked multiple times. The first time through, an SRF initializes its own context structure and stores that structure away so that each subsequent invocation can find it. In the old approach, an SRF examined fmgr_info->fn_extra to determine whether it was being invoked for the first time (if fmgr_info->fn_extra is NULL, this is the first call). In the new approach, you call the SRF_IS_FIRSTCALL() macro instead. You can probably guess what this macro does: It returns trUE if fmgr_info->fn_extra is NULL (implying that this is the first call). In fact, here's the definition of the SRF_IS_FIRSTCALL() macro:
#define SRF_IS_FIRSTCALL() ( fcinfo->flinfo->fn_extra == NULL )
No great surprises there.
Once you know that you're looking at the first invocation of an SRF, you typically allocate a context structure of some sort and store the address of the structure in fmgr_info->fn_extra. In the new approach, you call the SRF_FIRSTCALL_INIT() macro. This macro allocates its own context structure (a structure of type FuncCallContext), records the address of the structure in fmgr_info->fn_extra, and returns the address back to your SRF. A FuncCallContext structure looks like this:
typedef struct FuncCallContext { uint32 call_cntr; uint32 max_calls; TupleTableSlot * slot; void * user_fctx; AttInMetadata * attinmeta; MemoryContext multi_call_memory_ctx; TupleDesc tuple_desc; } FuncCallContext;
If the SRF_FIRSTCALL_INIT() macro stores its own pointer in fmgr_info->fn_extra, where are you supposed to store the address of your context structure? In the user_fctx fieldthat pointer is reserved for your own personal use, just like fmgr_info->fn_extra was reserved for your use in the old SRF mechanism. I'll explain the other members of the FuncCallContext structure in a moment.
Now that you have a pointer to a spanking new FuncCallContext (remember, SRF_FIRSTCALL_INIT() returns the address of the structure), you can allocate your own context structure and store its address in user_fctx:
... FuncCallContext * srf = SRF_FIRSTCALL_INIT(); dir_ctx * ctx; ctx = (dir_ctx *) MemoryContextAlloc( srf->multi_call_memory_ctx, sizeof( dir_ctx )); srf->usr_fctx = ctx; ...
Notice that the FuncCallContext structure holds a MemoryContext named multi_call_memory_ctx. Any data that you need to save from one invocation to the next must be allocated from the multi_call_memory_ctx or PostgreSQL will discard that data as soon as the first invocation completes (multi_call_memory_ctx is equivalent to fmgr_info->fn_mctx in the old SRF mechanism).
Each time your SRF is invoked (even the first time), you should call the SRF_PERCALL_SETUP() macro. Like SRF_FIRSTCALL_INIT(), SRF_PERCALL_SETUP() returns a pointer to the FuncCallContext structure. The context pointer that you saved in user_fctx is still there. You can use that pointer to get to the context structure that you allocated (and initialized) the first time through.
The new SRF mechanism provides two more macros: SRF_RETURN_NEXT() and SRF_RETURN_DONE(). As you might expect, these macros return information to the caller. The SRF_RETURN_NEXT() macros returns a value (a Datum) to the caller and tells the server to call you again to retrieve the next value in the result set (remember, you're writing a set-returning function; the server will call your function until you indicate that you have no more results to add to the set). The SRF_RETURN_DONE() macros returns a NULL value to the caller and tells the server that you have no more results to add to the result set. SRF_RETURN_DONE() also deallocates the FullCallContext structure so you should perform any cleanup work before you call SRF_RETURN_DONE()you won't get another chance.
To show you how all of these macros fit together, Listing 6.5 shows the filelist() function again, this time created with the new SRF mechanism:
Listing 6.5. filelistSRF.c
1 /* 2 ** Filename: filelistSRF.c 3 */ 4 5 #include "postgres.h" 6 #include "funcapi.h" 7 8 #include 9 #include 10 11 typedef struct 12 { 13 struct dirent ** dir_ctx_entries; 14 } dir_ctx; 15 16 PG_FUNCTION_INFO_V1(filelist); 17 18 Datum filelist(PG_FUNCTION_ARGS) 19 { 20 text * startText = PG_GETARG_TEXT_P(0); 21 int len = VARSIZE( startText ) - VARHDRSZ; 22 char * start = (char *)palloc( len+1 ); 23 dir_ctx * ctx; 24 FuncCallContext * srf; 25 26 memcpy( start, startText->vl_dat, len ); 27 start[len] = '