@@ -54,13 +54,6 @@ PG_FUNCTION_INFO_V1(pgrowlocks);
54
54
55
55
#define NCHARS 32
56
56
57
- typedef struct
58
- {
59
- Relation rel ;
60
- TableScanDesc scan ;
61
- int ncolumns ;
62
- } MyData ;
63
-
64
57
#define Atnum_tid 0
65
58
#define Atnum_xmax 1
66
59
#define Atnum_ismulti 2
@@ -71,84 +64,86 @@ typedef struct
71
64
Datum
72
65
pgrowlocks (PG_FUNCTION_ARGS )
73
66
{
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 ;
78
70
TupleDesc tupdesc ;
71
+ Tuplestorestate * tupstore ;
79
72
AttInMetadata * attinmeta ;
80
- Datum result ;
81
- MyData * mydata ;
82
73
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 ;
83
142
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 );
144
144
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 * ));
150
146
151
- /* scan the relation */
152
147
while ((tuple = heap_getnext (scan , ForwardScanDirection )) != NULL )
153
148
{
154
149
TM_Result htsu ;
@@ -169,10 +164,6 @@ pgrowlocks(PG_FUNCTION_ARGS)
169
164
*/
170
165
if (htsu == TM_BeingModified )
171
166
{
172
- char * * values ;
173
-
174
- values = (char * * ) palloc (mydata -> ncolumns * sizeof (char * ));
175
-
176
167
values [Atnum_tid ] = (char * ) DirectFunctionCall1 (tidout ,
177
168
PointerGetDatum (& tuple -> t_self ));
178
169
@@ -297,16 +288,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
297
288
298
289
/* build a tuple */
299
290
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 );
310
292
}
311
293
else
312
294
{
@@ -315,7 +297,6 @@ pgrowlocks(PG_FUNCTION_ARGS)
315
297
}
316
298
317
299
table_endscan (scan );
318
- table_close (mydata -> rel , AccessShareLock );
319
-
320
- SRF_RETURN_DONE (funcctx );
300
+ table_close (rel , AccessShareLock );
301
+ return (Datum ) 0 ;
321
302
}
0 commit comments