@ -54,13 +54,6 @@ PG_FUNCTION_INFO_V1(pgrowlocks);
# define NCHARS 32
typedef struct
{
Relation rel ;
TableScanDesc scan ;
int ncolumns ;
} MyData ;
# define Atnum_tid 0
# define Atnum_xmax 1
# define Atnum_ismulti 2
@ -71,84 +64,86 @@ typedef struct
Datum
pgrowlocks ( PG_FUNCTION_ARGS )
{
FuncCallContext * funcctx ;
TableScanDesc scan ;
HeapScanDesc hscan ;
HeapTuple tuple ;
text * relname = PG_GETARG_TEXT_PP ( 0 ) ;
ReturnSetInfo * rsinfo = ( ReturnSetInfo * ) fcinfo - > resultinfo ;
bool randomAccess ;
TupleDesc tupdesc ;
Tuplestorestate * tupstore ;
AttInMetadata * attinmeta ;
Datum result ;
MyData * mydata ;
Relation rel ;
RangeVar * relrv ;
TableScanDesc scan ;
HeapScanDesc hscan ;
HeapTuple tuple ;
MemoryContext oldcontext ;
AclResult aclresult ;
char * * values ;
/* check to see if caller supports us returning a tuplestore */
if ( rsinfo = = NULL | | ! IsA ( rsinfo , ReturnSetInfo ) )
ereport ( ERROR ,
( errcode ( ERRCODE_FEATURE_NOT_SUPPORTED ) ,
errmsg ( " set-valued function called in context that cannot accept a set " ) ) ) ;
if ( ! ( rsinfo - > allowedModes & SFRM_Materialize ) )
ereport ( ERROR ,
( errcode ( ERRCODE_SYNTAX_ERROR ) ,
errmsg ( " materialize mode required, but it is not allowed in this context " ) ) ) ;
/* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
oldcontext = MemoryContextSwitchTo ( rsinfo - > econtext - > ecxt_per_query_memory ) ;
if ( get_call_result_type ( fcinfo , NULL , & tupdesc ) ! = TYPEFUNC_COMPOSITE )
elog ( ERROR , " return type must be a row type " ) ;
randomAccess = ( rsinfo - > allowedModes & SFRM_Materialize_Random ) ! = 0 ;
tupstore = tuplestore_begin_heap ( randomAccess , false , work_mem ) ;
rsinfo - > returnMode = SFRM_Materialize ;
rsinfo - > setResult = tupstore ;
rsinfo - > setDesc = tupdesc ;
MemoryContextSwitchTo ( oldcontext ) ;
/* Access the table */
relrv = makeRangeVarFromNameList ( textToQualifiedNameList ( relname ) ) ;
rel = relation_openrv ( relrv , AccessShareLock ) ;
if ( rel - > rd_rel - > relam ! = HEAP_TABLE_AM_OID )
ereport ( ERROR , ( errcode ( ERRCODE_FEATURE_NOT_SUPPORTED ) ,
errmsg ( " only heap AM is supported " ) ) ) ;
if ( rel - > rd_rel - > relkind = = RELKIND_PARTITIONED_TABLE )
ereport ( ERROR ,
( errcode ( ERRCODE_WRONG_OBJECT_TYPE ) ,
errmsg ( " \" %s \" is a partitioned table " ,
RelationGetRelationName ( rel ) ) ,
errdetail ( " Partitioned tables do not contain rows. " ) ) ) ;
else if ( rel - > rd_rel - > relkind ! = RELKIND_RELATION )
ereport ( ERROR ,
( errcode ( ERRCODE_WRONG_OBJECT_TYPE ) ,
errmsg ( " \" %s \" is not a table " ,
RelationGetRelationName ( rel ) ) ) ) ;
/*
* check permissions : must have SELECT on table or be in
* pg_stat_scan_tables
*/
aclresult = pg_class_aclcheck ( RelationGetRelid ( rel ) , GetUserId ( ) ,
ACL_SELECT ) ;
if ( aclresult ! = ACLCHECK_OK )
aclresult = is_member_of_role ( GetUserId ( ) , DEFAULT_ROLE_STAT_SCAN_TABLES ) ? ACLCHECK_OK : ACLCHECK_NO_PRIV ;
if ( aclresult ! = ACLCHECK_OK )
aclcheck_error ( aclresult , get_relkind_objtype ( rel - > rd_rel - > relkind ) ,
RelationGetRelationName ( rel ) ) ;
/* Scan the relation */
scan = table_beginscan ( rel , GetActiveSnapshot ( ) , 0 , NULL ) ;
hscan = ( HeapScanDesc ) scan ;
if ( SRF_IS_FIRSTCALL ( ) )
{
text * relname ;
RangeVar * relrv ;
MemoryContext oldcontext ;
AclResult aclresult ;
funcctx = SRF_FIRSTCALL_INIT ( ) ;
oldcontext = MemoryContextSwitchTo ( funcctx - > multi_call_memory_ctx ) ;
/* Build a tuple descriptor for our result type */
if ( get_call_result_type ( fcinfo , NULL , & tupdesc ) ! = TYPEFUNC_COMPOSITE )
elog ( ERROR , " return type must be a row type " ) ;
attinmeta = TupleDescGetAttInMetadata ( tupdesc ) ;
funcctx - > attinmeta = attinmeta ;
relname = PG_GETARG_TEXT_PP ( 0 ) ;
relrv = makeRangeVarFromNameList ( textToQualifiedNameList ( relname ) ) ;
rel = relation_openrv ( relrv , AccessShareLock ) ;
if ( rel - > rd_rel - > relam ! = HEAP_TABLE_AM_OID )
ereport ( ERROR , ( errcode ( ERRCODE_FEATURE_NOT_SUPPORTED ) ,
errmsg ( " only heap AM is supported " ) ) ) ;
if ( rel - > rd_rel - > relkind = = RELKIND_PARTITIONED_TABLE )
ereport ( ERROR ,
( errcode ( ERRCODE_WRONG_OBJECT_TYPE ) ,
errmsg ( " \" %s \" is a partitioned table " ,
RelationGetRelationName ( rel ) ) ,
errdetail ( " Partitioned tables do not contain rows. " ) ) ) ;
else if ( rel - > rd_rel - > relkind ! = RELKIND_RELATION )
ereport ( ERROR ,
( errcode ( ERRCODE_WRONG_OBJECT_TYPE ) ,
errmsg ( " \" %s \" is not a table " ,
RelationGetRelationName ( rel ) ) ) ) ;
/*
* check permissions : must have SELECT on table or be in
* pg_stat_scan_tables
*/
aclresult = pg_class_aclcheck ( RelationGetRelid ( rel ) , GetUserId ( ) ,
ACL_SELECT ) ;
if ( aclresult ! = ACLCHECK_OK )
aclresult = is_member_of_role ( GetUserId ( ) , DEFAULT_ROLE_STAT_SCAN_TABLES ) ? ACLCHECK_OK : ACLCHECK_NO_PRIV ;
if ( aclresult ! = ACLCHECK_OK )
aclcheck_error ( aclresult , get_relkind_objtype ( rel - > rd_rel - > relkind ) ,
RelationGetRelationName ( rel ) ) ;
scan = table_beginscan ( rel , GetActiveSnapshot ( ) , 0 , NULL ) ;
hscan = ( HeapScanDesc ) scan ;
mydata = palloc ( sizeof ( * mydata ) ) ;
mydata - > rel = rel ;
mydata - > scan = scan ;
mydata - > ncolumns = tupdesc - > natts ;
funcctx - > user_fctx = mydata ;
MemoryContextSwitchTo ( oldcontext ) ;
}
attinmeta = TupleDescGetAttInMetadata ( tupdesc ) ;
funcctx = SRF_PERCALL_SETUP ( ) ;
attinmeta = funcctx - > attinmeta ;
mydata = ( MyData * ) funcctx - > user_fctx ;
scan = mydata - > scan ;
hscan = ( HeapScanDesc ) scan ;
values = ( char * * ) palloc ( tupdesc - > natts * sizeof ( char * ) ) ;
/* scan the relation */
while ( ( tuple = heap_getnext ( scan , ForwardScanDirection ) ) ! = NULL )
{
TM_Result htsu ;
@ -169,10 +164,6 @@ pgrowlocks(PG_FUNCTION_ARGS)
*/
if ( htsu = = TM_BeingModified )
{
char * * values ;
values = ( char * * ) palloc ( mydata - > ncolumns * sizeof ( char * ) ) ;
values [ Atnum_tid ] = ( char * ) DirectFunctionCall1 ( tidout ,
PointerGetDatum ( & tuple - > t_self ) ) ;
@ -297,16 +288,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
/* build a tuple */
tuple = BuildTupleFromCStrings ( attinmeta , values ) ;
/* make the tuple into a datum */
result = HeapTupleGetDatum ( tuple ) ;
/*
* no need to pfree what we allocated ; it ' s on a short - lived
* memory context anyway
*/
SRF_RETURN_NEXT ( funcctx , result ) ;
tuplestore_puttuple ( tupstore , tuple ) ;
}
else
{
@ -315,7 +297,6 @@ pgrowlocks(PG_FUNCTION_ARGS)
}
table_endscan ( scan ) ;
table_close ( mydata - > rel , AccessShareLock ) ;
SRF_RETURN_DONE ( funcctx ) ;
table_close ( rel , AccessShareLock ) ;
return ( Datum ) 0 ;
}