Alvaro Herrera wrote:
> The fact that the ROLE_ATTR_* definitions are in pg_authid.h means that
> there are now a lot of files that need to include that one.  I think the
> includes relative to ACLs and roles is rather messy now, and this patch
> makes it a bit worse.
> 
> I think we should create a new header file (maybe acltypes.h or
> acldefs.h), which only contains the AclMode and RoleAttr typedefs and
> their associated defines; that one would be included from parsenodes.h,
> acl.h and pg_authid.h.  Everything else would be in acl.h.  So code that
> currently checks permissions using only acl.h do not need any extra
> includes.

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


Unrelated: when changing from unified to context format, I saw
filterdiff fail to produce a complete patch, skipping some hunks in its
output.  My first impression was that I had dropped some hunks in git,
so I wasted some time comparing v5 and v6 by hand before remembering
that Michael Paquier had mentioned this problem previously.

-- 
Álvaro Herrera                http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 8475985..3181a79 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -22,7 +22,6 @@
 #include "access/xlog_internal.h"
 #include "access/xlogutils.h"
 #include "catalog/catalog.h"
-#include "catalog/pg_authid.h"
 #include "catalog/pg_type.h"
 #include "funcapi.h"
 #include "miscadmin.h"
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index a403c64..a6de2ff 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -28,7 +28,7 @@ all: $(BKIFILES) schemapg.h
 # 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 \
+	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
index 4663429..21d282c 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -5038,18 +5038,18 @@ pg_extension_ownercheck(Oid ext_oid, Oid roleid)
  * 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: 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.)
+ * 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)
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
index 2929b66..415ac17 100644
--- a/src/backend/catalog/genbki.pl
+++ b/src/backend/catalog/genbki.pl
@@ -91,7 +91,7 @@ my $BOOTSTRAP_SUPERUSERID =
 my $PG_CATALOG_NAMESPACE =
   find_defined_symbol('pg_namespace.h', 'PG_CATALOG_NAMESPACE');
 my $ROLE_ATTR_ALL =
-  find_defined_symbol('pg_authid.h', '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);
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 5bde610..564f77a 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -73,14 +73,7 @@ CreateRole(CreateRoleStmt *stmt)
 	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? */
-	RoleAttr	attributes = ROLE_ATTR_NONE;	/* role attributes, initialized to none. */
+	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 */
@@ -102,13 +95,17 @@ CreateRole(CreateRoleStmt *stmt)
 	DefElem    *dvalidUntil = NULL;
 	DefElem    *dbypassRLS = NULL;
 
-	/* The defaults can vary depending on the original statement type */
+	/*
+	 * 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:
-			canlogin = true;
+			attributes |= ROLE_ATTR_CANLOGIN;
 			/* may eventually want inherit to default to false here */
 			break;
 		case ROLESTMT_GROUP:
@@ -243,21 +240,74 @@ CreateRole(CreateRoleStmt *stmt)
 	if (dpassword && dpassword->arg)
 		password = strVal(dpassword->arg);
 
-	/* Role Attributes */
+	/* Set up role attributes and check permissions to set each of them */
 	if (dissuper)
-		issuper = intVal(dissuper->arg) != 0;
+	{
+		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)
-		inherit = intVal(dinherit->arg) != 0;
+	{
+		if (intVal(dinherit->arg) != 0)
+			attributes |= ROLE_ATTR_INHERIT;
+		else
+			attributes &= ~ROLE_ATTR_INHERIT;
+	}
 	if (dcreaterole)
-		createrole = intVal(dcreaterole->arg) != 0;
+	{
+		if (intVal(dcreaterole->arg) != 0)
+			attributes |= ROLE_ATTR_CREATEROLE;
+		else
+			attributes &= ~ROLE_ATTR_CREATEROLE;
+	}
 	if (dcreatedb)
-		createdb = intVal(dcreatedb->arg) != 0;
+	{
+		if (intVal(dcreatedb->arg) != 0)
+			attributes |= ROLE_ATTR_CREATEDB;
+		else
+			attributes &= ~ROLE_ATTR_CREATEDB;
+	}
 	if (dcanlogin)
-		canlogin = intVal(dcanlogin->arg) != 0;
+	{
+		if (intVal(dcanlogin->arg) != 0)
+			attributes |= ROLE_ATTR_CANLOGIN;
+		else
+			attributes &= ~ROLE_ATTR_CANLOGIN;
+	}
 	if (disreplication)
-		isreplication = intVal(disreplication->arg) != 0;
+	{
+		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)
-		bypassrls = intVal(dbypassRLS->arg) != 0;
+	{
+		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)
 	{
@@ -276,35 +326,11 @@ CreateRole(CreateRoleStmt *stmt)
 	if (dvalidUntil)
 		validUntil = strVal(dvalidUntil->arg);
 
-	/* 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_role_attribute(ROLE_ATTR_CREATEROLE))
-			ereport(ERROR,
-					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-					 errmsg("permission denied to create role")));
-	}
+	/* 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)
@@ -351,22 +377,6 @@ CreateRole(CreateRoleStmt *stmt)
 								validUntil_datum,
 								validUntil_null);
 
-	/* Set all role attributes */
-	if (issuper)
-		attributes |= ROLE_ATTR_SUPERUSER;
-	if (inherit)
-		attributes |= ROLE_ATTR_INHERIT;
-	if (createrole)
-		attributes |= ROLE_ATTR_CREATEROLE;
-	if (createdb)
-		attributes |= ROLE_ATTR_CREATEDB;
-	if (canlogin)
-		attributes |= ROLE_ATTR_CANLOGIN;
-	if (isreplication)
-		attributes |= ROLE_ATTR_REPLICATION;
-	if (bypassrls)
-		attributes |= ROLE_ATTR_BYPASSRLS;
-
 	/*
 	 * Build a tuple to insert
 	 */
@@ -663,8 +673,8 @@ AlterRole(AlterRoleStmt *stmt)
 	roleid = HeapTupleGetOid(tuple);
 
 	/*
-	 * To mess with a superuser you gotta be superuser; else you need
-	 * createrole, or just want to change your own password
+	 * 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;
@@ -751,50 +761,64 @@ AlterRole(AlterRoleStmt *stmt)
 	 */
 	if (issuper >= 0)
 	{
-		attributes = (issuper > 0) ? attributes | (ROLE_ATTR_SUPERUSER | ROLE_ATTR_CATUPDATE)
-								   : attributes & ~(ROLE_ATTR_SUPERUSER | ROLE_ATTR_CATUPDATE);
+		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)
 	{
-		attributes = (inherit > 0) ? attributes | ROLE_ATTR_INHERIT
-								   : attributes & ~(ROLE_ATTR_INHERIT);
+		if (inherit > 0)
+			attributes |= ROLE_ATTR_INHERIT;
+		else
+			attributes &= ~ROLE_ATTR_INHERIT;
 		new_record_repl[Anum_pg_authid_rolattr - 1] = true;
 	}
 
 	if (createrole >= 0)
 	{
-		attributes = (createrole > 0) ? attributes | ROLE_ATTR_CREATEROLE
-									  : attributes & ~(ROLE_ATTR_CREATEROLE);
+		if (createrole > 0)
+			attributes |= ROLE_ATTR_CREATEROLE;
+		else
+			attributes &= ~ROLE_ATTR_CREATEROLE;
 		new_record_repl[Anum_pg_authid_rolattr - 1] = true;
 	}
 
 	if (createdb >= 0)
 	{
-		attributes = (createdb > 0) ? attributes | ROLE_ATTR_CREATEDB
-									: attributes & ~(ROLE_ATTR_CREATEDB);
+		if (createdb > 0)
+			attributes |= ROLE_ATTR_CREATEDB;
+		else
+			attributes &= ~ROLE_ATTR_CREATEDB;
 		new_record_repl[Anum_pg_authid_rolattr - 1] = true;
 	}
 
 	if (canlogin >= 0)
 	{
-		attributes = (canlogin > 0) ? attributes | ROLE_ATTR_CANLOGIN
-									: attributes & ~(ROLE_ATTR_CANLOGIN);
+		if (canlogin > 0)
+			attributes |= ROLE_ATTR_CANLOGIN;
+		else
+			attributes &= ~ROLE_ATTR_CANLOGIN;
 		new_record_repl[Anum_pg_authid_rolattr - 1] = true;
 	}
 
 	if (isreplication >= 0)
 	{
-		attributes = (isreplication > 0) ? attributes | ROLE_ATTR_REPLICATION
-										 : attributes & ~(ROLE_ATTR_REPLICATION);
+		if (isreplication > 0)
+			attributes |= ROLE_ATTR_REPLICATION;
+		else
+			attributes &= ~ROLE_ATTR_REPLICATION;
 		new_record_repl[Anum_pg_authid_rolattr - 1] = true;
 	}
 
 	if (bypassrls >= 0)
 	{
-		attributes = (bypassrls > 0) ? attributes | ROLE_ATTR_BYPASSRLS
-										 : attributes & ~(ROLE_ATTR_BYPASSRLS);
+		if (bypassrls > 0)
+			attributes |= ROLE_ATTR_BYPASSRLS;
+		else
+			attributes &= ~ROLE_ATTR_BYPASSRLS;
 		new_record_repl[Anum_pg_authid_rolattr - 1] = true;
 	}
 
diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
index 5f1126e..1a38f56 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -17,19 +17,13 @@
 
 #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 "access/xlog_internal.h"
-
-#include "catalog/pg_authid.h"
-#include "catalog/pg_type.h"
-
 #include "nodes/makefuncs.h"
-
-#include "mb/pg_wchar.h"
-
 #include "utils/acl.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
@@ -38,11 +32,9 @@
 #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 */
diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c
index bc6a23a..c113a0b 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -17,7 +17,6 @@
 #include "miscadmin.h"
 
 #include "access/htup_details.h"
-#include "catalog/pg_authid.h"
 #include "replication/slot.h"
 #include "replication/logical.h"
 #include "replication/logicalfuncs.h"
diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c
index 58633cc..f41ad34 100644
--- a/src/backend/rewrite/rowsecurity.c
+++ b/src/backend/rewrite/rowsecurity.c
@@ -36,7 +36,6 @@
 #include "access/heapam.h"
 #include "access/htup_details.h"
 #include "access/sysattr.h"
-#include "catalog/pg_authid.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_inherits_fn.h"
 #include "catalog/pg_policy.h"
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 93017b2..4c03955 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -4723,9 +4723,7 @@ pg_all_role_attributes(PG_FUNCTION_ARGS)
 	int				i = 0;
 
 	/*
-	 * If no attributes are assigned, then there is no need to go through the
-	 * individual checks for which are assigned.  Therefore, we can short circuit
-	 * and return an empty array.
+	 * Short-circuit the case for no attributes assigned.
 	 */
 	if (attributes == ROLE_ATTR_NONE)
 		PG_RETURN_ARRAYTYPE_P(construct_empty_array(TEXTOID));
@@ -4734,21 +4732,21 @@ pg_all_role_attributes(PG_FUNCTION_ARGS)
 
 	/* Determine which attributes are assigned. */
 	if (attributes & ROLE_ATTR_SUPERUSER)
-		temp_array[i++] = CStringGetTextDatum("Superuser");
+		temp_array[i++] = CStringGetTextDatum(_("Superuser"));
 	if (attributes & ROLE_ATTR_INHERIT)
-		temp_array[i++] = CStringGetTextDatum("Inherit");
+		temp_array[i++] = CStringGetTextDatum(_("Inherit"));
 	if (attributes & ROLE_ATTR_CREATEROLE)
-		temp_array[i++] = CStringGetTextDatum("Create Role");
+		temp_array[i++] = CStringGetTextDatum(_("Create Role"));
 	if (attributes & ROLE_ATTR_CREATEDB)
-		temp_array[i++] = CStringGetTextDatum("Create DB");
+		temp_array[i++] = CStringGetTextDatum(_("Create DB"));
 	if (attributes & ROLE_ATTR_CATUPDATE)
-		temp_array[i++] = CStringGetTextDatum("Catalog Update");
+		temp_array[i++] = CStringGetTextDatum(_("Catalog Update"));
 	if (attributes & ROLE_ATTR_CANLOGIN)
-		temp_array[i++] = CStringGetTextDatum("Login");
+		temp_array[i++] = CStringGetTextDatum(_("Login"));
 	if (attributes & ROLE_ATTR_REPLICATION)
-		temp_array[i++] = CStringGetTextDatum("Replication");
+		temp_array[i++] = CStringGetTextDatum(_("Replication"));
 	if (attributes & ROLE_ATTR_BYPASSRLS)
-		temp_array[i++] = CStringGetTextDatum("Bypass RLS");
+		temp_array[i++] = CStringGetTextDatum(_("Bypass RLS"));
 
 	result = construct_array(temp_array, i, TEXTOID, -1, false, 'i');
 
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index efbcc71..ccb1066 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -33,7 +33,6 @@
 #include "access/htup_details.h"
 #include "access/sysattr.h"
 #include "access/xact.h"
-#include "catalog/pg_authid.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_operator.h"
@@ -44,7 +43,6 @@
 #include "parser/parse_coerce.h"
 #include "parser/parse_relation.h"
 #include "miscadmin.h"
-#include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
 #include "utils/guc.h"
diff --git a/src/backend/utils/misc/superuser.c b/src/backend/utils/misc/superuser.c
index 9af77ed..67d070c 100644
--- a/src/backend/utils/misc/superuser.c
+++ b/src/backend/utils/misc/superuser.c
@@ -22,7 +22,6 @@
 
 #include "access/htup_details.h"
 #include "catalog/pg_authid.h"
-#include "utils/acl.h"
 #include "utils/inval.h"
 #include "utils/syscache.h"
 #include "miscadmin.h"
diff --git a/src/include/catalog/acldefs.h b/src/include/catalog/acldefs.h
new file mode 100644
index 0000000..2dcc174
--- /dev/null
+++ b/src/include/catalog/acldefs.h
@@ -0,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
index f28d9f4..a45f38d 100644
--- a/src/include/catalog/pg_authid.h
+++ b/src/include/catalog/pg_authid.h
@@ -21,6 +21,7 @@
 #ifndef PG_AUTHID_H
 #define PG_AUTHID_H
 
+#include "catalog/acldefs.h"
 #include "catalog/genbki.h"
 
 /*
@@ -73,34 +74,6 @@ typedef FormData_pg_authid *Form_pg_authid;
 #define Anum_pg_authid_rolpassword		4
 #define Anum_pg_authid_rolvaliduntil	5
 
-/* ----------------
- * 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.
- * ----------------
- */
-#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_NONE			0
-
-/* ----------------
- * All currently available attributes.
- *
- * Note: This value is currently used by genbki.pl.  Unfortunately, we have to
- * hard code this value as we cannot use an approach like (1 << N_ROLE_ATTRIBUTES) - 1
- * as genbki.pl simply uses the literal value associated with the #define symbol
- * which causes an incorrect substitution. Therefore, whenever new role attributes
- * are added this value MUST be changed as well.
- * ----------------
- */
-#define ROLE_ATTR_ALL          255 /* equals (1 << N_ROLE_ATTRIBUTES) - 1 */
 
 /* ----------------
  *		initial contents of pg_authid
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 458eeb0..ecb5780 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -23,8 +23,10 @@
 #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 QuerySource
 {
@@ -51,33 +53,6 @@ typedef enum SortByNulls
 	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
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 1687633..4e8d81c 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -30,13 +30,6 @@
 
 
 /*
- * 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
@@ -200,7 +193,6 @@ typedef enum AclObjectKind
 	MAX_ACL_KIND				/* MUST BE LAST */
 } AclObjectKind;
 
-typedef uint64 RoleAttr;		/* a bitmask for role attribute bits */
 
 /*
  * routines used internally
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 9ceb96b..b0b4fca 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1391,51 +1391,103 @@
      </row>
 
      <row>
-      <entry><structfield>rolsuper</structfield></entry>
-      <entry><type>bool</type></entry>
+      <entry><structfield>rolattr</structfield></entry>
+      <entry><type>bigint</type></entry>
+      <entry>
+       Role attributes; see <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><structfield>rolattr</> bitmap positions</title>
+
+   <tgroup cols="3">
+    <thead>
+     <row>
+      <entry>Position</entry>
+      <entry>Attribute</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><literal>0</literal></entry>
+      <entry>Superuser</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>
+      <entry><literal>1</literal></entry>
+      <entry>Inherit</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><literal>2</literal></entry>
+      <entry>Create Role</entry>
       <entry>Role can create more roles</entry>
      </row>
 
      <row>
-      <entry><structfield>rolcreatedb</structfield></entry>
-      <entry><type>bool</type></entry>
+      <entry><literal>3</literal></entry>
+      <entry>Create DB</entry>
       <entry>Role can create databases</entry>
      </row>
 
      <row>
-      <entry><structfield>rolcatupdate</structfield></entry>
-      <entry><type>bool</type></entry>
+      <entry><literal>4</literal></entry>
+      <entry>Catalog Update</entry>
       <entry>
-       Role can update system catalogs directly.  (Even a superuser cannot do
-       this unless this column is true)
+       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><literal>5</literal></entry>
+      <entry>Can Login</entry>
       <entry>
-       Role can log in. That is, this role can be given as the initial
-       session authorization identifier
+       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><literal>6</literal></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
@@ -1445,35 +1497,13 @@
      </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><literal>7</literal></entry>
+      <entry>By-pass Row Level Security</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.
+       Role can by-pass row level security policies when <literal>row_security</> is set <literal>off</>
       </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>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 7934a12..20b8325 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -15139,6 +15139,123 @@ SELECT has_function_privilege('joeuser', 'myfunc(int, text)', 'execute');
     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>.
+    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.
+    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
index 133143d..3181a79 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -27,6 +27,7 @@
 #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"
@@ -54,7 +55,7 @@ pg_start_backup(PG_FUNCTION_ARGS)
 
 	backupidstr = text_to_cstring(backupid);
 
-	if (!superuser() && !has_rolreplication(GetUserId()))
+	if (!have_role_attribute(ROLE_ATTR_REPLICATION))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 		   errmsg("must be superuser or replication role to run a backup")));
@@ -82,7 +83,7 @@ pg_stop_backup(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	stoppoint;
 
-	if (!superuser() && !has_rolreplication(GetUserId()))
+	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
index a403c64..a6de2ff 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -28,7 +28,7 @@ all: $(BKIFILES) schemapg.h
 # 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 \
+	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
index d30612c..21d282c 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -3423,26 +3423,6 @@ aclcheck_error_type(AclResult aclerr, Oid typeOid)
 }
 
 
-/* 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
  */
@@ -3630,7 +3610,7 @@ pg_class_aclmask(Oid table_oid, Oid roleid,
 	if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) &&
 		IsSystemClass(table_oid, classForm) &&
 		classForm->relkind != RELKIND_VIEW &&
-		!has_rolcatupdate(roleid) &&
+		!has_role_attribute(roleid, ROLE_ATTR_CATUPDATE) &&
 		!allowSystemTableMods)
 	{
 #ifdef ACLDEBUG
@@ -5051,52 +5031,80 @@ pg_extension_ownercheck(Oid ext_oid, Oid roleid)
 }
 
 /*
- * Check whether specified role has CREATEROLE privilege (or is a superuser)
+ * 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.)
+ * 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)
+has_role_attribute(Oid roleid, RoleAttr attribute)
 {
-	bool		result = false;
-	HeapTuple	utup;
-
-	/* Superusers bypass all permission checking. */
-	if (superuser_arg(roleid))
+	/*
+	 * Superusers bypass all permission checking except in the case of CATUPDATE.
+	 */
+	if (!(attribute & ROLE_ATTR_CATUPDATE) && superuser_arg(roleid))
 		return true;
 
-	utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
-	if (HeapTupleIsValid(utup))
-	{
-		result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreaterole;
-		ReleaseSysCache(utup);
-	}
-	return result;
+	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
-has_bypassrls_privilege(Oid roleid)
+check_role_attribute(Oid roleid, RoleAttr attribute)
 {
-	bool		result = false;
-	HeapTuple	utup;
+	RoleAttr	attributes;
+	HeapTuple	tuple;
 
-	/* Superusers bypass all permission checking. */
-	if (superuser_arg(roleid))
-		return true;
+	tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
 
-	utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
-	if (HeapTupleIsValid(utup))
-	{
-		result = ((Form_pg_authid) GETSTRUCT(utup))->rolbypassrls;
-		ReleaseSysCache(utup);
-	}
-	return result;
+	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
index ca89879..415ac17 100644
--- a/src/backend/catalog/genbki.pl
+++ b/src/backend/catalog/genbki.pl
@@ -90,6 +90,8 @@ my $BOOTSTRAP_SUPERUSERID =
   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);
@@ -144,6 +146,7 @@ foreach my $catname (@{ $catalogs->{names} })
 			# 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
index a036c62..87b6d8c 100644
--- a/src/backend/catalog/information_schema.sql
+++ b/src/backend/catalog/information_schema.sql
@@ -2884,7 +2884,12 @@ CREATE VIEW user_mapping_options AS
            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
+                       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
index e261307..6df7e4d 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -1310,7 +1310,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
 			}
 			else
 			{
-				if (!has_createrole_privilege(roleid))
+				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
index 22b8cee..ae93832 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -9,17 +9,17 @@
 CREATE VIEW pg_roles AS
     SELECT
         rolname,
-        rolsuper,
-        rolinherit,
-        rolcreaterole,
-        rolcreatedb,
-        rolcatupdate,
-        rolcanlogin,
-        rolreplication,
+        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,
-        rolbypassrls,
         setconfig as rolconfig,
         pg_authid.oid
     FROM pg_authid LEFT JOIN pg_db_role_setting s
@@ -29,16 +29,16 @@ CREATE VIEW pg_shadow AS
     SELECT
         rolname AS usename,
         pg_authid.oid AS usesysid,
-        rolcreatedb AS usecreatedb,
-        rolsuper AS usesuper,
-        rolcatupdate AS usecatupd,
-        rolreplication AS userepl,
+        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 rolcanlogin;
+    WHERE pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN');
 
 REVOKE ALL on pg_shadow FROM public;
 
@@ -48,7 +48,7 @@ CREATE VIEW pg_group AS
         oid AS grosysid,
         ARRAY(SELECT member FROM pg_auth_members WHERE roleid = oid) AS grolist
     FROM pg_authid
-    WHERE NOT rolcanlogin;
+    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
index 1a5244c..c079168 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -85,7 +85,6 @@ static bool get_db_info(const char *name, LOCKMODE lockmode,
 			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);
@@ -291,7 +290,7 @@ createdb(const CreatedbStmt *stmt)
 	 * "giveaway" attacks.  Note that a superuser will always have both of
 	 * these privileges a fortiori.
 	 */
-	if (!have_createdb_privilege())
+	if (!have_role_attribute(ROLE_ATTR_CREATEDB))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied to create database")));
@@ -965,7 +964,7 @@ RenameDatabase(const char *oldname, const char *newname)
 					   oldname);
 
 	/* must have createdb rights */
-	if (!have_createdb_privilege())
+	if (!have_role_attribute(ROLE_ATTR_CREATEDB))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied to rename database")));
@@ -1623,7 +1622,7 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId)
 		 * databases.  Because superusers will always have this right, we need
 		 * no special case for them.
 		 */
-		if (!have_createdb_privilege())
+		if (!have_role_attribute(ROLE_ATTR_CREATEDB))
 			ereport(ERROR,
 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				   errmsg("permission denied to change owner of database")));
@@ -1802,26 +1801,6 @@ get_db_info(const char *name, LOCKMODE lockmode,
 	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
  *
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 1a73fd8..564f77a 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -56,14 +56,6 @@ static void DelRoleMems(const char *rolename, Oid roleid,
 			bool admin_opt);
 
 
-/* Check if current user has createrole privileges */
-static bool
-have_createrole_privilege(void)
-{
-	return has_createrole_privilege(GetUserId());
-}
-
-
 /*
  * CREATE ROLE
  */
@@ -81,13 +73,7 @@ CreateRole(CreateRoleStmt *stmt)
 	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? */
+	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 */
@@ -109,13 +95,17 @@ CreateRole(CreateRoleStmt *stmt)
 	DefElem    *dvalidUntil = NULL;
 	DefElem    *dbypassRLS = NULL;
 
-	/* The defaults can vary depending on the original statement type */
+	/*
+	 * 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:
-			canlogin = true;
+			attributes |= ROLE_ATTR_CANLOGIN;
 			/* may eventually want inherit to default to false here */
 			break;
 		case ROLESTMT_GROUP:
@@ -249,18 +239,76 @@ CreateRole(CreateRoleStmt *stmt)
 
 	if (dpassword && dpassword->arg)
 		password = strVal(dpassword->arg);
+
+	/* Set up role attributes and check permissions to set each of them */
 	if (dissuper)
-		issuper = intVal(dissuper->arg) != 0;
+	{
+		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)
-		inherit = intVal(dinherit->arg) != 0;
+	{
+		if (intVal(dinherit->arg) != 0)
+			attributes |= ROLE_ATTR_INHERIT;
+		else
+			attributes &= ~ROLE_ATTR_INHERIT;
+	}
 	if (dcreaterole)
-		createrole = intVal(dcreaterole->arg) != 0;
+	{
+		if (intVal(dcreaterole->arg) != 0)
+			attributes |= ROLE_ATTR_CREATEROLE;
+		else
+			attributes &= ~ROLE_ATTR_CREATEROLE;
+	}
 	if (dcreatedb)
-		createdb = intVal(dcreatedb->arg) != 0;
+	{
+		if (intVal(dcreatedb->arg) != 0)
+			attributes |= ROLE_ATTR_CREATEDB;
+		else
+			attributes &= ~ROLE_ATTR_CREATEDB;
+	}
 	if (dcanlogin)
-		canlogin = intVal(dcanlogin->arg) != 0;
+	{
+		if (intVal(dcanlogin->arg) != 0)
+			attributes |= ROLE_ATTR_CANLOGIN;
+		else
+			attributes &= ~ROLE_ATTR_CANLOGIN;
+	}
 	if (disreplication)
-		isreplication = intVal(disreplication->arg) != 0;
+	{
+		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);
@@ -277,38 +325,12 @@ CreateRole(CreateRoleStmt *stmt)
 		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")));
-	}
+	/* 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)
@@ -364,14 +386,8 @@ CreateRole(CreateRoleStmt *stmt)
 	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_rolattr - 1] = Int64GetDatum(attributes);
+
 	new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
 
 	if (password)
@@ -394,8 +410,6 @@ CreateRole(CreateRoleStmt *stmt)
 	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);
 
 	/*
@@ -508,6 +522,7 @@ AlterRole(AlterRoleStmt *stmt)
 	DefElem    *dvalidUntil = NULL;
 	DefElem    *dbypassRLS = NULL;
 	Oid			roleid;
+	RoleAttr	attributes;
 
 	/* Extract options from the statement node tree */
 	foreach(option, stmt->options)
@@ -658,31 +673,34 @@ AlterRole(AlterRoleStmt *stmt)
 	roleid = HeapTupleGetOid(tuple);
 
 	/*
-	 * To mess with a superuser you gotta be superuser; else you need
-	 * createrole, or just want to change your own password
+	 * 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
 	 */
-	if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper || issuper >= 0)
+
+	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 (((Form_pg_authid) GETSTRUCT(tuple))->rolreplication || isreplication >= 0)
+	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 (((Form_pg_authid) GETSTRUCT(tuple))->rolbypassrls || bypassrls >= 0)
+	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_createrole_privilege())
+	else if (!have_role_attribute(ROLE_ATTR_CREATEROLE))
 	{
 		if (!(inherit < 0 &&
 			  createrole < 0 &&
@@ -743,43 +761,71 @@ AlterRole(AlterRoleStmt *stmt)
 	 */
 	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 (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)
 	{
-		new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit > 0);
-		new_record_repl[Anum_pg_authid_rolinherit - 1] = true;
+		if (inherit > 0)
+			attributes |= ROLE_ATTR_INHERIT;
+		else
+			attributes &= ~ROLE_ATTR_INHERIT;
+		new_record_repl[Anum_pg_authid_rolattr - 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 (createrole > 0)
+			attributes |= ROLE_ATTR_CREATEROLE;
+		else
+			attributes &= ~ROLE_ATTR_CREATEROLE;
+		new_record_repl[Anum_pg_authid_rolattr - 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 (createdb > 0)
+			attributes |= ROLE_ATTR_CREATEDB;
+		else
+			attributes &= ~ROLE_ATTR_CREATEDB;
+		new_record_repl[Anum_pg_authid_rolattr - 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 (canlogin > 0)
+			attributes |= ROLE_ATTR_CANLOGIN;
+		else
+			attributes &= ~ROLE_ATTR_CANLOGIN;
+		new_record_repl[Anum_pg_authid_rolattr - 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 (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);
@@ -815,11 +861,6 @@ AlterRole(AlterRoleStmt *stmt)
 	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);
@@ -867,6 +908,7 @@ AlterRoleSet(AlterRoleSetStmt *stmt)
 	HeapTuple	roletuple;
 	Oid			databaseid = InvalidOid;
 	Oid			roleid = InvalidOid;
+	RoleAttr	attributes;
 
 	if (stmt->role)
 	{
@@ -889,7 +931,8 @@ AlterRoleSet(AlterRoleSetStmt *stmt)
 		 * 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)
+		attributes = ((Form_pg_authid) GETSTRUCT(roletuple))->rolattr;
+		if (attributes & ROLE_ATTR_SUPERUSER)
 		{
 			if (!superuser())
 				ereport(ERROR,
@@ -898,7 +941,7 @@ AlterRoleSet(AlterRoleSetStmt *stmt)
 		}
 		else
 		{
-			if (!have_createrole_privilege() &&
+			if (!have_role_attribute(ROLE_ATTR_CREATEROLE) &&
 				HeapTupleGetOid(roletuple) != GetUserId())
 				ereport(ERROR,
 						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
@@ -951,7 +994,7 @@ DropRole(DropRoleStmt *stmt)
 				pg_auth_members_rel;
 	ListCell   *item;
 
-	if (!have_createrole_privilege())
+	if (!have_role_attribute(ROLE_ATTR_CREATEROLE))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied to drop role")));
@@ -973,6 +1016,7 @@ DropRole(DropRoleStmt *stmt)
 		char	   *detail_log;
 		SysScanDesc sscan;
 		Oid			roleid;
+		RoleAttr	attributes;
 
 		tuple = SearchSysCache1(AUTHNAME, PointerGetDatum(role));
 		if (!HeapTupleIsValid(tuple))
@@ -1013,8 +1057,8 @@ DropRole(DropRoleStmt *stmt)
 		 * 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())
+		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")));
@@ -1128,6 +1172,7 @@ RenameRole(const char *oldname, const char *newname)
 	bool		repl_repl[Natts_pg_authid];
 	int			i;
 	Oid			roleid;
+	RoleAttr	attributes;
 
 	rel = heap_open(AuthIdRelationId, RowExclusiveLock);
 	dsc = RelationGetDescr(rel);
@@ -1173,7 +1218,8 @@ RenameRole(const char *oldname, const char *newname)
 	/*
 	 * createrole is enough privilege unless you want to mess with a superuser
 	 */
-	if (((Form_pg_authid) GETSTRUCT(oldtuple))->rolsuper)
+	attributes = ((Form_pg_authid) GETSTRUCT(oldtuple))->rolattr;
+	if (attributes & ROLE_ATTR_SUPERUSER)
 	{
 		if (!superuser())
 			ereport(ERROR,
@@ -1182,7 +1228,7 @@ RenameRole(const char *oldname, const char *newname)
 	}
 	else
 	{
-		if (!have_createrole_privilege())
+		if (!have_role_attribute(ROLE_ATTR_CREATEROLE))
 			ereport(ERROR,
 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 					 errmsg("permission denied to rename role")));
@@ -1409,7 +1455,7 @@ AddRoleMems(const char *rolename, Oid roleid,
 	}
 	else
 	{
-		if (!have_createrole_privilege() &&
+		if (!have_role_attribute(ROLE_ATTR_CREATEROLE) &&
 			!is_admin_of_role(grantorId, roleid))
 			ereport(ERROR,
 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
@@ -1555,7 +1601,7 @@ DelRoleMems(const char *rolename, Oid roleid,
 	}
 	else
 	{
-		if (!have_createrole_privilege() &&
+		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
index 6ce8dae..491dc38 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -776,6 +776,7 @@ check_session_authorization(char **newval, void **extra, GucSource source)
 	Oid			roleid;
 	bool		is_superuser;
 	role_auth_extra *myextra;
+	RoleAttr	attributes;
 
 	/* Do nothing for the boot_val default of NULL */
 	if (*newval == NULL)
@@ -800,7 +801,8 @@ check_session_authorization(char **newval, void **extra, GucSource source)
 	}
 
 	roleid = HeapTupleGetOid(roleTup);
-	is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
+	attributes = ((Form_pg_authid) GETSTRUCT(roleTup))->rolattr;
+	is_superuser = (attributes & ROLE_ATTR_SUPERUSER);
 
 	ReleaseSysCache(roleTup);
 
@@ -844,6 +846,7 @@ check_role(char **newval, void **extra, GucSource source)
 	Oid			roleid;
 	bool		is_superuser;
 	role_auth_extra *myextra;
+	RoleAttr	attributes;
 
 	if (strcmp(*newval, "none") == 0)
 	{
@@ -872,7 +875,8 @@ check_role(char **newval, void **extra, GucSource source)
 		}
 
 		roleid = HeapTupleGetOid(roleTup);
-		is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper;
+		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
index 1977f09..1a38f56 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -17,18 +17,14 @@
 
 #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 "access/xlog_internal.h"
-
-#include "catalog/pg_type.h"
-
 #include "nodes/makefuncs.h"
-
-#include "mb/pg_wchar.h"
-
+#include "utils/acl.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/inval.h"
@@ -36,11 +32,9 @@
 #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 */
@@ -205,7 +199,7 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count)
 static void
 check_permissions(void)
 {
-	if (!superuser() && !has_rolreplication(GetUserId()))
+	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
index bd4701f..c113a0b 100644
--- a/src/backend/replication/slotfuncs.c
+++ b/src/backend/replication/slotfuncs.c
@@ -20,13 +20,14 @@
 #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 (!superuser() && !has_rolreplication(GetUserId()))
+	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
index 6c232dc..f41ad34 100644
--- a/src/backend/rewrite/rowsecurity.c
+++ b/src/backend/rewrite/rowsecurity.c
@@ -521,7 +521,7 @@ check_enable_rls(Oid relid, Oid checkAsUser)
 	 */
 	if (!checkAsUser && row_security == ROW_SECURITY_OFF)
 	{
-		if (has_bypassrls_privilege(user_id))
+		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
index dc6eb2c..4c03955 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -115,6 +115,7 @@ static Oid	convert_type_name(text *typename);
 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);
 
@@ -4602,6 +4603,186 @@ pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode)
 	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)
@@ -4634,23 +4815,6 @@ RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
 }
 
 
-/* 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
  *
@@ -4697,7 +4861,7 @@ roles_has_privs_of(Oid roleid)
 		int			i;
 
 		/* Ignore non-inheriting roles */
-		if (!has_rolinherit(memberid))
+		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
index 5c75390..ccb1066 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -2308,7 +2308,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 	 * bypassrls right or is the table owner of the table(s) involved which
 	 * have RLS enabled.
 	 */
-	if (!has_bypassrls_privilege(GetUserId()) &&
+	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
index 8fccb4c..db2a0fb 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -40,6 +40,7 @@
 #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"
@@ -329,24 +330,6 @@ SetUserIdAndContext(Oid userid, bool sec_def_context)
 
 
 /*
- * 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
@@ -375,7 +358,7 @@ InitializeSessionUserId(const char *rolename)
 	roleid = HeapTupleGetOid(roleTup);
 
 	AuthenticatedUserId = roleid;
-	AuthenticatedUserIsSuperuser = rform->rolsuper;
+	AuthenticatedUserIsSuperuser = (rform->rolattr & ROLE_ATTR_SUPERUSER);
 
 	/* This sets OuterUserId/CurrentUserId too */
 	SetSessionUserId(roleid, AuthenticatedUserIsSuperuser);
@@ -394,7 +377,7 @@ InitializeSessionUserId(const char *rolename)
 		/*
 		 * Is role allowed to login at all?
 		 */
-		if (!rform->rolcanlogin)
+		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
index c348034..268001f 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -762,7 +762,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 	{
 		Assert(!bootstrap);
 
-		if (!superuser() && !has_rolreplication(GetUserId()))
+		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
index ff0f947..67d070c 100644
--- a/src/backend/utils/misc/superuser.c
+++ b/src/backend/utils/misc/superuser.c
@@ -58,6 +58,7 @@ superuser_arg(Oid roleid)
 {
 	bool		result;
 	HeapTuple	rtup;
+	RoleAttr	attributes;
 
 	/* Quick out for cache hit */
 	if (OidIsValid(last_roleid) && last_roleid == roleid)
@@ -71,7 +72,8 @@ superuser_arg(Oid roleid)
 	rtup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
 	if (HeapTupleIsValid(rtup))
 	{
-		result = ((Form_pg_authid) GETSTRUCT(rtup))->rolsuper;
+		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
index eb633bc..f638167 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -671,10 +671,16 @@ dumpRoles(PGconn *conn)
 	/* note: rolconfig is dumped later */
 	if (server_version >= 90500)
 		printfPQExpBuffer(buf,
-						  "SELECT oid, rolname, rolsuper, rolinherit, "
-						  "rolcreaterole, rolcreatedb, "
-						  "rolcanlogin, rolconnlimit, rolpassword, "
-						  "rolvaliduntil, rolreplication, rolbypassrls, "
+						  "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 0000000..2dcc174
--- /dev/null
+++ b/src/include/catalog/acldefs.h
@@ -0,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
index 3b63d2b..a45f38d 100644
--- a/src/include/catalog/pg_authid.h
+++ b/src/include/catalog/pg_authid.h
@@ -21,6 +21,7 @@
 #ifndef PG_AUTHID_H
 #define PG_AUTHID_H
 
+#include "catalog/acldefs.h"
 #include "catalog/genbki.h"
 
 /*
@@ -45,16 +46,8 @@
 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? */
+	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 */
@@ -74,28 +67,25 @@ typedef FormData_pg_authid *Form_pg_authid;
  *		compiler constants for pg_authid
  * ----------------
  */
-#define Natts_pg_authid					12
+#define Natts_pg_authid					5
 #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
+#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" t t t t t t t t -1 _null_ _null_));
+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
index eace352..b4f93af 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -5136,6 +5136,19 @@ DESCR("rank of hypothetical row without gaps");
 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
index 458eeb0..ecb5780 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -23,8 +23,10 @@
 #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 QuerySource
 {
@@ -51,33 +53,6 @@ typedef enum SortByNulls
 	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
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index a8e3164..4e8d81c 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -30,13 +30,6 @@
 
 
 /*
- * 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
@@ -326,7 +319,10 @@ extern bool pg_foreign_data_wrapper_ownercheck(Oid srv_oid, Oid roleid);
 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);
+
+/* 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
index 2da3002..c8e0e3a 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -106,6 +106,12 @@ extern Datum pg_has_role_id_name(PG_FUNCTION_ARGS);
 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
index 80c3351..d105215 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1314,7 +1314,7 @@ pg_group| SELECT pg_authid.rolname AS groname,
            FROM pg_auth_members
           WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist
    FROM pg_authid
-  WHERE (NOT pg_authid.rolcanlogin);
+  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,
@@ -1405,17 +1405,17 @@ pg_replication_slots| SELECT l.slot_name,
    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_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,
-    pg_authid.rolbypassrls,
     s.setconfig AS rolconfig,
     pg_authid.oid
    FROM (pg_authid
@@ -1608,16 +1608,16 @@ pg_settings| SELECT a.name,
    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_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_authid.rolcanlogin;
+  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