Harden has_xxx_privilege() functions against concurrent object drops.

The versions of these functions that accept object OIDs are supposed
to return NULL, rather than failing, if the target object has been
dropped.  This makes it safe(r) to use them in queries that scan
catalogs, since the functions will be applied to objects that are
visible in the query's snapshot but might now be gone according to
the catalog snapshot.  In most cases we implemented this by doing
a SearchSysCacheExists test and assuming that if that succeeds, we
can safely invoke the appropriate aclchk.c function, which will
immediately re-fetch the same syscache entry.  It was argued that
if the existence test succeeds then the followup fetch must succeed
as well, for lack of any intervening AcceptInvalidationMessages call.

Alexander Lakhin demonstrated that this is not so when
CATCACHE_FORCE_RELEASE is enabled: the syscache entry will be forcibly
dropped at the end of SearchSysCacheExists, and then it is possible
for the catalog snapshot to get advanced while re-fetching the entry.
Alexander's test case requires the operation to happen inside a
parallel worker, but that seems incidental to the fundamental problem.
What remains obscure is whether there is a way for this to happen in a
non-debug build.  Nonetheless, CATCACHE_FORCE_RELEASE is a very useful
test methodology, so we'd better make the code safe for it.

After some discussion we concluded that the most future-proof fix
is to give up the assumption that checking SearchSysCacheExists can
guarantee success of a later fetch.  At best that assumption leads
to fragile code --- for example, has_type_privilege appears broken
for array types even if you believe the assumption holds.  And it's
not even particularly efficient.

There had already been some work towards extending the aclchk.c
APIs to include "is_missing" output flags, so this patch extends
that work to cover all the aclchk.c functions that are used by the
has_xxx_privilege() functions.  (This allows getting rid of some
ad-hoc decisions about not throwing errors in certain places in
aclchk.c.)

In passing, this fixes the has_sequence_privilege() functions to
provide the same guarantees as their cousins: for some reason the
SearchSysCacheExists tests never got added to those.

There is more work to do to remove the unsafe coding pattern with
SearchSysCacheExists in other places, but this is a pretty
self-contained patch so I'll commit it separately.

Per bug #18014 from Alexander Lakhin.  Given the lack of hard evidence
that there's a bug in non-debug builds, I'm content to fix this only
in HEAD.  (Perhaps we should clean up the has_sequence_privilege()
oversight in the back branches, but in the absence of field complaints
I'm not too excited about that either.)

Discussion: https://postgr.es/m/18014-28c81cb79d44295d@postgresql.org
This commit is contained in:
Tom Lane 2023-10-14 14:49:50 -04:00
parent 22655aa231
commit 403ac226dd
3 changed files with 402 additions and 160 deletions

View File

@ -13,6 +13,27 @@
* NOTES
* See acl.h.
*
* The xxx_aclmask() functions in this file are wrappers around
* acl.c's aclmask() function; see that for basic usage information.
* The wrapper functions add object-type-specific lookup capability.
* Generally, they will throw error if the object doesn't exist.
*
* The xxx_aclmask_ext() functions add the ability to not throw
* error if the object doesn't exist. If their "is_missing" argument
* isn't NULL, then when the object isn't found they will set
* *is_missing = true and return zero (no privileges) instead of
* throwing an error. Caller must initialize *is_missing = false.
*
* The xxx_aclcheck() functions are simplified wrappers around the
* corresponding xxx_aclmask() functions, simply returning ACLCHECK_OK
* if any of the privileges specified in "mode" are held, and otherwise
* a suitable error code (in practice, always ACLCHECK_NO_PRIV).
* Again, they will throw error if the object doesn't exist.
*
* The xxx_aclcheck_ext() functions add the ability to not throw
* error if the object doesn't exist. Their "is_missing" argument
* works similarly to the xxx_aclmask_ext() functions.
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
@ -139,6 +160,9 @@ static AclMode pg_aclmask(ObjectType objtype, Oid object_oid, AttrNumber attnum,
Oid roleid, AclMode mask, AclMaskHow how);
static AclMode object_aclmask(Oid classid, Oid objectid, Oid roleid,
AclMode mask, AclMaskHow how);
static AclMode object_aclmask_ext(Oid classid, Oid objectid, Oid roleid,
AclMode mask, AclMaskHow how,
bool *is_missing);
static AclMode pg_attribute_aclmask(Oid table_oid, AttrNumber attnum,
Oid roleid, AclMode mask, AclMaskHow how);
static AclMode pg_attribute_aclmask_ext(Oid table_oid, AttrNumber attnum,
@ -151,10 +175,12 @@ static AclMode pg_parameter_acl_aclmask(Oid acl_oid, Oid roleid,
AclMode mask, AclMaskHow how);
static AclMode pg_largeobject_aclmask_snapshot(Oid lobj_oid, Oid roleid,
AclMode mask, AclMaskHow how, Snapshot snapshot);
static AclMode pg_namespace_aclmask(Oid nsp_oid, Oid roleid,
AclMode mask, AclMaskHow how);
static AclMode pg_type_aclmask(Oid type_oid, Oid roleid,
AclMode mask, AclMaskHow how);
static AclMode pg_namespace_aclmask_ext(Oid nsp_oid, Oid roleid,
AclMode mask, AclMaskHow how,
bool *is_missing);
static AclMode pg_type_aclmask_ext(Oid type_oid, Oid roleid,
AclMode mask, AclMaskHow how,
bool *is_missing);
static void recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid,
Acl *new_acl);
static void recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid,
@ -3064,6 +3090,18 @@ pg_aclmask(ObjectType objtype, Oid object_oid, AttrNumber attnum, Oid roleid,
static AclMode
object_aclmask(Oid classid, Oid objectid, Oid roleid,
AclMode mask, AclMaskHow how)
{
return object_aclmask_ext(classid, objectid, roleid, mask, how, NULL);
}
/*
* Generic routine for examining a user's privileges for an object,
* with is_missing
*/
static AclMode
object_aclmask_ext(Oid classid, Oid objectid, Oid roleid,
AclMode mask, AclMaskHow how,
bool *is_missing)
{
int cacheid;
AclMode result;
@ -3077,9 +3115,11 @@ object_aclmask(Oid classid, Oid objectid, Oid roleid,
switch (classid)
{
case NamespaceRelationId:
return pg_namespace_aclmask(objectid, roleid, mask, how);
return pg_namespace_aclmask_ext(objectid, roleid, mask, how,
is_missing);
case TypeRelationId:
return pg_type_aclmask(objectid, roleid, mask, how);
return pg_type_aclmask_ext(objectid, roleid, mask, how,
is_missing);
}
/* Even more special cases */
@ -3092,16 +3132,26 @@ object_aclmask(Oid classid, Oid objectid, Oid roleid,
return mask;
/*
* Get the objects's ACL from its catalog
* Get the object's ACL from its catalog
*/
cacheid = get_object_catcache_oid(classid);
tuple = SearchSysCache1(cacheid, ObjectIdGetDatum(objectid));
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_DATABASE),
errmsg("%s with OID %u does not exist", get_object_class_descr(classid), objectid)));
{
if (is_missing != NULL)
{
/* return "no privileges" instead of throwing an error */
*is_missing = true;
return 0;
}
else
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("%s with OID %u does not exist",
get_object_class_descr(classid), objectid)));
}
ownerId = DatumGetObjectId(SysCacheGetAttrNotNull(cacheid,
tuple,
@ -3149,10 +3199,7 @@ pg_attribute_aclmask(Oid table_oid, AttrNumber attnum, Oid roleid,
}
/*
* Routine for examining a user's privileges for a column
*
* Does the bulk of the work for pg_attribute_aclmask(), and allows other
* callers to avoid the missing attribute ERROR when is_missing is non-NULL.
* Routine for examining a user's privileges for a column, with is_missing
*/
static AclMode
pg_attribute_aclmask_ext(Oid table_oid, AttrNumber attnum, Oid roleid,
@ -3226,15 +3273,24 @@ pg_attribute_aclmask_ext(Oid table_oid, AttrNumber attnum, Oid roleid,
* Must get the relation's ownerId from pg_class. Since we already found
* a pg_attribute entry, the only likely reason for this to fail is that a
* concurrent DROP of the relation committed since then (which could only
* happen if we don't have lock on the relation). We prefer to report "no
* privileges" rather than failing in such a case, so as to avoid unwanted
* failures in has_column_privilege() tests.
* happen if we don't have lock on the relation). Treat that similarly to
* not finding the attribute entry.
*/
classTuple = SearchSysCache1(RELOID, ObjectIdGetDatum(table_oid));
if (!HeapTupleIsValid(classTuple))
{
ReleaseSysCache(attTuple);
return 0;
if (is_missing != NULL)
{
/* return "no privileges" instead of throwing an error */
*is_missing = true;
return 0;
}
else
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_TABLE),
errmsg("relation with OID %u does not exist",
table_oid)));
}
classForm = (Form_pg_class) GETSTRUCT(classTuple);
@ -3267,10 +3323,7 @@ pg_class_aclmask(Oid table_oid, Oid roleid,
}
/*
* Routine for examining a user's privileges for a table
*
* Does the bulk of the work for pg_class_aclmask(), and allows other
* callers to avoid the missing relation ERROR when is_missing is non-NULL.
* Routine for examining a user's privileges for a table, with is_missing
*/
static AclMode
pg_class_aclmask_ext(Oid table_oid, Oid roleid, AclMode mask,
@ -3585,11 +3638,12 @@ pg_largeobject_aclmask_snapshot(Oid lobj_oid, Oid roleid,
}
/*
* Routine for examining a user's privileges for a namespace
* Routine for examining a user's privileges for a namespace, with is_missing
*/
static AclMode
pg_namespace_aclmask(Oid nsp_oid, Oid roleid,
AclMode mask, AclMaskHow how)
pg_namespace_aclmask_ext(Oid nsp_oid, Oid roleid,
AclMode mask, AclMaskHow how,
bool *is_missing)
{
AclMode result;
HeapTuple tuple;
@ -3623,8 +3677,8 @@ pg_namespace_aclmask(Oid nsp_oid, Oid roleid,
*/
if (isTempNamespace(nsp_oid))
{
if (object_aclcheck(DatabaseRelationId, MyDatabaseId, roleid,
ACL_CREATE_TEMP) == ACLCHECK_OK)
if (object_aclcheck_ext(DatabaseRelationId, MyDatabaseId, roleid,
ACL_CREATE_TEMP, is_missing) == ACLCHECK_OK)
return mask & ACL_ALL_RIGHTS_SCHEMA;
else
return mask & ACL_USAGE;
@ -3635,9 +3689,18 @@ pg_namespace_aclmask(Oid nsp_oid, Oid roleid,
*/
tuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(nsp_oid));
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_SCHEMA),
errmsg("schema with OID %u does not exist", nsp_oid)));
{
if (is_missing != NULL)
{
/* return "no privileges" instead of throwing an error */
*is_missing = true;
return 0;
}
else
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_SCHEMA),
errmsg("schema with OID %u does not exist", nsp_oid)));
}
ownerId = ((Form_pg_namespace) GETSTRUCT(tuple))->nspowner;
@ -3677,20 +3740,20 @@ pg_namespace_aclmask(Oid nsp_oid, Oid roleid,
}
/*
* Routine for examining a user's privileges for a type.
* Routine for examining a user's privileges for a type, with is_missing
*/
static AclMode
pg_type_aclmask(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how)
pg_type_aclmask_ext(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how,
bool *is_missing)
{
AclMode result;
HeapTuple tuple;
Form_pg_type typeForm;
Datum aclDatum;
bool isNull;
Acl *acl;
Oid ownerId;
Form_pg_type typeForm;
/* Bypass permission checks for superusers */
if (superuser_arg(roleid))
return mask;
@ -3700,10 +3763,19 @@ pg_type_aclmask(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how)
*/
tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_oid));
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("type with OID %u does not exist",
type_oid)));
{
if (is_missing != NULL)
{
/* return "no privileges" instead of throwing an error */
*is_missing = true;
return 0;
}
else
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("type with OID %u does not exist",
type_oid)));
}
typeForm = (Form_pg_type) GETSTRUCT(tuple);
/*
@ -3717,9 +3789,20 @@ pg_type_aclmask(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how)
ReleaseSysCache(tuple);
tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(elttype_oid));
/* this case is not a user-facing error, so elog not ereport */
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for type %u", elttype_oid);
{
if (is_missing != NULL)
{
/* return "no privileges" instead of throwing an error */
*is_missing = true;
return 0;
}
else
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("type with OID %u does not exist",
elttype_oid)));
}
typeForm = (Form_pg_type) GETSTRUCT(tuple);
}
@ -3759,7 +3842,20 @@ pg_type_aclmask(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how)
AclResult
object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
{
if (object_aclmask(classid, objectid, roleid, mode, ACLMASK_ANY) != 0)
return object_aclcheck_ext(classid, objectid, roleid, mode, NULL);
}
/*
* Exported generic routine for checking a user's access privileges to an
* object, with is_missing
*/
AclResult
object_aclcheck_ext(Oid classid, Oid objectid,
Oid roleid, AclMode mode,
bool *is_missing)
{
if (object_aclmask_ext(classid, objectid, roleid, mode, ACLMASK_ANY,
is_missing) != 0)
return ACLCHECK_OK;
else
return ACLCHECK_NO_PRIV;
@ -3784,10 +3880,8 @@ pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum,
/*
* Exported routine for checking a user's access privileges to a column
*
* Does the bulk of the work for pg_attribute_aclcheck(), and allows other
* callers to avoid the missing attribute ERROR when is_missing is non-NULL.
* Exported routine for checking a user's access privileges to a column,
* with is_missing
*/
AclResult
pg_attribute_aclcheck_ext(Oid table_oid, AttrNumber attnum,
@ -3822,23 +3916,47 @@ pg_attribute_aclcheck_ext(Oid table_oid, AttrNumber attnum,
AclResult
pg_attribute_aclcheck_all(Oid table_oid, Oid roleid, AclMode mode,
AclMaskHow how)
{
return pg_attribute_aclcheck_all_ext(table_oid, roleid, mode, how, NULL);
}
/*
* Exported routine for checking a user's access privileges to any/all columns,
* with is_missing
*/
AclResult
pg_attribute_aclcheck_all_ext(Oid table_oid, Oid roleid,
AclMode mode, AclMaskHow how,
bool *is_missing)
{
AclResult result;
HeapTuple classTuple;
Form_pg_class classForm;
Oid ownerId;
AttrNumber nattrs;
AttrNumber curr_att;
/*
* Must fetch pg_class row to check number of attributes. As in
* pg_attribute_aclmask, we prefer to return "no privileges" instead of
* throwing an error if we get any unexpected lookup errors.
* Must fetch pg_class row to get owner ID and number of attributes.
*/
classTuple = SearchSysCache1(RELOID, ObjectIdGetDatum(table_oid));
if (!HeapTupleIsValid(classTuple))
return ACLCHECK_NO_PRIV;
{
if (is_missing != NULL)
{
/* return "no privileges" instead of throwing an error */
*is_missing = true;
return ACLCHECK_NO_PRIV;
}
else
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_TABLE),
errmsg("relation with OID %u does not exist",
table_oid)));
}
classForm = (Form_pg_class) GETSTRUCT(classTuple);
ownerId = classForm->relowner;
nattrs = classForm->relnatts;
ReleaseSysCache(classTuple);
@ -3852,11 +3970,20 @@ pg_attribute_aclcheck_all(Oid table_oid, Oid roleid, AclMode mode,
for (curr_att = 1; curr_att <= nattrs; curr_att++)
{
HeapTuple attTuple;
Datum aclDatum;
bool isNull;
Acl *acl;
AclMode attmask;
attTuple = SearchSysCache2(ATTNUM,
ObjectIdGetDatum(table_oid),
Int16GetDatum(curr_att));
/*
* Lookup failure probably indicates that the table was just dropped,
* but we'll treat it the same as a dropped column rather than
* throwing error.
*/
if (!HeapTupleIsValid(attTuple))
continue;
@ -3867,16 +3994,27 @@ pg_attribute_aclcheck_all(Oid table_oid, Oid roleid, AclMode mode,
continue;
}
aclDatum = SysCacheGetAttr(ATTNUM, attTuple, Anum_pg_attribute_attacl,
&isNull);
/*
* Here we hard-wire knowledge that the default ACL for a column
* grants no privileges, so that we can fall out quickly in the very
* common case where attacl is null.
*/
if (heap_attisnull(attTuple, Anum_pg_attribute_attacl, NULL))
if (isNull)
attmask = 0;
else
attmask = pg_attribute_aclmask(table_oid, curr_att, roleid,
mode, ACLMASK_ANY);
{
/* detoast column's ACL if necessary */
acl = DatumGetAclP(aclDatum);
attmask = aclmask(acl, roleid, ownerId, mode, ACLMASK_ANY);
/* if we have a detoasted copy, free it */
if ((Pointer) acl != DatumGetPointer(aclDatum))
pfree(acl);
}
ReleaseSysCache(attTuple);
@ -3911,10 +4049,8 @@ pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
}
/*
* Exported routine for checking a user's access privileges to a table
*
* Does the bulk of the work for pg_class_aclcheck(), and allows other
* callers to avoid the missing relation ERROR when is_missing is non-NULL.
* Exported routine for checking a user's access privileges to a table,
* with is_missing
*/
AclResult
pg_class_aclcheck_ext(Oid table_oid, Oid roleid,

View File

@ -1915,14 +1915,15 @@ has_table_privilege_name_id(PG_FUNCTION_ARGS)
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
roleid = get_role_oid_or_public(NameStr(*username));
mode = convert_table_priv_string(priv_type_text);
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
PG_RETURN_NULL();
aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing);
aclresult = pg_class_aclcheck(tableoid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -1941,14 +1942,15 @@ has_table_privilege_id(PG_FUNCTION_ARGS)
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
roleid = GetUserId();
mode = convert_table_priv_string(priv_type_text);
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
PG_RETURN_NULL();
aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing);
aclresult = pg_class_aclcheck(tableoid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -1989,13 +1991,14 @@ has_table_privilege_id_id(PG_FUNCTION_ARGS)
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
bool is_missing = false;
mode = convert_table_priv_string(priv_type_text);
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
PG_RETURN_NULL();
aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing);
aclresult = pg_class_aclcheck(tableoid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -2134,6 +2137,7 @@ has_sequence_privilege_name_id(PG_FUNCTION_ARGS)
AclMode mode;
AclResult aclresult;
char relkind;
bool is_missing = false;
roleid = get_role_oid_or_public(NameStr(*username));
mode = convert_sequence_priv_string(priv_type_text);
@ -2146,7 +2150,10 @@ has_sequence_privilege_name_id(PG_FUNCTION_ARGS)
errmsg("\"%s\" is not a sequence",
get_rel_name(sequenceoid))));
aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
aclresult = pg_class_aclcheck_ext(sequenceoid, roleid, mode, &is_missing);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -2166,6 +2173,7 @@ has_sequence_privilege_id(PG_FUNCTION_ARGS)
AclMode mode;
AclResult aclresult;
char relkind;
bool is_missing = false;
roleid = GetUserId();
mode = convert_sequence_priv_string(priv_type_text);
@ -2178,7 +2186,10 @@ has_sequence_privilege_id(PG_FUNCTION_ARGS)
errmsg("\"%s\" is not a sequence",
get_rel_name(sequenceoid))));
aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
aclresult = pg_class_aclcheck_ext(sequenceoid, roleid, mode, &is_missing);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -2225,6 +2236,7 @@ has_sequence_privilege_id_id(PG_FUNCTION_ARGS)
AclMode mode;
AclResult aclresult;
char relkind;
bool is_missing = false;
mode = convert_sequence_priv_string(priv_type_text);
relkind = get_rel_relkind(sequenceoid);
@ -2236,7 +2248,10 @@ has_sequence_privilege_id_id(PG_FUNCTION_ARGS)
errmsg("\"%s\" is not a sequence",
get_rel_name(sequenceoid))));
aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
aclresult = pg_class_aclcheck_ext(sequenceoid, roleid, mode, &is_missing);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -2345,18 +2360,22 @@ has_any_column_privilege_name_id(PG_FUNCTION_ARGS)
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
roleid = get_role_oid_or_public(NameStr(*username));
mode = convert_column_priv_string(priv_type_text);
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
PG_RETURN_NULL();
/* First check at table level, then examine each column if needed */
aclresult = pg_class_aclcheck(tableoid, roleid, mode);
aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing);
if (aclresult != ACLCHECK_OK)
aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
ACLMASK_ANY);
{
if (is_missing)
PG_RETURN_NULL();
aclresult = pg_attribute_aclcheck_all_ext(tableoid, roleid, mode,
ACLMASK_ANY, &is_missing);
if (is_missing)
PG_RETURN_NULL();
}
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -2375,18 +2394,22 @@ has_any_column_privilege_id(PG_FUNCTION_ARGS)
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
roleid = GetUserId();
mode = convert_column_priv_string(priv_type_text);
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
PG_RETURN_NULL();
/* First check at table level, then examine each column if needed */
aclresult = pg_class_aclcheck(tableoid, roleid, mode);
aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing);
if (aclresult != ACLCHECK_OK)
aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
ACLMASK_ANY);
{
if (is_missing)
PG_RETURN_NULL();
aclresult = pg_attribute_aclcheck_all_ext(tableoid, roleid, mode,
ACLMASK_ANY, &is_missing);
if (is_missing)
PG_RETURN_NULL();
}
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -2431,17 +2454,21 @@ has_any_column_privilege_id_id(PG_FUNCTION_ARGS)
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
bool is_missing = false;
mode = convert_column_priv_string(priv_type_text);
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
PG_RETURN_NULL();
/* First check at table level, then examine each column if needed */
aclresult = pg_class_aclcheck(tableoid, roleid, mode);
aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing);
if (aclresult != ACLCHECK_OK)
aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
ACLMASK_ANY);
{
if (is_missing)
PG_RETURN_NULL();
aclresult = pg_attribute_aclcheck_all_ext(tableoid, roleid, mode,
ACLMASK_ANY, &is_missing);
if (is_missing)
PG_RETURN_NULL();
}
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -2981,14 +3008,17 @@ has_database_privilege_name_id(PG_FUNCTION_ARGS)
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
roleid = get_role_oid_or_public(NameStr(*username));
mode = convert_database_priv_string(priv_type_text);
if (!SearchSysCacheExists1(DATABASEOID, ObjectIdGetDatum(databaseoid)))
PG_RETURN_NULL();
aclresult = object_aclcheck_ext(DatabaseRelationId, databaseoid,
roleid, mode,
&is_missing);
aclresult = object_aclcheck(DatabaseRelationId, databaseoid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -3007,14 +3037,17 @@ has_database_privilege_id(PG_FUNCTION_ARGS)
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
roleid = GetUserId();
mode = convert_database_priv_string(priv_type_text);
if (!SearchSysCacheExists1(DATABASEOID, ObjectIdGetDatum(databaseoid)))
PG_RETURN_NULL();
aclresult = object_aclcheck_ext(DatabaseRelationId, databaseoid,
roleid, mode,
&is_missing);
aclresult = object_aclcheck(DatabaseRelationId, databaseoid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -3055,13 +3088,16 @@ has_database_privilege_id_id(PG_FUNCTION_ARGS)
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
bool is_missing = false;
mode = convert_database_priv_string(priv_type_text);
if (!SearchSysCacheExists1(DATABASEOID, ObjectIdGetDatum(databaseoid)))
PG_RETURN_NULL();
aclresult = object_aclcheck_ext(DatabaseRelationId, databaseoid,
roleid, mode,
&is_missing);
aclresult = object_aclcheck(DatabaseRelationId, databaseoid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -3178,14 +3214,17 @@ has_foreign_data_wrapper_privilege_name_id(PG_FUNCTION_ARGS)
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
roleid = get_role_oid_or_public(NameStr(*username));
mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
if (!SearchSysCacheExists1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid)))
PG_RETURN_NULL();
aclresult = object_aclcheck_ext(ForeignDataWrapperRelationId, fdwid,
roleid, mode,
&is_missing);
aclresult = object_aclcheck(ForeignDataWrapperRelationId, fdwid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -3204,14 +3243,17 @@ has_foreign_data_wrapper_privilege_id(PG_FUNCTION_ARGS)
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
roleid = GetUserId();
mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
if (!SearchSysCacheExists1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid)))
PG_RETURN_NULL();
aclresult = object_aclcheck_ext(ForeignDataWrapperRelationId, fdwid,
roleid, mode,
&is_missing);
aclresult = object_aclcheck(ForeignDataWrapperRelationId, fdwid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -3252,13 +3294,16 @@ has_foreign_data_wrapper_privilege_id_id(PG_FUNCTION_ARGS)
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
bool is_missing = false;
mode = convert_foreign_data_wrapper_priv_string(priv_type_text);
if (!SearchSysCacheExists1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid)))
PG_RETURN_NULL();
aclresult = object_aclcheck_ext(ForeignDataWrapperRelationId, fdwid,
roleid, mode,
&is_missing);
aclresult = object_aclcheck(ForeignDataWrapperRelationId, fdwid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -3369,14 +3414,17 @@ has_function_privilege_name_id(PG_FUNCTION_ARGS)
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
roleid = get_role_oid_or_public(NameStr(*username));
mode = convert_function_priv_string(priv_type_text);
if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(functionoid)))
PG_RETURN_NULL();
aclresult = object_aclcheck_ext(ProcedureRelationId, functionoid,
roleid, mode,
&is_missing);
aclresult = object_aclcheck(ProcedureRelationId, functionoid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -3395,14 +3443,17 @@ has_function_privilege_id(PG_FUNCTION_ARGS)
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
roleid = GetUserId();
mode = convert_function_priv_string(priv_type_text);
if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(functionoid)))
PG_RETURN_NULL();
aclresult = object_aclcheck_ext(ProcedureRelationId, functionoid,
roleid, mode,
&is_missing);
aclresult = object_aclcheck(ProcedureRelationId, functionoid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -3443,13 +3494,16 @@ has_function_privilege_id_id(PG_FUNCTION_ARGS)
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
bool is_missing = false;
mode = convert_function_priv_string(priv_type_text);
if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(functionoid)))
PG_RETURN_NULL();
aclresult = object_aclcheck_ext(ProcedureRelationId, functionoid,
roleid, mode,
&is_missing);
aclresult = object_aclcheck(ProcedureRelationId, functionoid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -3569,14 +3623,17 @@ has_language_privilege_name_id(PG_FUNCTION_ARGS)
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
roleid = get_role_oid_or_public(NameStr(*username));
mode = convert_language_priv_string(priv_type_text);
if (!SearchSysCacheExists1(LANGOID, ObjectIdGetDatum(languageoid)))
PG_RETURN_NULL();
aclresult = object_aclcheck_ext(LanguageRelationId, languageoid,
roleid, mode,
&is_missing);
aclresult = object_aclcheck(LanguageRelationId, languageoid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -3595,14 +3652,17 @@ has_language_privilege_id(PG_FUNCTION_ARGS)
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
roleid = GetUserId();
mode = convert_language_priv_string(priv_type_text);
if (!SearchSysCacheExists1(LANGOID, ObjectIdGetDatum(languageoid)))
PG_RETURN_NULL();
aclresult = object_aclcheck_ext(LanguageRelationId, languageoid,
roleid, mode,
&is_missing);
aclresult = object_aclcheck(LanguageRelationId, languageoid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -3643,13 +3703,16 @@ has_language_privilege_id_id(PG_FUNCTION_ARGS)
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
bool is_missing = false;
mode = convert_language_priv_string(priv_type_text);
if (!SearchSysCacheExists1(LANGOID, ObjectIdGetDatum(languageoid)))
PG_RETURN_NULL();
aclresult = object_aclcheck_ext(LanguageRelationId, languageoid,
roleid, mode,
&is_missing);
aclresult = object_aclcheck(LanguageRelationId, languageoid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -3760,14 +3823,17 @@ has_schema_privilege_name_id(PG_FUNCTION_ARGS)
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
roleid = get_role_oid_or_public(NameStr(*username));
mode = convert_schema_priv_string(priv_type_text);
if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(schemaoid)))
PG_RETURN_NULL();
aclresult = object_aclcheck_ext(NamespaceRelationId, schemaoid,
roleid, mode,
&is_missing);
aclresult = object_aclcheck(NamespaceRelationId, schemaoid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -3786,14 +3852,17 @@ has_schema_privilege_id(PG_FUNCTION_ARGS)
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
roleid = GetUserId();
mode = convert_schema_priv_string(priv_type_text);
if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(schemaoid)))
PG_RETURN_NULL();
aclresult = object_aclcheck_ext(NamespaceRelationId, schemaoid,
roleid, mode,
&is_missing);
aclresult = object_aclcheck(NamespaceRelationId, schemaoid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -3834,13 +3903,16 @@ has_schema_privilege_id_id(PG_FUNCTION_ARGS)
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
bool is_missing = false;
mode = convert_schema_priv_string(priv_type_text);
if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(schemaoid)))
PG_RETURN_NULL();
aclresult = object_aclcheck_ext(NamespaceRelationId, schemaoid,
roleid, mode,
&is_missing);
aclresult = object_aclcheck(NamespaceRelationId, schemaoid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -3953,14 +4025,17 @@ has_server_privilege_name_id(PG_FUNCTION_ARGS)
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
roleid = get_role_oid_or_public(NameStr(*username));
mode = convert_server_priv_string(priv_type_text);
if (!SearchSysCacheExists1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid)))
PG_RETURN_NULL();
aclresult = object_aclcheck_ext(ForeignServerRelationId, serverid,
roleid, mode,
&is_missing);
aclresult = object_aclcheck(ForeignServerRelationId, serverid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -3979,14 +4054,17 @@ has_server_privilege_id(PG_FUNCTION_ARGS)
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
roleid = GetUserId();
mode = convert_server_priv_string(priv_type_text);
if (!SearchSysCacheExists1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid)))
PG_RETURN_NULL();
aclresult = object_aclcheck_ext(ForeignServerRelationId, serverid,
roleid, mode,
&is_missing);
aclresult = object_aclcheck(ForeignServerRelationId, serverid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -4027,13 +4105,16 @@ has_server_privilege_id_id(PG_FUNCTION_ARGS)
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
bool is_missing = false;
mode = convert_server_priv_string(priv_type_text);
if (!SearchSysCacheExists1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid)))
PG_RETURN_NULL();
aclresult = object_aclcheck_ext(ForeignServerRelationId, serverid,
roleid, mode,
&is_missing);
aclresult = object_aclcheck(ForeignServerRelationId, serverid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -4144,14 +4225,17 @@ has_tablespace_privilege_name_id(PG_FUNCTION_ARGS)
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
roleid = get_role_oid_or_public(NameStr(*username));
mode = convert_tablespace_priv_string(priv_type_text);
if (!SearchSysCacheExists1(TABLESPACEOID, ObjectIdGetDatum(tablespaceoid)))
PG_RETURN_NULL();
aclresult = object_aclcheck_ext(TableSpaceRelationId, tablespaceoid,
roleid, mode,
&is_missing);
aclresult = object_aclcheck(TableSpaceRelationId, tablespaceoid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -4170,14 +4254,17 @@ has_tablespace_privilege_id(PG_FUNCTION_ARGS)
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
roleid = GetUserId();
mode = convert_tablespace_priv_string(priv_type_text);
if (!SearchSysCacheExists1(TABLESPACEOID, ObjectIdGetDatum(tablespaceoid)))
PG_RETURN_NULL();
aclresult = object_aclcheck_ext(TableSpaceRelationId, tablespaceoid,
roleid, mode,
&is_missing);
aclresult = object_aclcheck(TableSpaceRelationId, tablespaceoid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -4218,13 +4305,16 @@ has_tablespace_privilege_id_id(PG_FUNCTION_ARGS)
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
bool is_missing = false;
mode = convert_tablespace_priv_string(priv_type_text);
if (!SearchSysCacheExists1(TABLESPACEOID, ObjectIdGetDatum(tablespaceoid)))
PG_RETURN_NULL();
aclresult = object_aclcheck_ext(TableSpaceRelationId, tablespaceoid,
roleid, mode,
&is_missing);
aclresult = object_aclcheck(TableSpaceRelationId, tablespaceoid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -4334,14 +4424,17 @@ has_type_privilege_name_id(PG_FUNCTION_ARGS)
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
roleid = get_role_oid_or_public(NameStr(*username));
mode = convert_type_priv_string(priv_type_text);
if (!SearchSysCacheExists1(TYPEOID, ObjectIdGetDatum(typeoid)))
PG_RETURN_NULL();
aclresult = object_aclcheck_ext(TypeRelationId, typeoid,
roleid, mode,
&is_missing);
aclresult = object_aclcheck(TypeRelationId, typeoid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -4360,14 +4453,17 @@ has_type_privilege_id(PG_FUNCTION_ARGS)
Oid roleid;
AclMode mode;
AclResult aclresult;
bool is_missing = false;
roleid = GetUserId();
mode = convert_type_priv_string(priv_type_text);
if (!SearchSysCacheExists1(TYPEOID, ObjectIdGetDatum(typeoid)))
PG_RETURN_NULL();
aclresult = object_aclcheck_ext(TypeRelationId, typeoid,
roleid, mode,
&is_missing);
aclresult = object_aclcheck(TypeRelationId, typeoid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}
@ -4408,13 +4504,16 @@ has_type_privilege_id_id(PG_FUNCTION_ARGS)
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
AclResult aclresult;
bool is_missing = false;
mode = convert_type_priv_string(priv_type_text);
if (!SearchSysCacheExists1(TYPEOID, ObjectIdGetDatum(typeoid)))
PG_RETURN_NULL();
aclresult = object_aclcheck_ext(TypeRelationId, typeoid,
roleid, mode,
&is_missing);
aclresult = object_aclcheck(TypeRelationId, typeoid, roleid, mode);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
}

View File

@ -239,8 +239,12 @@ extern void RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid);
extern AclMode pg_class_aclmask(Oid table_oid, Oid roleid,
AclMode mask, AclMaskHow how);
/* generic function */
extern AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode);
/* generic functions */
extern AclResult object_aclcheck(Oid classid, Oid objectid,
Oid roleid, AclMode mode);
extern AclResult object_aclcheck_ext(Oid classid, Oid objectid,
Oid roleid, AclMode mode,
bool *is_missing);
/* special cases */
extern AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum,
@ -250,6 +254,9 @@ extern AclResult pg_attribute_aclcheck_ext(Oid table_oid, AttrNumber attnum,
bool *is_missing);
extern AclResult pg_attribute_aclcheck_all(Oid table_oid, Oid roleid,
AclMode mode, AclMaskHow how);
extern AclResult pg_attribute_aclcheck_all_ext(Oid table_oid, Oid roleid,
AclMode mode, AclMaskHow how,
bool *is_missing);
extern AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode);
extern AclResult pg_class_aclcheck_ext(Oid table_oid, Oid roleid,
AclMode mode, bool *is_missing);