36
36
#include "common/hashfn.h"
37
37
#include "foreign/foreign.h"
38
38
#include "funcapi.h"
39
+ #include "lib/bloomfilter.h"
39
40
#include "lib/qunique.h"
40
41
#include "miscadmin.h"
41
42
#include "utils/acl.h"
@@ -78,6 +79,13 @@ static Oid cached_role[] = {InvalidOid, InvalidOid, InvalidOid};
78
79
static List * cached_roles [] = {NIL , NIL , NIL };
79
80
static uint32 cached_db_hash ;
80
81
82
+ /*
83
+ * If the list of roles gathered by roles_is_member_of() grows larger than the
84
+ * below threshold, a Bloom filter is created to speed up list membership
85
+ * checks. This threshold is set arbitrarily high to avoid the overhead of
86
+ * creating the Bloom filter until it seems likely to provide a net benefit.
87
+ */
88
+ #define ROLES_LIST_BLOOM_THRESHOLD 1024
81
89
82
90
static const char * getid (const char * s , char * n , Node * escontext );
83
91
static void putid (char * p , const char * s );
@@ -4918,6 +4926,53 @@ RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
4918
4926
cached_role [ROLERECURSE_SETROLE ] = InvalidOid ;
4919
4927
}
4920
4928
4929
+ /*
4930
+ * A helper function for roles_is_member_of() that provides an optimized
4931
+ * implementation of list_append_unique_oid() via a Bloom filter. The caller
4932
+ * (i.e., roles_is_member_of()) is responsible for freeing bf once it is done
4933
+ * using this function.
4934
+ */
4935
+ static inline List *
4936
+ roles_list_append (List * roles_list , bloom_filter * * bf , Oid role )
4937
+ {
4938
+ unsigned char * roleptr = (unsigned char * ) & role ;
4939
+
4940
+ /*
4941
+ * If there is a previously-created Bloom filter, use it to try to
4942
+ * determine whether the role is missing from the list. If it says yes,
4943
+ * that's a hard fact and we can go ahead and add the role. If it says
4944
+ * no, that's only probabilistic and we'd better search the list. Without
4945
+ * a filter, we must always do an ordinary linear search through the
4946
+ * existing list.
4947
+ */
4948
+ if ((* bf && bloom_lacks_element (* bf , roleptr , sizeof (Oid ))) ||
4949
+ !list_member_oid (roles_list , role ))
4950
+ {
4951
+ /*
4952
+ * If the list is large, we take on the overhead of creating and
4953
+ * populating a Bloom filter to speed up future calls to this
4954
+ * function.
4955
+ */
4956
+ if (* bf == NULL &&
4957
+ list_length (roles_list ) > ROLES_LIST_BLOOM_THRESHOLD )
4958
+ {
4959
+ * bf = bloom_create (ROLES_LIST_BLOOM_THRESHOLD * 10 , work_mem , 0 );
4960
+ foreach_oid (roleid , roles_list )
4961
+ bloom_add_element (* bf , (unsigned char * ) & roleid , sizeof (Oid ));
4962
+ }
4963
+
4964
+ /*
4965
+ * Finally, add the role to the list and the Bloom filter, if it
4966
+ * exists.
4967
+ */
4968
+ roles_list = lappend_oid (roles_list , role );
4969
+ if (* bf )
4970
+ bloom_add_element (* bf , roleptr , sizeof (Oid ));
4971
+ }
4972
+
4973
+ return roles_list ;
4974
+ }
4975
+
4921
4976
/*
4922
4977
* Get a list of roles that the specified roleid is a member of
4923
4978
*
@@ -4946,6 +5001,7 @@ roles_is_member_of(Oid roleid, enum RoleRecurseType type,
4946
5001
ListCell * l ;
4947
5002
List * new_cached_roles ;
4948
5003
MemoryContext oldctx ;
5004
+ bloom_filter * bf = NULL ;
4949
5005
4950
5006
Assert (OidIsValid (admin_of ) == PointerIsValid (admin_role ));
4951
5007
if (admin_role != NULL )
@@ -5023,16 +5079,22 @@ roles_is_member_of(Oid roleid, enum RoleRecurseType type,
5023
5079
* graph, we must test for having already seen this role. It is
5024
5080
* legal for instance to have both A->B and A->C->B.
5025
5081
*/
5026
- roles_list = list_append_unique_oid (roles_list , otherid );
5082
+ roles_list = roles_list_append (roles_list , & bf , otherid );
5027
5083
}
5028
5084
ReleaseSysCacheList (memlist );
5029
5085
5030
5086
/* implement pg_database_owner implicit membership */
5031
5087
if (memberid == dba && OidIsValid (dba ))
5032
- roles_list = list_append_unique_oid (roles_list ,
5033
- ROLE_PG_DATABASE_OWNER );
5088
+ roles_list = roles_list_append (roles_list , & bf ,
5089
+ ROLE_PG_DATABASE_OWNER );
5034
5090
}
5035
5091
5092
+ /*
5093
+ * Free the Bloom filter created by roles_list_append(), if there is one.
5094
+ */
5095
+ if (bf )
5096
+ bloom_free (bf );
5097
+
5036
5098
/*
5037
5099
* Copy the completed list into TopMemoryContext so it will persist.
5038
5100
*/
0 commit comments