#include "postgres.h" #include "access/heapam.h" #include "catalog/pg_type.h" #include "funcapi.h" #include "utils/acl.h" #include "utils/builtins.h" PG_MODULE_MAGIC; static const char * convert_aclright_to_string(int aclright) { switch (aclright) { case ACL_INSERT: return "INSERT"; case ACL_SELECT: return "SELECT"; case ACL_UPDATE: return "UPDATE"; case ACL_DELETE: return "DELETE"; #if defined(ACL_TRUNCATE) case ACL_TRUNCATE: return "TRUNCATE"; #endif case ACL_REFERENCES: return "REFERENCES"; case ACL_TRIGGER: return "TRIGGER"; case ACL_EXECUTE: return "EXECUTE"; case ACL_USAGE: return "USAGE"; case ACL_CREATE: return "CREATE"; case ACL_CREATE_TEMP: return "TEMPORARY"; case ACL_CONNECT: return "CONNECT"; default: elog(ERROR, "unrecognized aclright: %d", aclright); return NULL; } } /*---------- * Convert an aclitem[] to a table. * * Example: * * aclexplode('{=r/joe,foo=a*w/joe}'::aclitem[]) * * returns the table * * {{ OID(joe), 0::OID, 'SELECT', false }, * { OID(joe), OID(foo), 'INSERT', true }, * { OID(joe), OID(foo), 'UPDATE', false }} *---------- */ PG_FUNCTION_INFO_V1(aclexplode); Datum aclexplode(PG_FUNCTION_ARGS) { Acl *acl = PG_GETARG_ACL_P(0); FuncCallContext *funcctx; int *idx; AclItem *aidat; if (SRF_IS_FIRSTCALL()) { TupleDesc tupdesc; MemoryContext oldcontext; /* check_acl() */ if (ARR_ELEMTYPE(acl) != ACLITEMOID) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("ACL array contains wrong data type"))); if (ARR_NDIM(acl) != 1) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("ACL arrays must be one-dimensional"))); if (ARR_HASNULL(acl)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("ACL arrays must not contain null values"))); funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); /* * build tupdesc for result tuples (matches out parameters in pg_proc * entry) */ tupdesc = CreateTemplateTupleDesc(4, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "grantor", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "grantee", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "privilege_type", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 4, "is_grantable", BOOLOID, -1, 0); funcctx->tuple_desc = BlessTupleDesc(tupdesc); /* allocate memory for user context */ idx = (int *) palloc(sizeof(int[2])); idx[0] = 0; /* ACL array item index */ idx[1] = -1; /* privilege type counter */ funcctx->user_fctx = (void *) idx; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); idx = (int *) funcctx->user_fctx; aidat = ACL_DAT(acl); /* need test here in case acl has no items */ while (idx[0] < ACL_NUM(acl)) { AclItem *aidata; AclMode priv_bit; idx[1]++; if (idx[1] == N_ACL_RIGHTS) { idx[1] = 0; idx[0]++; if (idx[0] >= ACL_NUM(acl)) /* done */ break; } aidata = &aidat[idx[0]]; priv_bit = 1 << idx[1]; if (ACLITEM_GET_PRIVS(*aidata) & priv_bit) { Datum result; Datum values[4]; bool nulls[4]; HeapTuple tuple; values[0] = ObjectIdGetDatum(aidata->ai_grantor); values[1] = ObjectIdGetDatum(aidata->ai_grantee); values[2] = DirectFunctionCall1(textin, CStringGetDatum(convert_aclright_to_string(priv_bit))); values[3] = BoolGetDatum((ACLITEM_GET_GOPTIONS(*aidata) & priv_bit) != 0); MemSet(nulls, 0, sizeof(nulls)); tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } } SRF_RETURN_DONE(funcctx); }