@@ -54,13 +54,6 @@ PG_FUNCTION_INFO_V1(pgrowlocks);
5454
5555#define NCHARS 32
5656
57- typedef struct
58- {
59- Relation rel ;
60- TableScanDesc scan ;
61- int ncolumns ;
62- }MyData ;
63-
6457#define Atnum_tid 0
6558#define Atnum_xmax 1
6659#define Atnum_ismulti 2
@@ -71,84 +64,86 @@ typedef struct
7164Datum
7265pgrowlocks (PG_FUNCTION_ARGS )
7366{
74- FuncCallContext * funcctx ;
75- TableScanDesc scan ;
76- HeapScanDesc hscan ;
77- HeapTuple tuple ;
67+ text * relname = PG_GETARG_TEXT_PP (0 );
68+ ReturnSetInfo * rsinfo = (ReturnSetInfo * )fcinfo -> resultinfo ;
69+ bool randomAccess ;
7870TupleDesc tupdesc ;
71+ Tuplestorestate * tupstore ;
7972AttInMetadata * attinmeta ;
80- Datum result ;
81- MyData * mydata ;
8273Relation rel ;
74+ RangeVar * relrv ;
75+ TableScanDesc scan ;
76+ HeapScanDesc hscan ;
77+ HeapTuple tuple ;
78+ MemoryContext oldcontext ;
79+ AclResult aclresult ;
80+ char * * values ;
81+
82+ /* check to see if caller supports us returning a tuplestore */
83+ if (rsinfo == NULL || !IsA (rsinfo ,ReturnSetInfo ))
84+ ereport (ERROR ,
85+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
86+ errmsg ("set-valued function called in context that cannot accept a set" )));
87+ if (!(rsinfo -> allowedModes & SFRM_Materialize ))
88+ ereport (ERROR ,
89+ (errcode (ERRCODE_SYNTAX_ERROR ),
90+ errmsg ("materialize mode required, but it is not allowed in this context" )));
91+
92+ /* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
93+ oldcontext = MemoryContextSwitchTo (rsinfo -> econtext -> ecxt_per_query_memory );
94+
95+ if (get_call_result_type (fcinfo ,NULL ,& tupdesc )!= TYPEFUNC_COMPOSITE )
96+ elog (ERROR ,"return type must be a row type" );
97+
98+ randomAccess = (rsinfo -> allowedModes & SFRM_Materialize_Random )!= 0 ;
99+ tupstore = tuplestore_begin_heap (randomAccess , false,work_mem );
100+ rsinfo -> returnMode = SFRM_Materialize ;
101+ rsinfo -> setResult = tupstore ;
102+ rsinfo -> setDesc = tupdesc ;
103+
104+ MemoryContextSwitchTo (oldcontext );
105+
106+ /* Access the table */
107+ relrv = makeRangeVarFromNameList (textToQualifiedNameList (relname ));
108+ rel = relation_openrv (relrv ,AccessShareLock );
109+
110+ if (rel -> rd_rel -> relam != HEAP_TABLE_AM_OID )
111+ ereport (ERROR , (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
112+ errmsg ("only heap AM is supported" )));
113+
114+ if (rel -> rd_rel -> relkind == RELKIND_PARTITIONED_TABLE )
115+ ereport (ERROR ,
116+ (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
117+ errmsg ("\"%s\" is a partitioned table" ,
118+ RelationGetRelationName (rel )),
119+ errdetail ("Partitioned tables do not contain rows." )));
120+ else if (rel -> rd_rel -> relkind != RELKIND_RELATION )
121+ ereport (ERROR ,
122+ (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
123+ errmsg ("\"%s\" is not a table" ,
124+ RelationGetRelationName (rel ))));
125+
126+ /*
127+ * check permissions: must have SELECT on table or be in
128+ * pg_stat_scan_tables
129+ */
130+ aclresult = pg_class_aclcheck (RelationGetRelid (rel ),GetUserId (),
131+ ACL_SELECT );
132+ if (aclresult != ACLCHECK_OK )
133+ aclresult = is_member_of_role (GetUserId (),DEFAULT_ROLE_STAT_SCAN_TABLES ) ?ACLCHECK_OK :ACLCHECK_NO_PRIV ;
134+
135+ if (aclresult != ACLCHECK_OK )
136+ aclcheck_error (aclresult ,get_relkind_objtype (rel -> rd_rel -> relkind ),
137+ RelationGetRelationName (rel ));
138+
139+ /* Scan the relation */
140+ scan = table_beginscan (rel ,GetActiveSnapshot (),0 ,NULL );
141+ hscan = (HeapScanDesc )scan ;
83142
84- if (SRF_IS_FIRSTCALL ())
85- {
86- text * relname ;
87- RangeVar * relrv ;
88- MemoryContext oldcontext ;
89- AclResult aclresult ;
90-
91- funcctx = SRF_FIRSTCALL_INIT ();
92- oldcontext = MemoryContextSwitchTo (funcctx -> multi_call_memory_ctx );
93-
94- /* Build a tuple descriptor for our result type */
95- if (get_call_result_type (fcinfo ,NULL ,& tupdesc )!= TYPEFUNC_COMPOSITE )
96- elog (ERROR ,"return type must be a row type" );
97-
98- attinmeta = TupleDescGetAttInMetadata (tupdesc );
99- funcctx -> attinmeta = attinmeta ;
100-
101- relname = PG_GETARG_TEXT_PP (0 );
102- relrv = makeRangeVarFromNameList (textToQualifiedNameList (relname ));
103- rel = relation_openrv (relrv ,AccessShareLock );
104-
105- if (rel -> rd_rel -> relam != HEAP_TABLE_AM_OID )
106- ereport (ERROR , (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
107- errmsg ("only heap AM is supported" )));
108-
109- if (rel -> rd_rel -> relkind == RELKIND_PARTITIONED_TABLE )
110- ereport (ERROR ,
111- (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
112- errmsg ("\"%s\" is a partitioned table" ,
113- RelationGetRelationName (rel )),
114- errdetail ("Partitioned tables do not contain rows." )));
115- else if (rel -> rd_rel -> relkind != RELKIND_RELATION )
116- ereport (ERROR ,
117- (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
118- errmsg ("\"%s\" is not a table" ,
119- RelationGetRelationName (rel ))));
120-
121- /*
122- * check permissions: must have SELECT on table or be in
123- * pg_stat_scan_tables
124- */
125- aclresult = pg_class_aclcheck (RelationGetRelid (rel ),GetUserId (),
126- ACL_SELECT );
127- if (aclresult != ACLCHECK_OK )
128- aclresult = is_member_of_role (GetUserId (),DEFAULT_ROLE_STAT_SCAN_TABLES ) ?ACLCHECK_OK :ACLCHECK_NO_PRIV ;
129-
130- if (aclresult != ACLCHECK_OK )
131- aclcheck_error (aclresult ,get_relkind_objtype (rel -> rd_rel -> relkind ),
132- RelationGetRelationName (rel ));
133-
134- scan = table_beginscan (rel ,GetActiveSnapshot (),0 ,NULL );
135- hscan = (HeapScanDesc )scan ;
136- mydata = palloc (sizeof (* mydata ));
137- mydata -> rel = rel ;
138- mydata -> scan = scan ;
139- mydata -> ncolumns = tupdesc -> natts ;
140- funcctx -> user_fctx = mydata ;
141-
142- MemoryContextSwitchTo (oldcontext );
143- }
143+ attinmeta = TupleDescGetAttInMetadata (tupdesc );
144144
145- funcctx = SRF_PERCALL_SETUP ();
146- attinmeta = funcctx -> attinmeta ;
147- mydata = (MyData * )funcctx -> user_fctx ;
148- scan = mydata -> scan ;
149- hscan = (HeapScanDesc )scan ;
145+ values = (char * * )palloc (tupdesc -> natts * sizeof (char * ));
150146
151- /* scan the relation */
152147while ((tuple = heap_getnext (scan ,ForwardScanDirection ))!= NULL )
153148{
154149TM_Result htsu ;
@@ -169,10 +164,6 @@ pgrowlocks(PG_FUNCTION_ARGS)
169164 */
170165if (htsu == TM_BeingModified )
171166{
172- char * * values ;
173-
174- values = (char * * )palloc (mydata -> ncolumns * sizeof (char * ));
175-
176167values [Atnum_tid ]= (char * )DirectFunctionCall1 (tidout ,
177168PointerGetDatum (& tuple -> t_self ));
178169
@@ -297,16 +288,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
297288
298289/* build a tuple */
299290tuple = BuildTupleFromCStrings (attinmeta ,values );
300-
301- /* make the tuple into a datum */
302- result = HeapTupleGetDatum (tuple );
303-
304- /*
305- * no need to pfree what we allocated; it's on a short-lived
306- * memory context anyway
307- */
308-
309- SRF_RETURN_NEXT (funcctx ,result );
291+ tuplestore_puttuple (tupstore ,tuple );
310292}
311293else
312294{
@@ -315,7 +297,6 @@ pgrowlocks(PG_FUNCTION_ARGS)
315297}
316298
317299table_endscan (scan );
318- table_close (mydata -> rel ,AccessShareLock );
319-
320- SRF_RETURN_DONE (funcctx );
300+ table_close (rel ,AccessShareLock );
301+ return (Datum )0 ;
321302}