Alvaro and Stephen,

I propose this patch on top of Adam's v5.  Also included is a full patch
> against master.
>

I have attached an updated patch for review
(role-attribute-bitmask-v7.patch).

This patch incorporates the 'v5a' patch proposed by Alvaro, input
validation (Assert) check in 'check_role_attribute' and the documentation
updates requested by Stephen.

Thanks,
Adam


-- 
Adam Brightwell - adam.brightw...@crunchydatasolutions.com
Database Engineer - www.crunchydatasolutions.com
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
new file mode 100644
index 9ceb96b..9470916
*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 1391,1479 ****
       </row>
  
       <row>
!       <entry><structfield>rolsuper</structfield></entry>
!       <entry><type>bool</type></entry>
        <entry>Role has superuser privileges</entry>
       </row>
  
       <row>
!       <entry><structfield>rolinherit</structfield></entry>
!       <entry><type>bool</type></entry>
!       <entry>Role automatically inherits privileges of roles it is a
!        member of</entry>
       </row>
  
       <row>
!       <entry><structfield>rolcreaterole</structfield></entry>
!       <entry><type>bool</type></entry>
        <entry>Role can create more roles</entry>
       </row>
  
       <row>
!       <entry><structfield>rolcreatedb</structfield></entry>
!       <entry><type>bool</type></entry>
        <entry>Role can create databases</entry>
       </row>
  
       <row>
!       <entry><structfield>rolcatupdate</structfield></entry>
!       <entry><type>bool</type></entry>
        <entry>
         Role can update system catalogs directly.  (Even a superuser cannot do
         this unless this column is true)
        </entry>
       </row>
  
       <row>
!       <entry><structfield>rolcanlogin</structfield></entry>
!       <entry><type>bool</type></entry>
        <entry>
         Role can log in. That is, this role can be given as the initial
         session authorization identifier
        </entry>
       </row>
  
       <row>
!       <entry><structfield>rolreplication</structfield></entry>
!       <entry><type>bool</type></entry>
        <entry>
         Role is a replication role. That is, this role can initiate streaming
         replication (see <xref linkend="streaming-replication">) and set/unset
         the system backup mode using <function>pg_start_backup</> and
         <function>pg_stop_backup</>
        </entry>
       </row>
  
       <row>
!       <entry><structfield>rolconnlimit</structfield></entry>
!       <entry><type>int4</type></entry>
!       <entry>
!        For roles that can log in, this sets maximum number of concurrent
!        connections this role can make.  -1 means no limit.
!       </entry>
!      </row>
! 
!      <row>
!       <entry><structfield>rolpassword</structfield></entry>
!       <entry><type>text</type></entry>
        <entry>
!        Password (possibly encrypted); null if none.  If the password
!        is encrypted, this column will begin with the string <literal>md5</>
!        followed by a 32-character hexadecimal MD5 hash.  The MD5 hash
!        will be of the user's password concatenated to their user name.
!        For example, if user <literal>joe</> has password <literal>xyzzy</>,
!        <productname>PostgreSQL</> will store the md5 hash of
!        <literal>xyzzyjoe</>.  A password that does not follow that
!        format is assumed to be unencrypted.
        </entry>
       </row>
  
-      <row>
-       <entry><structfield>rolvaliduntil</structfield></entry>
-       <entry><type>timestamptz</type></entry>
-       <entry>Password expiry time (only used for password authentication);
-        null if no expiration</entry>
-      </row>
      </tbody>
     </tgroup>
    </table>
--- 1391,1524 ----
       </row>
  
       <row>
!       <entry><structfield>rolattr</structfield></entry>
!       <entry><type>bigint</type></entry>
!       <entry>
!        Role attributes; see <xref linkend="catalog-rolattr-bitmap-table"> and
!        <xref linkend="sql-createrole"> for details
!       </entry>
!      </row>
! 
!      <row>
!       <entry><structfield>rolconnlimit</structfield></entry>
!       <entry><type>int4</type></entry>
!       <entry>
!        For roles that can log in, this sets maximum number of concurrent
!        connections this role can make.  -1 means no limit.
!       </entry>
!      </row>
! 
!      <row>
!       <entry><structfield>rolpassword</structfield></entry>
!       <entry><type>text</type></entry>
!       <entry>
!        Password (possibly encrypted); null if none.  If the password
!        is encrypted, this column will begin with the string <literal>md5</>
!        followed by a 32-character hexadecimal MD5 hash.  The MD5 hash
!        will be of the user's password concatenated to their user name.
!        For example, if user <literal>joe</> has password <literal>xyzzy</>,
!        <productname>PostgreSQL</> will store the md5 hash of
!        <literal>xyzzyjoe</>.  A password that does not follow that
!        format is assumed to be unencrypted.
!       </entry>
!      </row>
! 
!      <row>
!       <entry><structfield>rolvaliduntil</structfield></entry>
!       <entry><type>timestamptz</type></entry>
!       <entry>Password expiry time (only used for password authentication);
!        null if no expiration</entry>
!      </row>
!     </tbody>
!    </tgroup>
!   </table>
! 
!   <table id="catalog-rolattr-bitmap-table">
!    <title>Attributes in <structfield>rolattr</></title>
! 
!    <tgroup cols="4">
!     <thead>
!      <row>
!       <entry>Attribute</entry>
!       <entry>CREATE ROLE Option</entry>
!       <entry>Description</entry>
!       <entry>Position</entry>
!      </row>
!     </thead>
! 
!     <tbody>
!      <row>
!       <entry>Superuser</entry>
!       <entry>SUPERUSER</entry>
        <entry>Role has superuser privileges</entry>
+       <entry><literal>0</literal></entry>
       </row>
  
       <row>
!       <entry>Inherit</entry>
!       <entry>INHERIT</entry>
!       <entry>
!        Role automatically inherits privileges of roles it is a member of
!       </entry>
!       <entry><literal>1</literal></entry>
       </row>
  
       <row>
!       <entry>Create Role</entry>
!       <entry>CREATEROLE</entry>
        <entry>Role can create more roles</entry>
+       <entry><literal>2</literal></entry>
       </row>
  
       <row>
!       <entry>Create DB</entry>
!       <entry>CREATEDB</entry>
        <entry>Role can create databases</entry>
+       <entry><literal>3</literal></entry>
       </row>
  
       <row>
!       <entry>Catalog Update</entry>
!       <entry>CATUPDATE</entry>
        <entry>
         Role can update system catalogs directly.  (Even a superuser cannot do
         this unless this column is true)
        </entry>
+       <entry><literal>4</literal></entry>
       </row>
  
       <row>
!       <entry>Can Login</entry>
!       <entry>LOGIN</entry>
        <entry>
         Role can log in. That is, this role can be given as the initial
         session authorization identifier
        </entry>
+       <entry><literal>5</literal></entry>
       </row>
  
       <row>
!       <entry>Replication</entry>
!       <entry>REPLICATION</entry>
        <entry>
         Role is a replication role. That is, this role can initiate streaming
         replication (see <xref linkend="streaming-replication">) and set/unset
         the system backup mode using <function>pg_start_backup</> and
         <function>pg_stop_backup</>
        </entry>
+       <entry><literal>6</literal></entry>
       </row>
  
       <row>
!       <entry>Bypass Row Level Security</entry>
!       <entry>BYPASSRLS</entry>
        <entry>
!        Role can bypass row level security policies when <literal>row_security</>
!        is set <literal>off</>
        </entry>
+       <entry><literal>7</literal></entry>
       </row>
  
      </tbody>
     </tgroup>
    </table>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
new file mode 100644
index 6f30946..0c9b890
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
*************** SELECT has_function_privilege('joeuser',
*** 15139,15144 ****
--- 15139,15260 ----
      are immediately available without doing <command>SET ROLE</>.
     </para>
  
+    <para>
+     <xref linkend="functions-info-role-attribute-table"> lists functions that
+     allow the user to query role attribute information programmatically.
+    </para>
+ 
+    <table id="functions-info-role-attribute-table">
+     <title>Role Attribute Inquiry Functions</title>
+     <tgroup cols="3">
+      <thead>
+       <row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry></row>
+      </thead>
+      <tbody>
+       <row>
+        <entry><literal><function>pg_has_role_attribute(role, attribute)</function></literal></entry>
+        <entry><type>boolean</type></entry>
+        <entry>does role have the permissions allowed by named attribute</entry>
+       </row>
+       <row>
+        <entry><literal><function>pg_check_role_attribute(role, attribute)</function></literal></entry>
+        <entry><type>boolean</type></entry>
+        <entry>does role have the named attribute</entry>
+       </row>
+       <row>
+        <entry><literal><function>pg_check_role_attribute(role_attributes, attribute)</function></literal></entry>
+        <entry><type>boolean</type></entry>
+        <entry>is attribute set in bitmap of role attributes</entry>
+       </row>
+       <row>
+        <entry><literal><function>pg_all_role_attributes(role_attributes)</function></literal></entry>
+        <entry><type>boolean</type></entry>
+        <entry>convert bitmap of role attribute representation to string array</entry>
+       </row>
+      </tbody>
+     </tgroup>
+    </table>
+ 
+    <para>
+     <function>pg_has_role_attribute</function> checks the attribute permissions
+     given to a role.  It will always return <literal>true</literal> for roles
+     with superuser privileges unless the attribute being checked is
+     <literal>CATUPDATE</literal> (superuser cannot bypass
+     <literal>CATUPDATE</literal> permissions). The role can be specified by name
+     and by OID. The attribute is specified by a text string which must evaluate
+     to one of the following role attributes:
+     <literal>SUPERUSER</literal>,
+     <literal>INHERIT</literal>,
+     <literal>CREATEROLE</literal>,
+     <literal>CREATEDB</literal>,
+     <literal>CATUPDATE</literal>,
+     <literal>CANLOGIN</literal>,
+     <literal>REPLICATION</literal>, or
+     <literal>BYPASSRLS</literal>. See <xref linkend="sql-createrole"> for more
+     information. For example:
+ <programlisting>
+ SELECT pg_has_role_attribute('joe', 'SUPERUSER');
+  pg_has_role_attribute 
+ -----------------------
+  f
+ (1 row)
+ 
+ SELECT rolname, pg_has_role_attribute(oid, 'INHERIT') AS rolinherit FROM pg_roles;
+  rolname  | rolinherit 
+ ----------+------------
+  postgres | t
+  joe      | t
+ (2 rows)
+ </programlisting>
+    </para>
+ 
+    <para>
+     <function>pg_check_role_attribute</function> check the attribute value given
+     to a role.  The role can be specified by name and by OID.  The attribute is
+     specified by a text string which must evaluate to a valid role attribute (see
+     <function>pg_has_role_attribute</function>).  A third variant of this function
+     allows for a bitmap representation (<literal>bigint</literal>) of attributes
+     to be given instead of a role. For example:
+ <programlisting>
+ SELECT pg_check_role_attribute('joe', 'SUPERUSER');
+  pg_check_role_attribute 
+ -------------------------
+  f
+ (1 row)
+ 
+ SELECT rolname, pg_check_role_attribute(oid, 'INHERIT') as rolinherit FROM pg_roles;
+  rolname  | rolinherit 
+ ----------+------------
+  postgres | t
+  joe      | t
+ (2 rows)
+  t
+ (1 row)
+ 
+ 
+ SELECT rolname, pg_check_role_attribute(rolattr, 'SUPERUSER') AS rolsuper FROM pg_authid;
+  rolname  | rolsuper 
+ ----------+----------
+  postgres | t
+  joe      | f
+ (2 rows)
+ </programlisting>
+    </para>
+ 
+    <para>
+     <function>pg_all_role_attributes</function> convert a set of role attributes
+     represented by an <literal>bigint</literal> bitmap to a string array.
+     Example:
+ <programlisting>
+ SELECT rolname, pg_all_role_attributes(rolattr) AS attributes FROM pg_authid;
+  rolname  |                                          attributes                                           
+ ----------+-----------------------------------------------------------------------------------------------
+  postgres | {Superuser,Inherit,"Create Role","Create DB","Catalog Update",Login,Replication,"Bypass RLS"}
+  joe      | {Inherit,Login}
+ (2 rows)
+ </programlisting>
+    </para>
+ 
    <para>
     <xref linkend="functions-info-schema-table"> shows functions that
     determine whether a certain object is <firstterm>visible</> in the
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
new file mode 100644
index 133143d..3181a79
*** a/src/backend/access/transam/xlogfuncs.c
--- b/src/backend/access/transam/xlogfuncs.c
***************
*** 27,32 ****
--- 27,33 ----
  #include "miscadmin.h"
  #include "replication/walreceiver.h"
  #include "storage/smgr.h"
+ #include "utils/acl.h"
  #include "utils/builtins.h"
  #include "utils/numeric.h"
  #include "utils/guc.h"
*************** pg_start_backup(PG_FUNCTION_ARGS)
*** 54,60 ****
  
  	backupidstr = text_to_cstring(backupid);
  
! 	if (!superuser() && !has_rolreplication(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  		   errmsg("must be superuser or replication role to run a backup")));
--- 55,61 ----
  
  	backupidstr = text_to_cstring(backupid);
  
! 	if (!have_role_attribute(ROLE_ATTR_REPLICATION))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  		   errmsg("must be superuser or replication role to run a backup")));
*************** pg_stop_backup(PG_FUNCTION_ARGS)
*** 82,88 ****
  {
  	XLogRecPtr	stoppoint;
  
! 	if (!superuser() && !has_rolreplication(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  		 (errmsg("must be superuser or replication role to run a backup"))));
--- 83,89 ----
  {
  	XLogRecPtr	stoppoint;
  
! 	if (!have_role_attribute(ROLE_ATTR_REPLICATION))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  		 (errmsg("must be superuser or replication role to run a backup"))));
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
new file mode 100644
index a403c64..a6de2ff
*** a/src/backend/catalog/Makefile
--- b/src/backend/catalog/Makefile
*************** all: $(BKIFILES) schemapg.h
*** 28,34 ****
  # indexing.h had better be last, and toasting.h just before it.
  
  POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
! 	pg_proc.h pg_type.h pg_attribute.h pg_class.h \
  	pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h \
  	pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \
  	pg_language.h pg_largeobject_metadata.h pg_largeobject.h pg_aggregate.h \
--- 28,34 ----
  # indexing.h had better be last, and toasting.h just before it.
  
  POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
! 	acldefs.h pg_proc.h pg_type.h pg_attribute.h pg_class.h \
  	pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h \
  	pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \
  	pg_language.h pg_largeobject_metadata.h pg_largeobject.h pg_aggregate.h \
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
new file mode 100644
index d30612c..cd60739
*** a/src/backend/catalog/aclchk.c
--- b/src/backend/catalog/aclchk.c
*************** aclcheck_error_type(AclResult aclerr, Oi
*** 3423,3448 ****
  }
  
  
- /* Check if given user has rolcatupdate privilege according to pg_authid */
- static bool
- has_rolcatupdate(Oid roleid)
- {
- 	bool		rolcatupdate;
- 	HeapTuple	tuple;
- 
- 	tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
- 	if (!HeapTupleIsValid(tuple))
- 		ereport(ERROR,
- 				(errcode(ERRCODE_UNDEFINED_OBJECT),
- 				 errmsg("role with OID %u does not exist", roleid)));
- 
- 	rolcatupdate = ((Form_pg_authid) GETSTRUCT(tuple))->rolcatupdate;
- 
- 	ReleaseSysCache(tuple);
- 
- 	return rolcatupdate;
- }
- 
  /*
   * Relay for the various pg_*_mask routines depending on object kind
   */
--- 3423,3428 ----
*************** pg_class_aclmask(Oid table_oid, Oid role
*** 3630,3636 ****
  	if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) &&
  		IsSystemClass(table_oid, classForm) &&
  		classForm->relkind != RELKIND_VIEW &&
! 		!has_rolcatupdate(roleid) &&
  		!allowSystemTableMods)
  	{
  #ifdef ACLDEBUG
--- 3610,3616 ----
  	if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) &&
  		IsSystemClass(table_oid, classForm) &&
  		classForm->relkind != RELKIND_VIEW &&
! 		!has_role_attribute(roleid, ROLE_ATTR_CATUPDATE) &&
  		!allowSystemTableMods)
  	{
  #ifdef ACLDEBUG
*************** pg_extension_ownercheck(Oid ext_oid, Oid
*** 5051,5102 ****
  }
  
  /*
!  * Check whether specified role has CREATEROLE privilege (or is a superuser)
   *
!  * Note: roles do not have owners per se; instead we use this test in
!  * places where an ownership-like permissions test is needed for a role.
!  * Be sure to apply it to the role trying to do the operation, not the
!  * role being operated on!	Also note that this generally should not be
!  * considered enough privilege if the target role is a superuser.
!  * (We don't handle that consideration here because we want to give a
!  * separate error message for such cases, so the caller has to deal with it.)
   */
  bool
! has_createrole_privilege(Oid roleid)
  {
! 	bool		result = false;
! 	HeapTuple	utup;
! 
! 	/* Superusers bypass all permission checking. */
! 	if (superuser_arg(roleid))
  		return true;
  
! 	utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
! 	if (HeapTupleIsValid(utup))
! 	{
! 		result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreaterole;
! 		ReleaseSysCache(utup);
! 	}
! 	return result;
  }
  
  bool
! has_bypassrls_privilege(Oid roleid)
  {
! 	bool		result = false;
! 	HeapTuple	utup;
  
! 	/* Superusers bypass all permission checking. */
! 	if (superuser_arg(roleid))
! 		return true;
  
! 	utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
! 	if (HeapTupleIsValid(utup))
! 	{
! 		result = ((Form_pg_authid) GETSTRUCT(utup))->rolbypassrls;
! 		ReleaseSysCache(utup);
! 	}
! 	return result;
  }
  
  /*
--- 5031,5117 ----
  }
  
  /*
!  * has_role_attribute
!  *   Check if the role with the specified id has been assigned a specific role
!  *   attribute.
   *
!  * roleid - the oid of the role to check.
!  * attribute - the attribute to check.
!  *
!  * Note: Use this function for role attribute permission checking as it
!  * accounts for superuser status.  It will always return true for roles with
!  * superuser privileges unless the attribute being checked is CATUPDATE
!  * (superusers are not allowed to bypass CATUPDATE permissions).
!  *
!  * Note: roles do not have owners per se; instead we use this test in places
!  * where an ownership-like permissions test is needed for a role.  Be sure to
!  * apply it to the role trying to do the operation, not the role being operated
!  * on!  Also note that this generally should not be considered enough privilege
!  * if the target role is a superuser.  (We don't handle that consideration here
!  * because we want to give a separate error message for such cases, so the
!  * caller has to deal with it.)
   */
  bool
! has_role_attribute(Oid roleid, RoleAttr attribute)
  {
! 	/*
! 	 * Superusers bypass all permission checking except in the case of CATUPDATE
! 	 */
! 	if (!(attribute & ROLE_ATTR_CATUPDATE) && superuser_arg(roleid))
  		return true;
  
! 	return check_role_attribute(roleid, attribute);
  }
  
+ /*
+  * have_role_attribute
+  *   Convenience function for checking if the role id returned by GetUserId()
+  *   has been assigned a specific role attribute.
+  *
+  * attribute - the attribute to check.
+  */
  bool
! have_role_attribute(RoleAttr attribute)
  {
! 	return has_role_attribute(GetUserId(), attribute);
! }
  
! /*
!  * check_role_attribute
!  *   Check if the role with the specified id has been assigned a specific role
!  *   attribute.
!  *
!  * roleid - the oid of the role to check.
!  * attribute - the attribute to check.
!  *
!  * Note: This function should only be used for checking the value of an
!  * individual attribute in the rolattr bitmap and should *not* be used for
!  * permission checking. For the purposes of permission checking use
!  * 'has_role_attribute' instead.
!  */
! bool
! check_role_attribute(Oid roleid, RoleAttr attribute)
! {
! 	RoleAttr	attributes;
! 	HeapTuple	tuple;
  
! 	/* ROLE_ATTR_NONE (zero) is not a valid attribute */
! 	Assert(attribute != ROLE_ATTR_NONE);
! 
! 	/* Check that only one bit is set in 'attribute' */
! 	Assert(!(attribute & (attribute - 1)));
! 
! 	tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
! 
! 	if (!HeapTupleIsValid(tuple))
! 		ereport(ERROR,
! 				(errcode(ERRCODE_UNDEFINED_OBJECT),
! 				 errmsg("role with OID %u does not exist", roleid)));
! 
! 	attributes = ((Form_pg_authid) GETSTRUCT(tuple))->rolattr;
! 	ReleaseSysCache(tuple);
! 
! 	return (attributes & attribute);
  }
  
  /*
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
new file mode 100644
index ca89879..415ac17
*** a/src/backend/catalog/genbki.pl
--- b/src/backend/catalog/genbki.pl
*************** my $BOOTSTRAP_SUPERUSERID =
*** 90,95 ****
--- 90,97 ----
    find_defined_symbol('pg_authid.h', 'BOOTSTRAP_SUPERUSERID');
  my $PG_CATALOG_NAMESPACE =
    find_defined_symbol('pg_namespace.h', 'PG_CATALOG_NAMESPACE');
+ my $ROLE_ATTR_ALL =
+   find_defined_symbol('acldefs.h', 'ROLE_ATTR_ALL');
  
  # Read all the input header files into internal data structures
  my $catalogs = Catalog::Catalogs(@input_files);
*************** foreach my $catname (@{ $catalogs->{name
*** 144,149 ****
--- 146,152 ----
  			# substitute constant values we acquired above
  			$row->{bki_values} =~ s/\bPGUID\b/$BOOTSTRAP_SUPERUSERID/g;
  			$row->{bki_values} =~ s/\bPGNSP\b/$PG_CATALOG_NAMESPACE/g;
+ 			$row->{bki_values} =~ s/\bPGROLATTRALL/$ROLE_ATTR_ALL/g;
  
  			# Save pg_type info for pg_attribute processing below
  			if ($catname eq 'pg_type')
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql
new file mode 100644
index a036c62..87b6d8c
*** a/src/backend/catalog/information_schema.sql
--- b/src/backend/catalog/information_schema.sql
*************** CREATE VIEW user_mapping_options AS
*** 2884,2890 ****
             CAST((pg_options_to_table(um.umoptions)).option_name AS sql_identifier) AS option_name,
             CAST(CASE WHEN (umuser <> 0 AND authorization_identifier = current_user)
                         OR (umuser = 0 AND pg_has_role(srvowner, 'USAGE'))
!                        OR (SELECT rolsuper FROM pg_authid WHERE rolname = current_user) THEN (pg_options_to_table(um.umoptions)).option_value
                       ELSE NULL END AS character_data) AS option_value
      FROM _pg_user_mappings um;
  
--- 2884,2895 ----
             CAST((pg_options_to_table(um.umoptions)).option_name AS sql_identifier) AS option_name,
             CAST(CASE WHEN (umuser <> 0 AND authorization_identifier = current_user)
                         OR (umuser = 0 AND pg_has_role(srvowner, 'USAGE'))
!                        OR (
!                             SELECT pg_check_role_attribute(pg_authid.rolattr, 'SUPERUSER') AS rolsuper
!                             FROM pg_authid
!                             WHERE rolname = current_user
!                           )
!                        THEN (pg_options_to_table(um.umoptions)).option_value
                       ELSE NULL END AS character_data) AS option_value
      FROM _pg_user_mappings um;
  
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
new file mode 100644
index e261307..6df7e4d
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
*************** check_object_ownership(Oid roleid, Objec
*** 1310,1316 ****
  			}
  			else
  			{
! 				if (!has_createrole_privilege(roleid))
  					ereport(ERROR,
  							(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  							 errmsg("must have CREATEROLE privilege")));
--- 1310,1316 ----
  			}
  			else
  			{
! 				if (!has_role_attribute(roleid, ROLE_ATTR_CREATEROLE))
  					ereport(ERROR,
  							(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  							 errmsg("must have CREATEROLE privilege")));
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
new file mode 100644
index 22b8cee..ae93832
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
***************
*** 9,25 ****
  CREATE VIEW pg_roles AS
      SELECT
          rolname,
!         rolsuper,
!         rolinherit,
!         rolcreaterole,
!         rolcreatedb,
!         rolcatupdate,
!         rolcanlogin,
!         rolreplication,
          rolconnlimit,
          '********'::text as rolpassword,
          rolvaliduntil,
-         rolbypassrls,
          setconfig as rolconfig,
          pg_authid.oid
      FROM pg_authid LEFT JOIN pg_db_role_setting s
--- 9,25 ----
  CREATE VIEW pg_roles AS
      SELECT
          rolname,
!         pg_check_role_attribute(pg_authid.rolattr, 'SUPERUSER') AS rolsuper,
!         pg_check_role_attribute(pg_authid.rolattr, 'INHERIT') AS rolinherit,
!         pg_check_role_attribute(pg_authid.rolattr, 'CREATEROLE') AS rolcreaterole,
!         pg_check_role_attribute(pg_authid.rolattr, 'CREATEDB') AS rolcreatedb,
!         pg_check_role_attribute(pg_authid.rolattr, 'CATUPDATE') AS rolcatupdate,
!         pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN') AS rolcanlogin,
!         pg_check_role_attribute(pg_authid.rolattr, 'REPLICATION') AS rolreplication,
!         pg_check_role_attribute(pg_authid.rolattr, 'BYPASSRLS') AS rolbypassrls,
          rolconnlimit,
          '********'::text as rolpassword,
          rolvaliduntil,
          setconfig as rolconfig,
          pg_authid.oid
      FROM pg_authid LEFT JOIN pg_db_role_setting s
*************** CREATE VIEW pg_shadow AS
*** 29,44 ****
      SELECT
          rolname AS usename,
          pg_authid.oid AS usesysid,
!         rolcreatedb AS usecreatedb,
!         rolsuper AS usesuper,
!         rolcatupdate AS usecatupd,
!         rolreplication AS userepl,
          rolpassword AS passwd,
          rolvaliduntil::abstime AS valuntil,
          setconfig AS useconfig
      FROM pg_authid LEFT JOIN pg_db_role_setting s
      ON (pg_authid.oid = setrole AND setdatabase = 0)
!     WHERE rolcanlogin;
  
  REVOKE ALL on pg_shadow FROM public;
  
--- 29,44 ----
      SELECT
          rolname AS usename,
          pg_authid.oid AS usesysid,
!         pg_check_role_attribute(pg_authid.rolattr, 'CREATEDB') AS usecreatedb,
!         pg_check_role_attribute(pg_authid.rolattr, 'SUPERUSER') AS usesuper,
!         pg_check_role_attribute(pg_authid.rolattr, 'CATUPDATE') AS usecatupd,
!         pg_check_role_attribute(pg_authid.rolattr, 'REPLICATION') AS userepl,
          rolpassword AS passwd,
          rolvaliduntil::abstime AS valuntil,
          setconfig AS useconfig
      FROM pg_authid LEFT JOIN pg_db_role_setting s
      ON (pg_authid.oid = setrole AND setdatabase = 0)
!     WHERE pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN');
  
  REVOKE ALL on pg_shadow FROM public;
  
*************** CREATE VIEW pg_group AS
*** 48,54 ****
          oid AS grosysid,
          ARRAY(SELECT member FROM pg_auth_members WHERE roleid = oid) AS grolist
      FROM pg_authid
!     WHERE NOT rolcanlogin;
  
  CREATE VIEW pg_user AS
      SELECT
--- 48,54 ----
          oid AS grosysid,
          ARRAY(SELECT member FROM pg_auth_members WHERE roleid = oid) AS grolist
      FROM pg_authid
!     WHERE NOT pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN');
  
  CREATE VIEW pg_user AS
      SELECT
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
new file mode 100644
index 1a5244c..c079168
*** a/src/backend/commands/dbcommands.c
--- b/src/backend/commands/dbcommands.c
*************** static bool get_db_info(const char *name
*** 85,91 ****
  			Oid *dbLastSysOidP, TransactionId *dbFrozenXidP,
  			MultiXactId *dbMinMultiP,
  			Oid *dbTablespace, char **dbCollate, char **dbCtype);
- static bool have_createdb_privilege(void);
  static void remove_dbtablespaces(Oid db_id);
  static bool check_db_file_conflict(Oid db_id);
  static int	errdetail_busy_db(int notherbackends, int npreparedxacts);
--- 85,90 ----
*************** createdb(const CreatedbStmt *stmt)
*** 291,297 ****
  	 * "giveaway" attacks.  Note that a superuser will always have both of
  	 * these privileges a fortiori.
  	 */
! 	if (!have_createdb_privilege())
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  				 errmsg("permission denied to create database")));
--- 290,296 ----
  	 * "giveaway" attacks.  Note that a superuser will always have both of
  	 * these privileges a fortiori.
  	 */
! 	if (!have_role_attribute(ROLE_ATTR_CREATEDB))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  				 errmsg("permission denied to create database")));
*************** RenameDatabase(const char *oldname, cons
*** 965,971 ****
  					   oldname);
  
  	/* must have createdb rights */
! 	if (!have_createdb_privilege())
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  				 errmsg("permission denied to rename database")));
--- 964,970 ----
  					   oldname);
  
  	/* must have createdb rights */
! 	if (!have_role_attribute(ROLE_ATTR_CREATEDB))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  				 errmsg("permission denied to rename database")));
*************** AlterDatabaseOwner(const char *dbname, O
*** 1623,1629 ****
  		 * databases.  Because superusers will always have this right, we need
  		 * no special case for them.
  		 */
! 		if (!have_createdb_privilege())
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  				   errmsg("permission denied to change owner of database")));
--- 1622,1628 ----
  		 * databases.  Because superusers will always have this right, we need
  		 * no special case for them.
  		 */
! 		if (!have_role_attribute(ROLE_ATTR_CREATEDB))
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  				   errmsg("permission denied to change owner of database")));
*************** get_db_info(const char *name, LOCKMODE l
*** 1802,1827 ****
  	return result;
  }
  
- /* Check if current user has createdb privileges */
- static bool
- have_createdb_privilege(void)
- {
- 	bool		result = false;
- 	HeapTuple	utup;
- 
- 	/* Superusers can always do everything */
- 	if (superuser())
- 		return true;
- 
- 	utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(GetUserId()));
- 	if (HeapTupleIsValid(utup))
- 	{
- 		result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreatedb;
- 		ReleaseSysCache(utup);
- 	}
- 	return result;
- }
- 
  /*
   * Remove tablespace directories
   *
--- 1801,1806 ----
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
new file mode 100644
index 1a73fd8..564f77a
*** a/src/backend/commands/user.c
--- b/src/backend/commands/user.c
*************** static void DelRoleMems(const char *role
*** 56,69 ****
  			bool admin_opt);
  
  
- /* Check if current user has createrole privileges */
- static bool
- have_createrole_privilege(void)
- {
- 	return has_createrole_privilege(GetUserId());
- }
- 
- 
  /*
   * CREATE ROLE
   */
--- 56,61 ----
*************** CreateRole(CreateRoleStmt *stmt)
*** 81,93 ****
  	char	   *password = NULL;	/* user password */
  	bool		encrypt_password = Password_encryption; /* encrypt password? */
  	char		encrypted_password[MD5_PASSWD_LEN + 1];
! 	bool		issuper = false;	/* Make the user a superuser? */
! 	bool		inherit = true; /* Auto inherit privileges? */
! 	bool		createrole = false;		/* Can this user create roles? */
! 	bool		createdb = false;		/* Can the user create databases? */
! 	bool		canlogin = false;		/* Can this user login? */
! 	bool		isreplication = false;	/* Is this a replication role? */
! 	bool		bypassrls = false;		/* Is this a row security enabled role? */
  	int			connlimit = -1; /* maximum connections allowed */
  	List	   *addroleto = NIL;	/* roles to make this a member of */
  	List	   *rolemembers = NIL;		/* roles to be members of this role */
--- 73,79 ----
  	char	   *password = NULL;	/* user password */
  	bool		encrypt_password = Password_encryption; /* encrypt password? */
  	char		encrypted_password[MD5_PASSWD_LEN + 1];
! 	RoleAttr	attributes;
  	int			connlimit = -1; /* maximum connections allowed */
  	List	   *addroleto = NIL;	/* roles to make this a member of */
  	List	   *rolemembers = NIL;		/* roles to be members of this role */
*************** CreateRole(CreateRoleStmt *stmt)
*** 109,121 ****
  	DefElem    *dvalidUntil = NULL;
  	DefElem    *dbypassRLS = NULL;
  
! 	/* The defaults can vary depending on the original statement type */
  	switch (stmt->stmt_type)
  	{
  		case ROLESTMT_ROLE:
  			break;
  		case ROLESTMT_USER:
! 			canlogin = true;
  			/* may eventually want inherit to default to false here */
  			break;
  		case ROLESTMT_GROUP:
--- 95,111 ----
  	DefElem    *dvalidUntil = NULL;
  	DefElem    *dbypassRLS = NULL;
  
! 	/*
! 	 * Every role has INHERIT by default, and CANLOGIN depends on the statement
! 	 * type.
! 	 */
! 	attributes = ROLE_ATTR_INHERIT;
  	switch (stmt->stmt_type)
  	{
  		case ROLESTMT_ROLE:
  			break;
  		case ROLESTMT_USER:
! 			attributes |= ROLE_ATTR_CANLOGIN;
  			/* may eventually want inherit to default to false here */
  			break;
  		case ROLESTMT_GROUP:
*************** CreateRole(CreateRoleStmt *stmt)
*** 249,266 ****
  
  	if (dpassword && dpassword->arg)
  		password = strVal(dpassword->arg);
  	if (dissuper)
! 		issuper = intVal(dissuper->arg) != 0;
  	if (dinherit)
! 		inherit = intVal(dinherit->arg) != 0;
  	if (dcreaterole)
! 		createrole = intVal(dcreaterole->arg) != 0;
  	if (dcreatedb)
! 		createdb = intVal(dcreatedb->arg) != 0;
  	if (dcanlogin)
! 		canlogin = intVal(dcanlogin->arg) != 0;
  	if (disreplication)
! 		isreplication = intVal(disreplication->arg) != 0;
  	if (dconnlimit)
  	{
  		connlimit = intVal(dconnlimit->arg);
--- 239,314 ----
  
  	if (dpassword && dpassword->arg)
  		password = strVal(dpassword->arg);
+ 
+ 	/* Set up role attributes and check permissions to set each of them */
  	if (dissuper)
! 	{
! 		if (intVal(dissuper->arg) != 0)
! 		{
! 			if (!superuser())
! 				ereport(ERROR,
! 						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 						 errmsg("must be superuser to create superusers")));
! 			attributes |= ROLE_ATTR_SUPERUSER;
! 		}
! 		else
! 			attributes &= ~ROLE_ATTR_SUPERUSER;
! 	}
  	if (dinherit)
! 	{
! 		if (intVal(dinherit->arg) != 0)
! 			attributes |= ROLE_ATTR_INHERIT;
! 		else
! 			attributes &= ~ROLE_ATTR_INHERIT;
! 	}
  	if (dcreaterole)
! 	{
! 		if (intVal(dcreaterole->arg) != 0)
! 			attributes |= ROLE_ATTR_CREATEROLE;
! 		else
! 			attributes &= ~ROLE_ATTR_CREATEROLE;
! 	}
  	if (dcreatedb)
! 	{
! 		if (intVal(dcreatedb->arg) != 0)
! 			attributes |= ROLE_ATTR_CREATEDB;
! 		else
! 			attributes &= ~ROLE_ATTR_CREATEDB;
! 	}
  	if (dcanlogin)
! 	{
! 		if (intVal(dcanlogin->arg) != 0)
! 			attributes |= ROLE_ATTR_CANLOGIN;
! 		else
! 			attributes &= ~ROLE_ATTR_CANLOGIN;
! 	}
  	if (disreplication)
! 	{
! 		if (intVal(disreplication->arg) != 0)
! 		{
! 			if (!superuser())
! 				ereport(ERROR,
! 						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 						 errmsg("must be superuser to create replication users")));
! 			attributes |= ROLE_ATTR_REPLICATION;
! 		}
! 		else
! 			attributes &= ~ROLE_ATTR_REPLICATION;
! 	}
! 	if (dbypassRLS)
! 	{
! 		if (intVal(dbypassRLS->arg) != 0)
! 		{
! 			if (!superuser())
! 				ereport(ERROR,
! 						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 						 errmsg("must be superuser to change bypassrls attribute")));
! 			attributes |= ROLE_ATTR_BYPASSRLS;
! 		}
! 		else
! 			attributes &= ~ROLE_ATTR_BYPASSRLS;
! 	}
! 
  	if (dconnlimit)
  	{
  		connlimit = intVal(dconnlimit->arg);
*************** CreateRole(CreateRoleStmt *stmt)
*** 277,314 ****
  		adminmembers = (List *) dadminmembers->arg;
  	if (dvalidUntil)
  		validUntil = strVal(dvalidUntil->arg);
- 	if (dbypassRLS)
- 		bypassrls = intVal(dbypassRLS->arg) != 0;
  
! 	/* Check some permissions first */
! 	if (issuper)
! 	{
! 		if (!superuser())
! 			ereport(ERROR,
! 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 					 errmsg("must be superuser to create superusers")));
! 	}
! 	else if (isreplication)
! 	{
! 		if (!superuser())
! 			ereport(ERROR,
! 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				   errmsg("must be superuser to create replication users")));
! 	}
! 	else if (bypassrls)
! 	{
! 		if (!superuser())
! 			ereport(ERROR,
! 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 					 errmsg("must be superuser to change bypassrls attribute.")));
! 	}
! 	else
! 	{
! 		if (!have_createrole_privilege())
! 			ereport(ERROR,
! 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 					 errmsg("permission denied to create role")));
! 	}
  
  	if (strcmp(stmt->role, "public") == 0 ||
  		strcmp(stmt->role, "none") == 0)
--- 325,336 ----
  		adminmembers = (List *) dadminmembers->arg;
  	if (dvalidUntil)
  		validUntil = strVal(dvalidUntil->arg);
  
! 	/* Check permissions */
! 	if (!have_role_attribute(ROLE_ATTR_CREATEROLE))
! 		ereport(ERROR,
! 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 errmsg("permission denied to create role")));
  
  	if (strcmp(stmt->role, "public") == 0 ||
  		strcmp(stmt->role, "none") == 0)
*************** CreateRole(CreateRoleStmt *stmt)
*** 364,377 ****
  	new_record[Anum_pg_authid_rolname - 1] =
  		DirectFunctionCall1(namein, CStringGetDatum(stmt->role));
  
! 	new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper);
! 	new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit);
! 	new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole);
! 	new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb);
! 	/* superuser gets catupdate right by default */
! 	new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper);
! 	new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin);
! 	new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication);
  	new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
  
  	if (password)
--- 386,393 ----
  	new_record[Anum_pg_authid_rolname - 1] =
  		DirectFunctionCall1(namein, CStringGetDatum(stmt->role));
  
! 	new_record[Anum_pg_authid_rolattr - 1] = Int64GetDatum(attributes);
! 
  	new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
  
  	if (password)
*************** CreateRole(CreateRoleStmt *stmt)
*** 394,401 ****
  	new_record[Anum_pg_authid_rolvaliduntil - 1] = validUntil_datum;
  	new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
  
- 	new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls);
- 
  	tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls);
  
  	/*
--- 410,415 ----
*************** AlterRole(AlterRoleStmt *stmt)
*** 508,513 ****
--- 522,528 ----
  	DefElem    *dvalidUntil = NULL;
  	DefElem    *dbypassRLS = NULL;
  	Oid			roleid;
+ 	RoleAttr	attributes;
  
  	/* Extract options from the statement node tree */
  	foreach(option, stmt->options)
*************** AlterRole(AlterRoleStmt *stmt)
*** 658,688 ****
  	roleid = HeapTupleGetOid(tuple);
  
  	/*
! 	 * To mess with a superuser you gotta be superuser; else you need
! 	 * createrole, or just want to change your own password
  	 */
! 	if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper || issuper >= 0)
  	{
  		if (!superuser())
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser to alter superusers")));
  	}
! 	else if (((Form_pg_authid) GETSTRUCT(tuple))->rolreplication || isreplication >= 0)
  	{
  		if (!superuser())
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser to alter replication users")));
  	}
! 	else if (((Form_pg_authid) GETSTRUCT(tuple))->rolbypassrls || bypassrls >= 0)
  	{
  		if (!superuser())
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser to change bypassrls attribute")));
  	}
! 	else if (!have_createrole_privilege())
  	{
  		if (!(inherit < 0 &&
  			  createrole < 0 &&
--- 673,706 ----
  	roleid = HeapTupleGetOid(tuple);
  
  	/*
! 	 * To mess with a superuser or a replication user you gotta be superuser;
! 	 * else you need createrole, or just want to change your own password
  	 */
! 
! 	attributes = ((Form_pg_authid) GETSTRUCT(tuple))->rolattr;
! 
! 	if ((attributes & ROLE_ATTR_SUPERUSER) || issuper >= 0)
  	{
  		if (!superuser())
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser to alter superusers")));
  	}
! 	else if ((attributes & ROLE_ATTR_REPLICATION) || isreplication >= 0)
  	{
  		if (!superuser())
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser to alter replication users")));
  	}
! 	else if ((attributes & ROLE_ATTR_BYPASSRLS) || bypassrls >= 0)
  	{
  		if (!superuser())
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser to change bypassrls attribute")));
  	}
! 	else if (!have_role_attribute(ROLE_ATTR_CREATEROLE))
  	{
  		if (!(inherit < 0 &&
  			  createrole < 0 &&
*************** AlterRole(AlterRoleStmt *stmt)
*** 743,785 ****
  	 */
  	if (issuper >= 0)
  	{
! 		new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper > 0);
! 		new_record_repl[Anum_pg_authid_rolsuper - 1] = true;
! 
! 		new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper > 0);
! 		new_record_repl[Anum_pg_authid_rolcatupdate - 1] = true;
  	}
  
  	if (inherit >= 0)
  	{
! 		new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit > 0);
! 		new_record_repl[Anum_pg_authid_rolinherit - 1] = true;
  	}
  
  	if (createrole >= 0)
  	{
! 		new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole > 0);
! 		new_record_repl[Anum_pg_authid_rolcreaterole - 1] = true;
  	}
  
  	if (createdb >= 0)
  	{
! 		new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb > 0);
! 		new_record_repl[Anum_pg_authid_rolcreatedb - 1] = true;
  	}
  
  	if (canlogin >= 0)
  	{
! 		new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin > 0);
! 		new_record_repl[Anum_pg_authid_rolcanlogin - 1] = true;
  	}
  
  	if (isreplication >= 0)
  	{
! 		new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication > 0);
! 		new_record_repl[Anum_pg_authid_rolreplication - 1] = true;
  	}
  
  	if (dconnlimit)
  	{
  		new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
--- 761,831 ----
  	 */
  	if (issuper >= 0)
  	{
! 		if (issuper > 0)
! 			attributes |= ROLE_ATTR_SUPERUSER | ROLE_ATTR_CATUPDATE;
! 		else
! 			attributes &= ~(ROLE_ATTR_SUPERUSER | ROLE_ATTR_CATUPDATE);
! 		new_record_repl[Anum_pg_authid_rolattr - 1] = true;
  	}
  
  	if (inherit >= 0)
  	{
! 		if (inherit > 0)
! 			attributes |= ROLE_ATTR_INHERIT;
! 		else
! 			attributes &= ~ROLE_ATTR_INHERIT;
! 		new_record_repl[Anum_pg_authid_rolattr - 1] = true;
  	}
  
  	if (createrole >= 0)
  	{
! 		if (createrole > 0)
! 			attributes |= ROLE_ATTR_CREATEROLE;
! 		else
! 			attributes &= ~ROLE_ATTR_CREATEROLE;
! 		new_record_repl[Anum_pg_authid_rolattr - 1] = true;
  	}
  
  	if (createdb >= 0)
  	{
! 		if (createdb > 0)
! 			attributes |= ROLE_ATTR_CREATEDB;
! 		else
! 			attributes &= ~ROLE_ATTR_CREATEDB;
! 		new_record_repl[Anum_pg_authid_rolattr - 1] = true;
  	}
  
  	if (canlogin >= 0)
  	{
! 		if (canlogin > 0)
! 			attributes |= ROLE_ATTR_CANLOGIN;
! 		else
! 			attributes &= ~ROLE_ATTR_CANLOGIN;
! 		new_record_repl[Anum_pg_authid_rolattr - 1] = true;
  	}
  
  	if (isreplication >= 0)
  	{
! 		if (isreplication > 0)
! 			attributes |= ROLE_ATTR_REPLICATION;
! 		else
! 			attributes &= ~ROLE_ATTR_REPLICATION;
! 		new_record_repl[Anum_pg_authid_rolattr - 1] = true;
  	}
  
+ 	if (bypassrls >= 0)
+ 	{
+ 		if (bypassrls > 0)
+ 			attributes |= ROLE_ATTR_BYPASSRLS;
+ 		else
+ 			attributes &= ~ROLE_ATTR_BYPASSRLS;
+ 		new_record_repl[Anum_pg_authid_rolattr - 1] = true;
+ 	}
+ 
+ 	/* If any role attributes were set, then update. */
+ 	if (new_record_repl[Anum_pg_authid_rolattr - 1])
+ 		new_record[Anum_pg_authid_rolattr - 1] = Int64GetDatum(attributes);
+ 
  	if (dconnlimit)
  	{
  		new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
*************** AlterRole(AlterRoleStmt *stmt)
*** 815,825 ****
  	new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
  	new_record_repl[Anum_pg_authid_rolvaliduntil - 1] = true;
  
- 	if (bypassrls >= 0)
- 	{
- 		new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls > 0);
- 		new_record_repl[Anum_pg_authid_rolbypassrls - 1] = true;
- 	}
  
  	new_tuple = heap_modify_tuple(tuple, pg_authid_dsc, new_record,
  								  new_record_nulls, new_record_repl);
--- 861,866 ----
*************** AlterRoleSet(AlterRoleSetStmt *stmt)
*** 867,872 ****
--- 908,914 ----
  	HeapTuple	roletuple;
  	Oid			databaseid = InvalidOid;
  	Oid			roleid = InvalidOid;
+ 	RoleAttr	attributes;
  
  	if (stmt->role)
  	{
*************** AlterRoleSet(AlterRoleSetStmt *stmt)
*** 889,895 ****
  		 * To mess with a superuser you gotta be superuser; else you need
  		 * createrole, or just want to change your own settings
  		 */
! 		if (((Form_pg_authid) GETSTRUCT(roletuple))->rolsuper)
  		{
  			if (!superuser())
  				ereport(ERROR,
--- 931,938 ----
  		 * To mess with a superuser you gotta be superuser; else you need
  		 * createrole, or just want to change your own settings
  		 */
! 		attributes = ((Form_pg_authid) GETSTRUCT(roletuple))->rolattr;
! 		if (attributes & ROLE_ATTR_SUPERUSER)
  		{
  			if (!superuser())
  				ereport(ERROR,
*************** AlterRoleSet(AlterRoleSetStmt *stmt)
*** 898,904 ****
  		}
  		else
  		{
! 			if (!have_createrole_privilege() &&
  				HeapTupleGetOid(roletuple) != GetUserId())
  				ereport(ERROR,
  						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
--- 941,947 ----
  		}
  		else
  		{
! 			if (!have_role_attribute(ROLE_ATTR_CREATEROLE) &&
  				HeapTupleGetOid(roletuple) != GetUserId())
  				ereport(ERROR,
  						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
*************** DropRole(DropRoleStmt *stmt)
*** 951,957 ****
  				pg_auth_members_rel;
  	ListCell   *item;
  
! 	if (!have_createrole_privilege())
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  				 errmsg("permission denied to drop role")));
--- 994,1000 ----
  				pg_auth_members_rel;
  	ListCell   *item;
  
! 	if (!have_role_attribute(ROLE_ATTR_CREATEROLE))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  				 errmsg("permission denied to drop role")));
*************** DropRole(DropRoleStmt *stmt)
*** 973,978 ****
--- 1016,1022 ----
  		char	   *detail_log;
  		SysScanDesc sscan;
  		Oid			roleid;
+ 		RoleAttr	attributes;
  
  		tuple = SearchSysCache1(AUTHNAME, PointerGetDatum(role));
  		if (!HeapTupleIsValid(tuple))
*************** DropRole(DropRoleStmt *stmt)
*** 1013,1020 ****
  		 * roles but not superuser roles.  This is mainly to avoid the
  		 * scenario where you accidentally drop the last superuser.
  		 */
! 		if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper &&
! 			!superuser())
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser to drop superusers")));
--- 1057,1064 ----
  		 * roles but not superuser roles.  This is mainly to avoid the
  		 * scenario where you accidentally drop the last superuser.
  		 */
! 		attributes = ((Form_pg_authid) GETSTRUCT(tuple))->rolattr;
! 		if ((attributes & ROLE_ATTR_SUPERUSER) && !superuser())
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser to drop superusers")));
*************** RenameRole(const char *oldname, const ch
*** 1128,1133 ****
--- 1172,1178 ----
  	bool		repl_repl[Natts_pg_authid];
  	int			i;
  	Oid			roleid;
+ 	RoleAttr	attributes;
  
  	rel = heap_open(AuthIdRelationId, RowExclusiveLock);
  	dsc = RelationGetDescr(rel);
*************** RenameRole(const char *oldname, const ch
*** 1173,1179 ****
  	/*
  	 * createrole is enough privilege unless you want to mess with a superuser
  	 */
! 	if (((Form_pg_authid) GETSTRUCT(oldtuple))->rolsuper)
  	{
  		if (!superuser())
  			ereport(ERROR,
--- 1218,1225 ----
  	/*
  	 * createrole is enough privilege unless you want to mess with a superuser
  	 */
! 	attributes = ((Form_pg_authid) GETSTRUCT(oldtuple))->rolattr;
! 	if (attributes & ROLE_ATTR_SUPERUSER)
  	{
  		if (!superuser())
  			ereport(ERROR,
*************** RenameRole(const char *oldname, const ch
*** 1182,1188 ****
  	}
  	else
  	{
! 		if (!have_createrole_privilege())
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("permission denied to rename role")));
--- 1228,1234 ----
  	}
  	else
  	{
! 		if (!have_role_attribute(ROLE_ATTR_CREATEROLE))
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("permission denied to rename role")));
*************** AddRoleMems(const char *rolename, Oid ro
*** 1409,1415 ****
  	}
  	else
  	{
! 		if (!have_createrole_privilege() &&
  			!is_admin_of_role(grantorId, roleid))
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
--- 1455,1461 ----
  	}
  	else
  	{
! 		if (!have_role_attribute(ROLE_ATTR_CREATEROLE) &&
  			!is_admin_of_role(grantorId, roleid))
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
*************** DelRoleMems(const char *rolename, Oid ro
*** 1555,1561 ****
  	}
  	else
  	{
! 		if (!have_createrole_privilege() &&
  			!is_admin_of_role(GetUserId(), roleid))
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
--- 1601,1607 ----
  	}
  	else
  	{
! 		if (!have_role_attribute(ROLE_ATTR_CREATEROLE) &&
  			!is_admin_of_role(GetUserId(), roleid))
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
new file mode 100644
index 6ce8dae..491dc38
*** a/src/backend/commands/variable.c
--- b/src/backend/commands/variable.c
*************** check_session_authorization(char **newva
*** 776,781 ****
--- 776,782 ----
  	Oid			roleid;
  	bool		is_superuser;
  	role_auth_extra *myextra;
+ 	RoleAttr	attributes;
  
  	/* Do nothing for the boot_val default of NULL */
  	if (*newval == NULL)
*************** check_session_authorization(char **newva
*** 800,806 ****
  	}
  
  	roleid = HeapTupleGetOid(roleTup);
! 	is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
  
  	ReleaseSysCache(roleTup);
  
--- 801,808 ----
  	}
  
  	roleid = HeapTupleGetOid(roleTup);
! 	attributes = ((Form_pg_authid) GETSTRUCT(roleTup))->rolattr;
! 	is_superuser = (attributes & ROLE_ATTR_SUPERUSER);
  
  	ReleaseSysCache(roleTup);
  
*************** check_role(char **newval, void **extra,
*** 844,849 ****
--- 846,852 ----
  	Oid			roleid;
  	bool		is_superuser;
  	role_auth_extra *myextra;
+ 	RoleAttr	attributes;
  
  	if (strcmp(*newval, "none") == 0)
  	{
*************** check_role(char **newval, void **extra,
*** 872,878 ****
  		}
  
  		roleid = HeapTupleGetOid(roleTup);
! 		is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
  
  		ReleaseSysCache(roleTup);
  
--- 875,882 ----
  		}
  
  		roleid = HeapTupleGetOid(roleTup);
! 		attributes = ((Form_pg_authid) GETSTRUCT(roleTup))->rolattr;
! 		is_superuser = (attributes & ROLE_ATTR_SUPERUSER);
  
  		ReleaseSysCache(roleTup);
  
diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
new file mode 100644
index 1977f09..1a38f56
*** a/src/backend/replication/logical/logicalfuncs.c
--- b/src/backend/replication/logical/logicalfuncs.c
***************
*** 17,34 ****
  
  #include <unistd.h>
  
  #include "fmgr.h"
  #include "funcapi.h"
  #include "miscadmin.h"
- 
- #include "access/xlog_internal.h"
- 
- #include "catalog/pg_type.h"
- 
  #include "nodes/makefuncs.h"
! 
! #include "mb/pg_wchar.h"
! 
  #include "utils/array.h"
  #include "utils/builtins.h"
  #include "utils/inval.h"
--- 17,30 ----
  
  #include <unistd.h>
  
+ #include "access/xlog_internal.h"
+ #include "catalog/pg_type.h"
  #include "fmgr.h"
  #include "funcapi.h"
+ #include "mb/pg_wchar.h"
  #include "miscadmin.h"
  #include "nodes/makefuncs.h"
! #include "utils/acl.h"
  #include "utils/array.h"
  #include "utils/builtins.h"
  #include "utils/inval.h"
***************
*** 36,46 ****
  #include "utils/pg_lsn.h"
  #include "utils/resowner.h"
  #include "utils/lsyscache.h"
- 
  #include "replication/decode.h"
  #include "replication/logical.h"
  #include "replication/logicalfuncs.h"
- 
  #include "storage/fd.h"
  
  /* private date for writing out data */
--- 32,40 ----
*************** XLogRead(char *buf, TimeLineID tli, XLog
*** 205,211 ****
  static void
  check_permissions(void)
  {
! 	if (!superuser() && !has_rolreplication(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  				 (errmsg("must be superuser or replication role to use replication slots"))));
--- 199,205 ----
  static void
  check_permissions(void)
  {
! 	if (!have_role_attribute(ROLE_ATTR_REPLICATION))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  				 (errmsg("must be superuser or replication role to use replication slots"))));
diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
new file mode 100644
index bd4701f..c113a0b
*** a/src/backend/replication/slotfuncs.c
--- b/src/backend/replication/slotfuncs.c
***************
*** 20,32 ****
  #include "replication/slot.h"
  #include "replication/logical.h"
  #include "replication/logicalfuncs.h"
  #include "utils/builtins.h"
  #include "utils/pg_lsn.h"
  
  static void
  check_permissions(void)
  {
! 	if (!superuser() && !has_rolreplication(GetUserId()))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  				 (errmsg("must be superuser or replication role to use replication slots"))));
--- 20,33 ----
  #include "replication/slot.h"
  #include "replication/logical.h"
  #include "replication/logicalfuncs.h"
+ #include "utils/acl.h"
  #include "utils/builtins.h"
  #include "utils/pg_lsn.h"
  
  static void
  check_permissions(void)
  {
! 	if (!have_role_attribute(ROLE_ATTR_REPLICATION))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  				 (errmsg("must be superuser or replication role to use replication slots"))));
diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c
new file mode 100644
index 6c232dc..f41ad34
*** a/src/backend/rewrite/rowsecurity.c
--- b/src/backend/rewrite/rowsecurity.c
*************** check_enable_rls(Oid relid, Oid checkAsU
*** 521,527 ****
  	 */
  	if (!checkAsUser && row_security == ROW_SECURITY_OFF)
  	{
! 		if (has_bypassrls_privilege(user_id))
  			/* OK to bypass */
  			return RLS_NONE_ENV;
  		else
--- 521,527 ----
  	 */
  	if (!checkAsUser && row_security == ROW_SECURITY_OFF)
  	{
! 		if (has_role_attribute(user_id, ROLE_ATTR_BYPASSRLS))
  			/* OK to bypass */
  			return RLS_NONE_ENV;
  		else
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
new file mode 100644
index dc6eb2c..4c03955
*** a/src/backend/utils/adt/acl.c
--- b/src/backend/utils/adt/acl.c
*************** static Oid	convert_type_name(text *typen
*** 115,120 ****
--- 115,121 ----
  static AclMode convert_type_priv_string(text *priv_type_text);
  static AclMode convert_role_priv_string(text *priv_type_text);
  static AclResult pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode);
+ static RoleAttr convert_role_attr_string(text *attr_type_text);
  
  static void RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue);
  
*************** pg_role_aclcheck(Oid role_oid, Oid rolei
*** 4602,4607 ****
--- 4603,4788 ----
  	return ACLCHECK_NO_PRIV;
  }
  
+ /*
+  * pg_has_role_attribute_id
+  *		Check that the role with the given oid has the given named role
+  *		attribute.
+  *
+  * Note: This function applies superuser checks.  Therefore, if the provided
+  * role is a superuser, then the result will always be true.
+  */
+ Datum
+ pg_has_role_attribute_id(PG_FUNCTION_ARGS)
+ {
+ 	Oid			roleoid = PG_GETARG_OID(0);
+ 	text	   *attr_type_text = PG_GETARG_TEXT_P(1);
+ 	RoleAttr	attribute;
+ 
+ 	attribute = convert_role_attr_string(attr_type_text);
+ 
+ 	PG_RETURN_BOOL(has_role_attribute(roleoid, attribute));
+ }
+ 
+ /*
+  * pg_has_role_attribute_name
+  *		Check that the named role has the given named role attribute.
+  *
+  * Note: This function applies superuser checks.  Therefore, if the provided
+  * role is a superuser, then the result will always be true.
+  */
+ Datum
+ pg_has_role_attribute_name(PG_FUNCTION_ARGS)
+ {
+ 	Name		rolename = PG_GETARG_NAME(0);
+ 	text	   *attr_type_text = PG_GETARG_TEXT_P(1);
+ 	Oid			roleoid;
+ 	RoleAttr	attribute;
+ 
+ 	roleoid = get_role_oid(NameStr(*rolename), false);
+ 	attribute = convert_role_attr_string(attr_type_text);
+ 
+ 	PG_RETURN_BOOL(has_role_attribute(roleoid, attribute));
+ }
+ 
+ /*
+  * pg_check_role_attribute_id
+  *		Check that the role with the given oid has the given named role
+  *		attribute.
+  *
+  * Note: This function is different from 'pg_has_role_attribute_id_attr' in that
+  * it does *not* apply any superuser checks.  Therefore, this function will
+  * always return the set value of the attribute, despite the superuser-ness of
+  * the provided role.
+  */
+ Datum
+ pg_check_role_attribute_id(PG_FUNCTION_ARGS)
+ {
+ 	Oid			roleoid = PG_GETARG_OID(0);
+ 	text	   *attr_type_text = PG_GETARG_TEXT_P(1);
+ 	RoleAttr	attribute;
+ 
+ 	attribute = convert_role_attr_string(attr_type_text);
+ 
+ 	PG_RETURN_BOOL(check_role_attribute(roleoid, attribute));
+ }
+ 
+ /*
+  * pg_check_role_attribute_name
+  *		Check that the named role has the given named role attribute.
+  *
+  * Note: This function is different from 'pg_has_role_attribute_name_attr' in
+  * that it does *not* apply any superuser checks.  Therefore, this function will
+  * always return the set value of the attribute, despite the superuser-ness of
+  * the provided role.
+  */
+ Datum
+ pg_check_role_attribute_name(PG_FUNCTION_ARGS)
+ {
+ 	Name		rolename = PG_GETARG_NAME(0);
+ 	text	   *attr_type_text = PG_GETARG_TEXT_P(1);
+ 	Oid			roleoid;
+ 	RoleAttr	attribute;
+ 
+ 	roleoid = get_role_oid(NameStr(*rolename), false);
+ 	attribute = convert_role_attr_string(attr_type_text);
+ 
+ 	PG_RETURN_BOOL(check_role_attribute(roleoid, attribute));
+ }
+ 
+ /*
+  * pg_check_role_attribute_attrs
+  *		Check that the named attribute is enabled in the given RoleAttr
+  *		representation of role attributes.
+  */
+ Datum
+ pg_check_role_attribute_attrs(PG_FUNCTION_ARGS)
+ {
+ 	RoleAttr	attributes = PG_GETARG_INT64(0);
+ 	text	   *attr_type_text = PG_GETARG_TEXT_P(1);
+ 	RoleAttr	attribute;
+ 
+ 	attribute = convert_role_attr_string(attr_type_text);
+ 
+ 	PG_RETURN_BOOL(attributes & attribute);
+ }
+ 
+ /*
+  * pg_all_role_attributes
+  *		Convert a RoleAttr representation of role attributes into an array of
+  *		corresponding text values.
+  *
+  * The first and only argument is a RoleAttr (int64) representation of the
+  * role attributes.
+  */
+ Datum
+ pg_all_role_attributes(PG_FUNCTION_ARGS)
+ {
+ 	RoleAttr		attributes = PG_GETARG_INT64(0);
+ 	Datum		   *temp_array;
+ 	ArrayType	   *result;
+ 	int				i = 0;
+ 
+ 	/*
+ 	 * Short-circuit the case for no attributes assigned.
+ 	 */
+ 	if (attributes == ROLE_ATTR_NONE)
+ 		PG_RETURN_ARRAYTYPE_P(construct_empty_array(TEXTOID));
+ 
+ 	temp_array = (Datum *) palloc(N_ROLE_ATTRIBUTES * sizeof(Datum));
+ 
+ 	/* Determine which attributes are assigned. */
+ 	if (attributes & ROLE_ATTR_SUPERUSER)
+ 		temp_array[i++] = CStringGetTextDatum(_("Superuser"));
+ 	if (attributes & ROLE_ATTR_INHERIT)
+ 		temp_array[i++] = CStringGetTextDatum(_("Inherit"));
+ 	if (attributes & ROLE_ATTR_CREATEROLE)
+ 		temp_array[i++] = CStringGetTextDatum(_("Create Role"));
+ 	if (attributes & ROLE_ATTR_CREATEDB)
+ 		temp_array[i++] = CStringGetTextDatum(_("Create DB"));
+ 	if (attributes & ROLE_ATTR_CATUPDATE)
+ 		temp_array[i++] = CStringGetTextDatum(_("Catalog Update"));
+ 	if (attributes & ROLE_ATTR_CANLOGIN)
+ 		temp_array[i++] = CStringGetTextDatum(_("Login"));
+ 	if (attributes & ROLE_ATTR_REPLICATION)
+ 		temp_array[i++] = CStringGetTextDatum(_("Replication"));
+ 	if (attributes & ROLE_ATTR_BYPASSRLS)
+ 		temp_array[i++] = CStringGetTextDatum(_("Bypass RLS"));
+ 
+ 	result = construct_array(temp_array, i, TEXTOID, -1, false, 'i');
+ 
+ 	PG_RETURN_ARRAYTYPE_P(result);
+ }
+ 
+ /*
+  * convert_role_attr_string
+  *		Convert text string to RoleAttr value.
+  */
+ static RoleAttr
+ convert_role_attr_string(text *attr_type_text)
+ {
+ 	char	   *attr_type = text_to_cstring(attr_type_text);
+ 
+ 	if (pg_strcasecmp(attr_type, "SUPERUSER") == 0)
+ 		return ROLE_ATTR_SUPERUSER;
+ 	else if (pg_strcasecmp(attr_type, "INHERIT") == 0)
+ 		return ROLE_ATTR_INHERIT;
+ 	else if (pg_strcasecmp(attr_type, "CREATEROLE") == 0)
+ 		return ROLE_ATTR_CREATEROLE;
+ 	else if (pg_strcasecmp(attr_type, "CREATEDB") == 0)
+ 		return ROLE_ATTR_CREATEDB;
+ 	else if (pg_strcasecmp(attr_type, "CATUPDATE") == 0)
+ 		return ROLE_ATTR_CATUPDATE;
+ 	else if (pg_strcasecmp(attr_type, "CANLOGIN") == 0)
+ 		return ROLE_ATTR_CANLOGIN;
+ 	else if (pg_strcasecmp(attr_type, "REPLICATION") == 0)
+ 		return ROLE_ATTR_REPLICATION;
+ 	else if (pg_strcasecmp(attr_type, "BYPASSRLS") == 0)
+ 		return ROLE_ATTR_BYPASSRLS;
+ 	else
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 				 errmsg("unrecognized role attribute: \"%s\"", attr_type)));
+ }
  
  /*
   * initialization function (called by InitPostgres)
*************** RoleMembershipCacheCallback(Datum arg, i
*** 4634,4656 ****
  }
  
  
- /* Check if specified role has rolinherit set */
- static bool
- has_rolinherit(Oid roleid)
- {
- 	bool		result = false;
- 	HeapTuple	utup;
- 
- 	utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
- 	if (HeapTupleIsValid(utup))
- 	{
- 		result = ((Form_pg_authid) GETSTRUCT(utup))->rolinherit;
- 		ReleaseSysCache(utup);
- 	}
- 	return result;
- }
- 
- 
  /*
   * Get a list of roles that the specified roleid has the privileges of
   *
--- 4815,4820 ----
*************** roles_has_privs_of(Oid roleid)
*** 4697,4703 ****
  		int			i;
  
  		/* Ignore non-inheriting roles */
! 		if (!has_rolinherit(memberid))
  			continue;
  
  		/* Find roles that memberid is directly a member of */
--- 4861,4867 ----
  		int			i;
  
  		/* Ignore non-inheriting roles */
! 		if (!has_role_attribute(memberid, ROLE_ATTR_INHERIT))
  			continue;
  
  		/* Find roles that memberid is directly a member of */
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
new file mode 100644
index 5c75390..ccb1066
*** a/src/backend/utils/adt/ri_triggers.c
--- b/src/backend/utils/adt/ri_triggers.c
*************** RI_Initial_Check(Trigger *trigger, Relat
*** 2308,2314 ****
  	 * bypassrls right or is the table owner of the table(s) involved which
  	 * have RLS enabled.
  	 */
! 	if (!has_bypassrls_privilege(GetUserId()) &&
  		((pk_rel->rd_rel->relrowsecurity &&
  		  !pg_class_ownercheck(pkrte->relid, GetUserId())) ||
  		 (fk_rel->rd_rel->relrowsecurity &&
--- 2308,2314 ----
  	 * bypassrls right or is the table owner of the table(s) involved which
  	 * have RLS enabled.
  	 */
! 	if (!have_role_attribute(ROLE_ATTR_BYPASSRLS) &&
  		((pk_rel->rd_rel->relrowsecurity &&
  		  !pg_class_ownercheck(pkrte->relid, GetUserId())) ||
  		 (fk_rel->rd_rel->relrowsecurity &&
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
new file mode 100644
index 8fccb4c..db2a0fb
*** a/src/backend/utils/init/miscinit.c
--- b/src/backend/utils/init/miscinit.c
***************
*** 40,45 ****
--- 40,46 ----
  #include "storage/pg_shmem.h"
  #include "storage/proc.h"
  #include "storage/procarray.h"
+ #include "utils/acl.h"
  #include "utils/builtins.h"
  #include "utils/guc.h"
  #include "utils/memutils.h"
*************** SetUserIdAndContext(Oid userid, bool sec
*** 329,352 ****
  
  
  /*
-  * Check whether specified role has explicit REPLICATION privilege
-  */
- bool
- has_rolreplication(Oid roleid)
- {
- 	bool		result = false;
- 	HeapTuple	utup;
- 
- 	utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
- 	if (HeapTupleIsValid(utup))
- 	{
- 		result = ((Form_pg_authid) GETSTRUCT(utup))->rolreplication;
- 		ReleaseSysCache(utup);
- 	}
- 	return result;
- }
- 
- /*
   * Initialize user identity during normal backend startup
   */
  void
--- 330,335 ----
*************** InitializeSessionUserId(const char *role
*** 375,381 ****
  	roleid = HeapTupleGetOid(roleTup);
  
  	AuthenticatedUserId = roleid;
! 	AuthenticatedUserIsSuperuser = rform->rolsuper;
  
  	/* This sets OuterUserId/CurrentUserId too */
  	SetSessionUserId(roleid, AuthenticatedUserIsSuperuser);
--- 358,364 ----
  	roleid = HeapTupleGetOid(roleTup);
  
  	AuthenticatedUserId = roleid;
! 	AuthenticatedUserIsSuperuser = (rform->rolattr & ROLE_ATTR_SUPERUSER);
  
  	/* This sets OuterUserId/CurrentUserId too */
  	SetSessionUserId(roleid, AuthenticatedUserIsSuperuser);
*************** InitializeSessionUserId(const char *role
*** 394,400 ****
  		/*
  		 * Is role allowed to login at all?
  		 */
! 		if (!rform->rolcanlogin)
  			ereport(FATAL,
  					(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
  					 errmsg("role \"%s\" is not permitted to log in",
--- 377,383 ----
  		/*
  		 * Is role allowed to login at all?
  		 */
! 		if (!(rform->rolattr & ROLE_ATTR_CANLOGIN))
  			ereport(FATAL,
  					(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
  					 errmsg("role \"%s\" is not permitted to log in",
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
new file mode 100644
index c348034..268001f
*** a/src/backend/utils/init/postinit.c
--- b/src/backend/utils/init/postinit.c
*************** InitPostgres(const char *in_dbname, Oid
*** 762,768 ****
  	{
  		Assert(!bootstrap);
  
! 		if (!superuser() && !has_rolreplication(GetUserId()))
  			ereport(FATAL,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser or replication role to start walsender")));
--- 762,768 ----
  	{
  		Assert(!bootstrap);
  
! 		if (!have_role_attribute(ROLE_ATTR_REPLICATION))
  			ereport(FATAL,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  					 errmsg("must be superuser or replication role to start walsender")));
diff --git a/src/backend/utils/misc/superuser.c b/src/backend/utils/misc/superuser.c
new file mode 100644
index ff0f947..67d070c
*** a/src/backend/utils/misc/superuser.c
--- b/src/backend/utils/misc/superuser.c
*************** superuser_arg(Oid roleid)
*** 58,63 ****
--- 58,64 ----
  {
  	bool		result;
  	HeapTuple	rtup;
+ 	RoleAttr	attributes;
  
  	/* Quick out for cache hit */
  	if (OidIsValid(last_roleid) && last_roleid == roleid)
*************** superuser_arg(Oid roleid)
*** 71,77 ****
  	rtup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
  	if (HeapTupleIsValid(rtup))
  	{
! 		result = ((Form_pg_authid) GETSTRUCT(rtup))->rolsuper;
  		ReleaseSysCache(rtup);
  	}
  	else
--- 72,79 ----
  	rtup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
  	if (HeapTupleIsValid(rtup))
  	{
! 		attributes = ((Form_pg_authid) GETSTRUCT(rtup))->rolattr;
! 		result = (attributes & ROLE_ATTR_SUPERUSER);
  		ReleaseSysCache(rtup);
  	}
  	else
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
new file mode 100644
index eb633bc..f638167
*** a/src/bin/pg_dump/pg_dumpall.c
--- b/src/bin/pg_dump/pg_dumpall.c
*************** dumpRoles(PGconn *conn)
*** 671,680 ****
  	/* note: rolconfig is dumped later */
  	if (server_version >= 90500)
  		printfPQExpBuffer(buf,
! 						  "SELECT oid, rolname, rolsuper, rolinherit, "
! 						  "rolcreaterole, rolcreatedb, "
! 						  "rolcanlogin, rolconnlimit, rolpassword, "
! 						  "rolvaliduntil, rolreplication, rolbypassrls, "
  			 "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
  						  "rolname = current_user AS is_current_user "
  						  "FROM pg_authid "
--- 671,686 ----
  	/* note: rolconfig is dumped later */
  	if (server_version >= 90500)
  		printfPQExpBuffer(buf,
! 						  "SELECT oid, rolname, "
! 						  "pg_check_role_attribute(oid, 'SUPERUSER') AS rolsuper, "
! 						  "pg_check_role_attribute(oid, 'INHERIT') AS rolinherit, "
! 						  "pg_check_role_attribute(oid, 'CREATEROLE') AS rolcreaterole, "
! 						  "pg_check_role_attribute(oid, 'CREATEDB') AS rolcreatedb, "
! 						  "pg_check_role_attribute(oid, 'CANLOGIN') AS rolcanlogin, "
! 						  "pg_check_role_attribute(oid, 'REPLICATION') AS rolreplication, "
! 						  "pg_check_role_attribute(oid, 'BYPASSRLS') AS rolbypassrls, "
! 						  "rolconnlimit, rolpassword, "
! 						  "rolvaliduntil, "
  			 "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
  						  "rolname = current_user AS is_current_user "
  						  "FROM pg_authid "
diff --git a/src/include/catalog/acldefs.h b/src/include/catalog/acldefs.h
new file mode 100644
index ...2dcc174
*** a/src/include/catalog/acldefs.h
--- b/src/include/catalog/acldefs.h
***************
*** 0 ****
--- 1,72 ----
+ /*-------------------------------------------------------------------------
+  *
+  * acldefs.h
+  *	  base definitions for ACLs and role attributes
+  *
+  * Portions Copyright (c) 2014, PostgreSQL Global Development Group
+  *
+  * src/include/catalog/acldefs.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef ACLDEFS_H
+ #define ACLDEFS_H
+ 
+ /*
+  * Grantable rights are encoded so that we can OR them together in a bitmask.
+  * The present representation of AclItem limits us to 16 distinct rights,
+  * even though AclMode is defined as uint32.  See utils/acl.h.
+  *
+  * Caution: changing these codes breaks stored ACLs, hence forces initdb.
+  */
+ typedef uint32 AclMode;			/* a bitmask of privilege bits */
+ 
+ #define ACL_INSERT		(1<<0)	/* for relations */
+ #define ACL_SELECT		(1<<1)
+ #define ACL_UPDATE		(1<<2)
+ #define ACL_DELETE		(1<<3)
+ #define ACL_TRUNCATE	(1<<4)
+ #define ACL_REFERENCES	(1<<5)
+ #define ACL_TRIGGER		(1<<6)
+ #define ACL_EXECUTE		(1<<7)	/* for functions */
+ #define ACL_USAGE		(1<<8)	/* for languages, namespaces, FDWs, and
+ 								 * servers */
+ #define ACL_CREATE		(1<<9)	/* for namespaces and databases */
+ #define ACL_CREATE_TEMP (1<<10) /* for databases */
+ #define ACL_CONNECT		(1<<11) /* for databases */
+ #define N_ACL_RIGHTS	12		/* 1 plus the last 1<<x */
+ #define ACL_NO_RIGHTS	0
+ /* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
+ #define ACL_SELECT_FOR_UPDATE	ACL_UPDATE
+ 
+ #define ACL_ID_PUBLIC	0		/* placeholder for id in a PUBLIC acl item */
+ 
+ 
+ /*
+  * Role attributes are encoded so that we can OR them together in a bitmask.
+  * The present representation of RoleAttr (defined in acl.h) limits us to 64
+  * distinct rights.
+  *
+  * Note about ROLE_ATTR_ALL: This symbol is used verbatim by genbki.pl, which
+  * means we need to hard-code its value instead of using a symbolic definition.
+  * Therefore, whenever role attributes are changed, this value MUST be updated
+  * manually.
+  */
+ 
+ /* A bitmask for role attributes */
+ typedef uint64 RoleAttr;
+ 
+ #define ROLE_ATTR_NONE			0
+ #define ROLE_ATTR_SUPERUSER		(1<<0)
+ #define ROLE_ATTR_INHERIT		(1<<1)
+ #define ROLE_ATTR_CREATEROLE	(1<<2)
+ #define ROLE_ATTR_CREATEDB		(1<<3)
+ #define ROLE_ATTR_CATUPDATE		(1<<4)
+ #define ROLE_ATTR_CANLOGIN		(1<<5)
+ #define ROLE_ATTR_REPLICATION	(1<<6)
+ #define ROLE_ATTR_BYPASSRLS		(1<<7)
+ #define N_ROLE_ATTRIBUTES		8		/* 1 plus the last 1<<x */
+ #define ROLE_ATTR_ALL			255		/* (1 << N_ROLE_ATTRIBUTES) - 1 */
+ 
+ 
+ #endif   /* ACLDEFS_H */
diff --git a/src/include/catalog/pg_authid.h b/src/include/catalog/pg_authid.h
new file mode 100644
index 3b63d2b..a45f38d
*** a/src/include/catalog/pg_authid.h
--- b/src/include/catalog/pg_authid.h
***************
*** 21,26 ****
--- 21,27 ----
  #ifndef PG_AUTHID_H
  #define PG_AUTHID_H
  
+ #include "catalog/acldefs.h"
  #include "catalog/genbki.h"
  
  /*
***************
*** 45,60 ****
  CATALOG(pg_authid,1260) BKI_SHARED_RELATION BKI_ROWTYPE_OID(2842) BKI_SCHEMA_MACRO
  {
  	NameData	rolname;		/* name of role */
! 	bool		rolsuper;		/* read this field via superuser() only! */
! 	bool		rolinherit;		/* inherit privileges from other roles? */
! 	bool		rolcreaterole;	/* allowed to create more roles? */
! 	bool		rolcreatedb;	/* allowed to create databases? */
! 	bool		rolcatupdate;	/* allowed to alter catalogs manually? */
! 	bool		rolcanlogin;	/* allowed to log in as session user? */
! 	bool		rolreplication; /* role used for streaming replication */
! 	bool		rolbypassrls;	/* allowed to bypass row level security? */
  	int32		rolconnlimit;	/* max connections allowed (-1=no limit) */
- 
  	/* remaining fields may be null; use heap_getattr to read them! */
  	text		rolpassword;	/* password, if any */
  	timestamptz rolvaliduntil;	/* password expiration time, if any */
--- 46,53 ----
  CATALOG(pg_authid,1260) BKI_SHARED_RELATION BKI_ROWTYPE_OID(2842) BKI_SCHEMA_MACRO
  {
  	NameData	rolname;		/* name of role */
! 	int64		rolattr;		/* role attribute bitmask */
  	int32		rolconnlimit;	/* max connections allowed (-1=no limit) */
  	/* remaining fields may be null; use heap_getattr to read them! */
  	text		rolpassword;	/* password, if any */
  	timestamptz rolvaliduntil;	/* password expiration time, if any */
*************** typedef FormData_pg_authid *Form_pg_auth
*** 74,101 ****
   *		compiler constants for pg_authid
   * ----------------
   */
! #define Natts_pg_authid					12
  #define Anum_pg_authid_rolname			1
! #define Anum_pg_authid_rolsuper			2
! #define Anum_pg_authid_rolinherit		3
! #define Anum_pg_authid_rolcreaterole	4
! #define Anum_pg_authid_rolcreatedb		5
! #define Anum_pg_authid_rolcatupdate		6
! #define Anum_pg_authid_rolcanlogin		7
! #define Anum_pg_authid_rolreplication	8
! #define Anum_pg_authid_rolbypassrls		9
! #define Anum_pg_authid_rolconnlimit		10
! #define Anum_pg_authid_rolpassword		11
! #define Anum_pg_authid_rolvaliduntil	12
  
  /* ----------------
   *		initial contents of pg_authid
   *
   * The uppercase quantities will be replaced at initdb time with
   * user choices.
   * ----------------
   */
! DATA(insert OID = 10 ( "POSTGRES" t t t t t t t t -1 _null_ _null_));
  
  #define BOOTSTRAP_SUPERUSERID 10
  
--- 67,91 ----
   *		compiler constants for pg_authid
   * ----------------
   */
! #define Natts_pg_authid					5
  #define Anum_pg_authid_rolname			1
! #define Anum_pg_authid_rolattr			2
! #define Anum_pg_authid_rolconnlimit		3
! #define Anum_pg_authid_rolpassword		4
! #define Anum_pg_authid_rolvaliduntil	5
! 
  
  /* ----------------
   *		initial contents of pg_authid
   *
   * The uppercase quantities will be replaced at initdb time with
   * user choices.
+  *
+  * PGROLATTRALL is substituted by genbki.pl to use the value defined by
+  * ROLE_ATTR_ALL.
   * ----------------
   */
! DATA(insert OID = 10 ( "POSTGRES" PGROLATTRALL -1 _null_ _null_));
  
  #define BOOTSTRAP_SUPERUSERID 10
  
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
new file mode 100644
index f766ed7..7f64aaa
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DESCR("rank of hypothetical row without
*** 5136,5141 ****
--- 5136,5154 ----
  DATA(insert OID = 3993 ( dense_rank_final	PGNSP PGUID 12 1 0 2276 0 f f f f f f i 2 0 20 "2281 2276" "{2281,2276}" "{i,v}" _null_ _null_	hypothetical_dense_rank_final _null_ _null_ _null_ ));
  DESCR("aggregate final function");
  
+ /* role attribute support functions */
+ DATA(insert OID = 3994 ( pg_has_role_attribute		PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 16 "26 25" _null_ _null_ _null_ _null_ pg_has_role_attribute_id _null_ _null_ _null_ ));
+ DESCR("check role attribute by role oid with superuser bypass check");
+ DATA(insert OID = 3995 ( pg_has_role_attribute		PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 16 "19 25" _null_ _null_ _null_ _null_ pg_has_role_attribute_name _null_ _null_ _null_ ));
+ DESCR("check role attribute by role name with superuser bypass check");
+ DATA(insert OID = 3996 ( pg_check_role_attribute		PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 16 "26 25" _null_ _null_ _null_ _null_ pg_check_role_attribute_id _null_ _null_ _null_ ));
+ DESCR("check role attribute by role id");
+ DATA(insert OID = 3997 ( pg_check_role_attribute		PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 16 "19 25" _null_ _null_ _null_ _null_ pg_check_role_attribute_name _null_ _null_ _null_ ));
+ DESCR("check role attribute by role name");
+ DATA(insert OID = 3998 ( pg_check_role_attribute		PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 16 "20 25" _null_ _null_ _null_ _null_ pg_check_role_attribute_attrs _null_ _null_ _null_ ));
+ DESCR("check role attribute");
+ DATA(insert OID = 3999 ( pg_all_role_attributes		PGNSP PGUID 12 10 0 0 0 f f f f t f s 1 0 1009 "20" _null_ _null_ _null_ _null_ pg_all_role_attributes _null_ _null_ _null_));
+ DESCR("convert role attributes to string array");
  
  /*
   * Symbolic values for provolatile column: these indicate whether the result
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
new file mode 100644
index 458eeb0..1b97728
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 23,28 ****
--- 23,29 ----
  #include "nodes/bitmapset.h"
  #include "nodes/primnodes.h"
  #include "nodes/value.h"
+ #include "catalog/acldefs.h"
  #include "utils/lockwaitpolicy.h"
  
  /* Possible sources of a Query */
*************** typedef enum SortByNulls
*** 51,83 ****
  	SORTBY_NULLS_LAST
  } SortByNulls;
  
- /*
-  * Grantable rights are encoded so that we can OR them together in a bitmask.
-  * The present representation of AclItem limits us to 16 distinct rights,
-  * even though AclMode is defined as uint32.  See utils/acl.h.
-  *
-  * Caution: changing these codes breaks stored ACLs, hence forces initdb.
-  */
- typedef uint32 AclMode;			/* a bitmask of privilege bits */
- 
- #define ACL_INSERT		(1<<0)	/* for relations */
- #define ACL_SELECT		(1<<1)
- #define ACL_UPDATE		(1<<2)
- #define ACL_DELETE		(1<<3)
- #define ACL_TRUNCATE	(1<<4)
- #define ACL_REFERENCES	(1<<5)
- #define ACL_TRIGGER		(1<<6)
- #define ACL_EXECUTE		(1<<7)	/* for functions */
- #define ACL_USAGE		(1<<8)	/* for languages, namespaces, FDWs, and
- 								 * servers */
- #define ACL_CREATE		(1<<9)	/* for namespaces and databases */
- #define ACL_CREATE_TEMP (1<<10) /* for databases */
- #define ACL_CONNECT		(1<<11) /* for databases */
- #define N_ACL_RIGHTS	12		/* 1 plus the last 1<<x */
- #define ACL_NO_RIGHTS	0
- /* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
- #define ACL_SELECT_FOR_UPDATE	ACL_UPDATE
- 
  
  /*****************************************************************************
   *	Query Tree
--- 52,57 ----
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
new file mode 100644
index a8e3164..4e8d81c
*** a/src/include/utils/acl.h
--- b/src/include/utils/acl.h
***************
*** 30,42 ****
  
  
  /*
-  * typedef AclMode is declared in parsenodes.h, also the individual privilege
-  * bit meanings are defined there
-  */
- 
- #define ACL_ID_PUBLIC	0		/* placeholder for id in a PUBLIC acl item */
- 
- /*
   * AclItem
   *
   * Note: must be same size on all platforms, because the size is hardcoded
--- 30,35 ----
*************** extern bool pg_foreign_data_wrapper_owne
*** 326,332 ****
  extern bool pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid);
  extern bool pg_event_trigger_ownercheck(Oid et_oid, Oid roleid);
  extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid);
! extern bool has_createrole_privilege(Oid roleid);
! extern bool has_bypassrls_privilege(Oid roleid);
  
  #endif   /* ACL_H */
--- 319,328 ----
  extern bool pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid);
  extern bool pg_event_trigger_ownercheck(Oid et_oid, Oid roleid);
  extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid);
! 
! /* role attribute check routines */
! extern bool has_role_attribute(Oid roleid, RoleAttr attribute);
! extern bool have_role_attribute(RoleAttr attribute);
! extern bool check_role_attribute(Oid roleid, RoleAttr attribute);
  
  #endif   /* ACL_H */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
new file mode 100644
index 2da3002..c8e0e3a
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum pg_has_role_id_name(PG_FUNC
*** 106,111 ****
--- 106,117 ----
  extern Datum pg_has_role_id_id(PG_FUNCTION_ARGS);
  extern Datum pg_has_role_name(PG_FUNCTION_ARGS);
  extern Datum pg_has_role_id(PG_FUNCTION_ARGS);
+ extern Datum pg_has_role_attribute_id(PG_FUNCTION_ARGS);
+ extern Datum pg_has_role_attribute_name(PG_FUNCTION_ARGS);
+ extern Datum pg_check_role_attribute_id(PG_FUNCTION_ARGS);
+ extern Datum pg_check_role_attribute_name(PG_FUNCTION_ARGS);
+ extern Datum pg_check_role_attribute_attrs(PG_FUNCTION_ARGS);
+ extern Datum pg_all_role_attributes(PG_FUNCTION_ARGS);
  
  /* bool.c */
  extern Datum boolin(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
new file mode 100644
index 80c3351..d105215
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
*************** pg_group| SELECT pg_authid.rolname AS gr
*** 1314,1320 ****
             FROM pg_auth_members
            WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist
     FROM pg_authid
!   WHERE (NOT pg_authid.rolcanlogin);
  pg_indexes| SELECT n.nspname AS schemaname,
      c.relname AS tablename,
      i.relname AS indexname,
--- 1314,1320 ----
             FROM pg_auth_members
            WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist
     FROM pg_authid
!   WHERE (NOT pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN'::text));
  pg_indexes| SELECT n.nspname AS schemaname,
      c.relname AS tablename,
      i.relname AS indexname,
*************** pg_replication_slots| SELECT l.slot_name
*** 1405,1421 ****
     FROM (pg_get_replication_slots() l(slot_name, plugin, slot_type, datoid, active, xmin, catalog_xmin, restart_lsn)
       LEFT JOIN pg_database d ON ((l.datoid = d.oid)));
  pg_roles| SELECT pg_authid.rolname,
!     pg_authid.rolsuper,
!     pg_authid.rolinherit,
!     pg_authid.rolcreaterole,
!     pg_authid.rolcreatedb,
!     pg_authid.rolcatupdate,
!     pg_authid.rolcanlogin,
!     pg_authid.rolreplication,
      pg_authid.rolconnlimit,
      '********'::text AS rolpassword,
      pg_authid.rolvaliduntil,
-     pg_authid.rolbypassrls,
      s.setconfig AS rolconfig,
      pg_authid.oid
     FROM (pg_authid
--- 1405,1421 ----
     FROM (pg_get_replication_slots() l(slot_name, plugin, slot_type, datoid, active, xmin, catalog_xmin, restart_lsn)
       LEFT JOIN pg_database d ON ((l.datoid = d.oid)));
  pg_roles| SELECT pg_authid.rolname,
!     pg_check_role_attribute(pg_authid.rolattr, 'SUPERUSER'::text) AS rolsuper,
!     pg_check_role_attribute(pg_authid.rolattr, 'INHERIT'::text) AS rolinherit,
!     pg_check_role_attribute(pg_authid.rolattr, 'CREATEROLE'::text) AS rolcreaterole,
!     pg_check_role_attribute(pg_authid.rolattr, 'CREATEDB'::text) AS rolcreatedb,
!     pg_check_role_attribute(pg_authid.rolattr, 'CATUPDATE'::text) AS rolcatupdate,
!     pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN'::text) AS rolcanlogin,
!     pg_check_role_attribute(pg_authid.rolattr, 'REPLICATION'::text) AS rolreplication,
!     pg_check_role_attribute(pg_authid.rolattr, 'BYPASSRLS'::text) AS rolbypassrls,
      pg_authid.rolconnlimit,
      '********'::text AS rolpassword,
      pg_authid.rolvaliduntil,
      s.setconfig AS rolconfig,
      pg_authid.oid
     FROM (pg_authid
*************** pg_settings| SELECT a.name,
*** 1608,1623 ****
     FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline);
  pg_shadow| SELECT pg_authid.rolname AS usename,
      pg_authid.oid AS usesysid,
!     pg_authid.rolcreatedb AS usecreatedb,
!     pg_authid.rolsuper AS usesuper,
!     pg_authid.rolcatupdate AS usecatupd,
!     pg_authid.rolreplication AS userepl,
      pg_authid.rolpassword AS passwd,
      (pg_authid.rolvaliduntil)::abstime AS valuntil,
      s.setconfig AS useconfig
     FROM (pg_authid
       LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid))))
!   WHERE pg_authid.rolcanlogin;
  pg_stat_activity| SELECT s.datid,
      d.datname,
      s.pid,
--- 1608,1623 ----
     FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline);
  pg_shadow| SELECT pg_authid.rolname AS usename,
      pg_authid.oid AS usesysid,
!     pg_check_role_attribute(pg_authid.rolattr, 'CREATEDB'::text) AS usecreatedb,
!     pg_check_role_attribute(pg_authid.rolattr, 'SUPERUSER'::text) AS usesuper,
!     pg_check_role_attribute(pg_authid.rolattr, 'CATUPDATE'::text) AS usecatupd,
!     pg_check_role_attribute(pg_authid.rolattr, 'REPLICATION'::text) AS userepl,
      pg_authid.rolpassword AS passwd,
      (pg_authid.rolvaliduntil)::abstime AS valuntil,
      s.setconfig AS useconfig
     FROM (pg_authid
       LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid))))
!   WHERE pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN'::text);
  pg_stat_activity| SELECT s.datid,
      d.datname,
      s.pid,
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to