diff options
Diffstat (limited to 'contrib/pgrowlocks/pgrowlocks.c')
-rw-r--r-- | contrib/pgrowlocks/pgrowlocks.c | 173 |
1 files changed, 77 insertions, 96 deletions
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c index a2c44a916cf..714398831bc 100644 --- a/contrib/pgrowlocks/pgrowlocks.c +++ b/contrib/pgrowlocks/pgrowlocks.c @@ -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; } |