@@ -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 ;
7870 TupleDesc tupdesc ;
71+ Tuplestorestate * tupstore ;
7972 AttInMetadata * attinmeta ;
80- Datum result ;
81- MyData * mydata ;
8273 Relation 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 */
152147 while ((tuple = heap_getnext (scan , ForwardScanDirection )) != NULL )
153148 {
154149 TM_Result htsu ;
@@ -169,10 +164,6 @@ pgrowlocks(PG_FUNCTION_ARGS)
169164 */
170165 if (htsu == TM_BeingModified )
171166 {
172- char * * values ;
173-
174- values = (char * * ) palloc (mydata -> ncolumns * sizeof (char * ));
175-
176167 values [Atnum_tid ] = (char * ) DirectFunctionCall1 (tidout ,
177168 PointerGetDatum (& tuple -> t_self ));
178169
@@ -297,16 +288,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
297288
298289 /* build a tuple */
299290 tuple = 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 }
311293 else
312294 {
@@ -315,7 +297,6 @@ pgrowlocks(PG_FUNCTION_ARGS)
315297 }
316298
317299 table_endscan (scan );
318- table_close (mydata -> rel , AccessShareLock );
319-
320- SRF_RETURN_DONE (funcctx );
300+ table_close (rel , AccessShareLock );
301+ return (Datum ) 0 ;
321302}
0 commit comments