diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml
index d8ca39f..0a3b82e 100644
--- a/doc/src/sgml/ref/grant.sgml
+++ b/doc/src/sgml/ref/grant.sgml
@@ -463,15 +463,17 @@ GRANT <replaceable class="PARAMETER">role_name</replaceable> [, ...] TO <replace
     all objects regardless of object privilege settings.  This
     is comparable to the rights of <literal>root</> in a Unix system.
     As with <literal>root</>, it's unwise to operate as a superuser
-    except when absolutely necessary.
+    except when absolutely necessary. Similarly, database owners can
+    access all objects regardless of object privilege settings, except
+    for objects that are owned by superusers.
    </para>
 
    <para>
-    If a superuser chooses to issue a <command>GRANT</> or <command>REVOKE</>
-    command, the command is performed as though it were issued by the
-    owner of the affected object.  In particular, privileges granted via
-    such a command will appear to have been granted by the object owner.
-    (For role membership, the membership appears to have been granted
+    If a superuser or database owner chooses to issue a <command>GRANT</> or
+    <command>REVOKE</> command, the command is performed as though it were
+    issued by the owner of the affected object.  In particular, privileges
+    granted via such a command will appear to have been granted by the object
+    owner.  (For role membership, the membership appears to have been granted
     by the containing role itself.)
    </para>
 
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index f4df6df..dfd437a 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -3676,11 +3676,22 @@ pg_class_aclmask(Oid table_oid, Oid roleid,
 		return mask;
 	}
 
+	ownerId = classForm->relowner;
+
 	/*
-	 * Normal case: get the relation's ACL from pg_class
+	 * If we have the privs of the database owner AND the owner of the object is
+	 * not a superuser, then we bypass further permission-checking.
 	 */
-	ownerId = classForm->relowner;
+	if (is_databaseowner(roleid) &&
+		!superuser_arg(ownerId))
+	{
+		ReleaseSysCache(tuple);
+		return mask;
+	}
 
+	/*
+	 * Normal case: get the relation's ACL from pg_class
+	 */
 	aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl,
 							   &isNull);
 	if (isNull)
@@ -3797,6 +3808,14 @@ pg_proc_aclmask(Oid proc_oid, Oid roleid,
 
 	ownerId = ((Form_pg_proc) GETSTRUCT(tuple))->proowner;
 
+	/*
+	 * If we have the privs of the database owner AND the owner of the object is
+	 * not a superuser, then we bypass further permission-checking.
+	 */
+	if (is_databaseowner(roleid) &&
+		!superuser_arg(ownerId))
+		return mask;
+
 	aclDatum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proacl,
 							   &isNull);
 	if (isNull)
@@ -4272,6 +4291,14 @@ pg_type_aclmask(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how)
 	 */
 	ownerId = typeForm->typowner;
 
+	/*
+	 * If we have the privs of the database owner AND the owner of the object is
+	 * not a superuser, then we bypass further permission-checking.
+	 */
+	if (is_databaseowner(roleid) &&
+		!superuser_arg(ownerId))
+		return mask;
+
 	aclDatum = SysCacheGetAttr(TYPEOID, tuple,
 							   Anum_pg_type_typacl, &isNull);
 	if (isNull)
@@ -4565,6 +4592,14 @@ pg_class_ownercheck(Oid class_oid, Oid roleid)
 
 	ReleaseSysCache(tuple);
 
+	/*
+	 * If we have the privs of the database owner AND the owner of the object is
+	 * not a superuser, then we bypass further permission-checking.
+	 */
+	if (is_databaseowner(roleid) &&
+		!superuser_arg(ownerId))
+		return true;
+
 	return has_privs_of_role(roleid, ownerId);
 }
 
@@ -4591,6 +4626,14 @@ pg_type_ownercheck(Oid type_oid, Oid roleid)
 
 	ReleaseSysCache(tuple);
 
+	/*
+	 * If we have the privs of the database owner AND the owner of the object is
+	 * not a superuser, then we bypass further permission-checking.
+	 */
+	if (is_databaseowner(roleid) &&
+		!superuser_arg(ownerId))
+		return true;
+
 	return has_privs_of_role(roleid, ownerId);
 }
 
@@ -4643,6 +4686,14 @@ pg_proc_ownercheck(Oid proc_oid, Oid roleid)
 
 	ReleaseSysCache(tuple);
 
+	/*
+	 * If we have the privs of the database owner AND the owner of the object is
+	 * not a superuser, then we bypass further permission-checking.
+	 */
+	if (is_databaseowner(roleid) &&
+		!superuser_arg(ownerId))
+		return true;
+
 	return has_privs_of_role(roleid, ownerId);
 }
 
diff --git a/src/backend/utils/misc/superuser.c b/src/backend/utils/misc/superuser.c
index 175a067..89c36df 100644
--- a/src/backend/utils/misc/superuser.c
+++ b/src/backend/utils/misc/superuser.c
@@ -22,6 +22,7 @@
 
 #include "access/htup_details.h"
 #include "catalog/pg_authid.h"
+#include "catalog/pg_database.h"
 #include "utils/inval.h"
 #include "utils/syscache.h"
 #include "miscadmin.h"
@@ -39,6 +40,11 @@ static bool roleid_callback_registered = false;
 
 static void RoleidCallback(Datum arg, int cacheid, uint32 hashvalue);
 
+static Oid dbowner_roleid = InvalidOid;
+static bool dbowner_callback_registered = false;
+
+static void DBOwnerCallback(Datum arg, int cacheid, uint32 hashvalue);
+
 
 /*
  * The Postgres user running this command has Postgres superuser privileges
@@ -106,3 +112,46 @@ RoleidCallback(Datum arg, int cacheid, uint32 hashvalue)
 	/* Invalidate our local cache in case role's superuserness changed */
 	last_roleid = InvalidOid;
 }
+
+/*
+ * The Postgres user running this command is the owner of current database
+ */
+bool
+is_databaseowner(Oid roleid)
+{
+	HeapTuple	rtup;
+
+	/* Quick out for cache hit */
+	if (OidIsValid(dbowner_roleid))
+		return (dbowner_roleid == roleid);
+
+	/* If first time through, set up callback for cache flushes */
+	if (!dbowner_callback_registered)
+	{
+		CacheRegisterSyscacheCallback(DATABASEOID,
+									  DBOwnerCallback,
+									  (Datum) 0);
+		dbowner_callback_registered = true;
+	}
+
+	/* OK, look up the information in pg_database */
+	rtup = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
+	if (!HeapTupleIsValid(rtup))
+		elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
+
+	dbowner_roleid = ((Form_pg_database) GETSTRUCT(rtup))->datdba;
+	ReleaseSysCache(rtup);
+
+	return (dbowner_roleid == roleid);
+}
+
+/*
+ * DBOwnerCallback
+ *		Syscache inval callback function
+ */
+static void
+DBOwnerCallback(Datum arg, int cacheid, uint32 hashvalue)
+{
+	/* Invalidate our local cache in case database's owner changed */
+	dbowner_roleid = InvalidOid;
+}
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 4c607b2..ebe1839 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -327,6 +327,7 @@ extern void SwitchBackToLocalLatch(void);
 /* in utils/misc/superuser.c */
 extern bool superuser(void);	/* current user is superuser */
 extern bool superuser_arg(Oid roleid);	/* given user is superuser */
+extern bool is_databaseowner(Oid roleid); /* given user is database owner */
 
 
 /*****************************************************************************
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index 00dc7bd..50c5275 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -29,6 +29,7 @@ CREATE USER regress_user3;
 CREATE USER regress_user4;
 CREATE USER regress_user5;
 CREATE USER regress_user5;	-- duplicate
+CREATE USER regress_dbowner;
 
 CREATE GROUP regress_group1;
 CREATE GROUP regress_group2 WITH USER regress_user1, regress_user2;
@@ -39,6 +40,8 @@ ALTER GROUP regress_group2 ADD USER regress_user2;	-- duplicate
 ALTER GROUP regress_group2 DROP USER regress_user2;
 GRANT regress_group2 TO regress_user4 WITH ADMIN OPTION;
 
+select 'ALTER DATABASE '||current_database()||' OWNER TO regress_dbowner'  \gexec
+
 -- test owner privileges
 
 SET SESSION AUTHORIZATION regress_user1;
@@ -126,6 +129,21 @@ bar	true
 \.
 SELECT * FROM atest1; -- ok
 
+--dbowner
+SET SESSION AUTHORIZATION regress_dbowner;
+CREATE TABLE atest3 ( a int, b text );
+CREATE INDEX ON atest1 (a);
+INSERT INTO atest1 VALUES (11, 'one');
+SELECT * FROM atest1 WHERE a = 11;
+DELETE FROM atest1 WHERE a = 11;
+UPDATE atest1 SET b = 'blech' WHERE a = 11;
+INSERT INTO atest3 SELECT * FROM atest1 WHERE a <> 11;
+TRUNCATE atest1;
+INSERT INTO atest1 SELECT * FROM atest3;
+DROP TABLE atest3;
+BEGIN;
+LOCK atest1 IN ACCESS EXCLUSIVE MODE;
+COMMIT;
 
 -- groups
 
