@@ -52,13 +52,6 @@ PG_FUNCTION_INFO_V1(pgrowlocks);
5252
5353#define NCHARS 32
5454
55- typedef struct
56- {
57- Relation rel ;
58- HeapScanDesc scan ;
59- int ncolumns ;
60- }MyData ;
61-
6255#define Atnum_tid 0
6356#define Atnum_xmax 1
6457#define Atnum_ismulti 2
@@ -69,77 +62,80 @@ typedef struct
6962Datum
7063pgrowlocks (PG_FUNCTION_ARGS )
7164{
72- FuncCallContext * funcctx ;
73- HeapScanDesc scan ;
74- HeapTuple tuple ;
65+ text * relname = PG_GETARG_TEXT_PP ( 0 ) ;
66+ ReturnSetInfo * rsinfo = ( ReturnSetInfo * ) fcinfo -> resultinfo ;
67+ bool randomAccess ;
7568TupleDesc tupdesc ;
69+ Tuplestorestate * tupstore ;
7670AttInMetadata * attinmeta ;
77- Datum result ;
78- MyData * mydata ;
7971Relation rel ;
72+ RangeVar * relrv ;
73+ HeapScanDesc scan ;
74+ HeapTuple tuple ;
75+ MemoryContext oldcontext ;
76+ AclResult aclresult ;
77+ char * * values ;
78+
79+ /* check to see if caller supports us returning a tuplestore */
80+ if (rsinfo == NULL || !IsA (rsinfo ,ReturnSetInfo ))
81+ ereport (ERROR ,
82+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
83+ errmsg ("set-valued function called in context that cannot accept a set" )));
84+ if (!(rsinfo -> allowedModes & SFRM_Materialize ))
85+ ereport (ERROR ,
86+ (errcode (ERRCODE_SYNTAX_ERROR ),
87+ errmsg ("materialize mode required, but it is not allowed in this context" )));
88+
89+ /* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
90+ oldcontext = MemoryContextSwitchTo (rsinfo -> econtext -> ecxt_per_query_memory );
91+
92+ if (get_call_result_type (fcinfo ,NULL ,& tupdesc )!= TYPEFUNC_COMPOSITE )
93+ elog (ERROR ,"return type must be a row type" );
94+
95+ randomAccess = (rsinfo -> allowedModes & SFRM_Materialize_Random )!= 0 ;
96+ tupstore = tuplestore_begin_heap (randomAccess , false,work_mem );
97+ rsinfo -> returnMode = SFRM_Materialize ;
98+ rsinfo -> setResult = tupstore ;
99+ rsinfo -> setDesc = tupdesc ;
100+
101+ MemoryContextSwitchTo (oldcontext );
102+
103+ /* Access the table */
104+ relrv = makeRangeVarFromNameList (textToQualifiedNameList (relname ));
105+ rel = relation_openrv (relrv ,AccessShareLock );
106+
107+ if (rel -> rd_rel -> relkind == RELKIND_PARTITIONED_TABLE )
108+ ereport (ERROR ,
109+ (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
110+ errmsg ("\"%s\" is a partitioned table" ,
111+ RelationGetRelationName (rel )),
112+ errdetail ("Partitioned tables do not contain rows." )));
113+ else if (rel -> rd_rel -> relkind != RELKIND_RELATION )
114+ ereport (ERROR ,
115+ (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
116+ errmsg ("\"%s\" is not a table" ,
117+ RelationGetRelationName (rel ))));
118+
119+ /*
120+ * check permissions: must have SELECT on table or be in
121+ * pg_stat_scan_tables
122+ */
123+ aclresult = pg_class_aclcheck (RelationGetRelid (rel ),GetUserId (),
124+ ACL_SELECT );
125+ if (aclresult != ACLCHECK_OK )
126+ aclresult = is_member_of_role (GetUserId (),DEFAULT_ROLE_STAT_SCAN_TABLES ) ?ACLCHECK_OK :ACLCHECK_NO_PRIV ;
127+
128+ if (aclresult != ACLCHECK_OK )
129+ aclcheck_error (aclresult ,get_relkind_objtype (rel -> rd_rel -> relkind ),
130+ RelationGetRelationName (rel ));
131+
132+ /* Scan the relation */
133+ scan = heap_beginscan (rel ,GetActiveSnapshot (),0 ,NULL );
134+
135+ attinmeta = TupleDescGetAttInMetadata (tupdesc );
136+
137+ values = (char * * )palloc (tupdesc -> natts * sizeof (char * ));
80138
81- if (SRF_IS_FIRSTCALL ())
82- {
83- text * relname ;
84- RangeVar * relrv ;
85- MemoryContext oldcontext ;
86- AclResult aclresult ;
87-
88- funcctx = SRF_FIRSTCALL_INIT ();
89- oldcontext = MemoryContextSwitchTo (funcctx -> multi_call_memory_ctx );
90-
91- /* Build a tuple descriptor for our result type */
92- if (get_call_result_type (fcinfo ,NULL ,& tupdesc )!= TYPEFUNC_COMPOSITE )
93- elog (ERROR ,"return type must be a row type" );
94-
95- attinmeta = TupleDescGetAttInMetadata (tupdesc );
96- funcctx -> attinmeta = attinmeta ;
97-
98- relname = PG_GETARG_TEXT_PP (0 );
99- relrv = makeRangeVarFromNameList (textToQualifiedNameList (relname ));
100- rel = relation_openrv (relrv ,AccessShareLock );
101-
102- if (rel -> rd_rel -> relkind == RELKIND_PARTITIONED_TABLE )
103- ereport (ERROR ,
104- (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
105- errmsg ("\"%s\" is a partitioned table" ,
106- RelationGetRelationName (rel )),
107- errdetail ("Partitioned tables do not contain rows." )));
108- else if (rel -> rd_rel -> relkind != RELKIND_RELATION )
109- ereport (ERROR ,
110- (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
111- errmsg ("\"%s\" is not a table" ,
112- RelationGetRelationName (rel ))));
113-
114- /*
115- * check permissions: must have SELECT on table or be in
116- * pg_stat_scan_tables
117- */
118- aclresult = pg_class_aclcheck (RelationGetRelid (rel ),GetUserId (),
119- ACL_SELECT );
120- if (aclresult != ACLCHECK_OK )
121- aclresult = is_member_of_role (GetUserId (),DEFAULT_ROLE_STAT_SCAN_TABLES ) ?ACLCHECK_OK :ACLCHECK_NO_PRIV ;
122-
123- if (aclresult != ACLCHECK_OK )
124- aclcheck_error (aclresult ,get_relkind_objtype (rel -> rd_rel -> relkind ),
125- RelationGetRelationName (rel ));
126-
127- scan = heap_beginscan (rel ,GetActiveSnapshot (),0 ,NULL );
128- mydata = palloc (sizeof (* mydata ));
129- mydata -> rel = rel ;
130- mydata -> scan = scan ;
131- mydata -> ncolumns = tupdesc -> natts ;
132- funcctx -> user_fctx = mydata ;
133-
134- MemoryContextSwitchTo (oldcontext );
135- }
136-
137- funcctx = SRF_PERCALL_SETUP ();
138- attinmeta = funcctx -> attinmeta ;
139- mydata = (MyData * )funcctx -> user_fctx ;
140- scan = mydata -> scan ;
141-
142- /* scan the relation */
143139while ((tuple = heap_getnext (scan ,ForwardScanDirection ))!= NULL )
144140{
145141HTSU_Result htsu ;
@@ -160,10 +156,6 @@ pgrowlocks(PG_FUNCTION_ARGS)
160156 */
161157if (htsu == HeapTupleBeingUpdated )
162158{
163- char * * values ;
164-
165- values = (char * * )palloc (mydata -> ncolumns * sizeof (char * ));
166-
167159values [Atnum_tid ]= (char * )DirectFunctionCall1 (tidout ,
168160PointerGetDatum (& tuple -> t_self ));
169161
@@ -288,16 +280,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
288280
289281/* build a tuple */
290282tuple = BuildTupleFromCStrings (attinmeta ,values );
291-
292- /* make the tuple into a datum */
293- result = HeapTupleGetDatum (tuple );
294-
295- /*
296- * no need to pfree what we allocated; it's on a short-lived
297- * memory context anyway
298- */
299-
300- SRF_RETURN_NEXT (funcctx ,result );
283+ tuplestore_puttuple (tupstore ,tuple );
301284}
302285else
303286{
@@ -306,7 +289,6 @@ pgrowlocks(PG_FUNCTION_ARGS)
306289}
307290
308291heap_endscan (scan );
309- heap_close (mydata -> rel ,AccessShareLock );
310-
311- SRF_RETURN_DONE (funcctx );
292+ heap_close (rel ,AccessShareLock );
293+ return (Datum )0 ;
312294}