diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 725bacee5dd..d6b60db0744 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -178,6 +178,11 @@
table inheritance hierarchy
+
+ pg_init_privs
+ object initial privileges
+
+
pg_languagelanguages for writing functions
@@ -3785,6 +3790,109 @@
+
+ pg_init_privs
+
+
+ pg_init_privs
+
+
+
+ The catalog pg_init_privs> records information about
+ the initial privileges of objects in the system. There is one entry
+ for each object in the database which has a non-default (non-NULL)
+ initial set of privileges.
+
+
+
+ Objects can have initial privileges either by having those privileges set
+ when the system is initialized (by initdb>) or when the
+ object is created during a CREATE EXTENSION and the
+ extension script sets initial privileges using the GRANT
+ system. Note that the system will automatically handle recording of the
+ privileges during the extension script and that extension authors need
+ only use the GRANT and REVOKE
+ statements in their script to have the privileges recorded. The
+ privtype column indicates if the initial privilege was
+ set by initdb> or during a
+ CREATE EXTENSION command.
+
+
+
+ Objects which have initial privileges set by initdb> will
+ have entries where privtype is
+ 'i', while objects which have initial privileges set
+ by CREATE EXTENSION will have entries where
+ privtype is 'e'.
+
+
+
+ pg_inherits> Columns
+
+
+
+
+ Name
+ Type
+ References
+ Description
+
+
+
+
+
+ objoid
+ oid
+ any OID column
+ The OID of the specific object
+
+
+
+ classoid
+ oid
+ pg_class.oid
+ The OID of the system catalog the object is in
+
+
+
+ objsubid
+ int4
+
+
+ For a table column, this is the column number (the
+ objoid> and classoid> refer to the
+ table itself). For all other object types, this column is
+ zero.
+
+
+
+
+ privtype
+ char
+
+
+ A code defining the type of initial privilege of this object; see text
+
+
+
+
+ initprivs
+ aclitem[]
+
+
+ The initial access privileges; see
+ and
+
+ for details
+
+
+
+
+
+
+
+
+
pg_language
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 25130ecf124..1ce76100497 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -40,7 +40,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_ts_parser.h pg_ts_template.h pg_extension.h \
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
pg_foreign_table.h pg_policy.h pg_replication_origin.h \
- pg_default_acl.h pg_seclabel.h pg_shseclabel.h \
+ pg_default_acl.h pg_init_privs.h pg_seclabel.h pg_shseclabel.h \
pg_collation.h pg_range.h pg_transform.h \
toasting.h indexing.h \
)
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 0f3bc074e1f..ffb6678c6a4 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -35,6 +35,7 @@
#include "catalog/pg_extension.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
+#include "catalog/pg_init_privs.h"
#include "catalog/pg_language.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_largeobject_metadata.h"
@@ -49,6 +50,7 @@
#include "catalog/pg_ts_dict.h"
#include "commands/dbcommands.h"
#include "commands/event_trigger.h"
+#include "commands/extension.h"
#include "commands/proclang.h"
#include "commands/tablespace.h"
#include "foreign/foreign.h"
@@ -119,6 +121,8 @@ static AclMode restrict_and_check_grant(bool is_grant, AclMode avail_goptions,
AttrNumber att_number, const char *colname);
static AclMode pg_aclmask(AclObjectKind objkind, Oid table_oid, AttrNumber attnum,
Oid roleid, AclMode mask, AclMaskHow how);
+static void recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid,
+ Acl *new_acl);
#ifdef ACLDEBUG
@@ -1678,6 +1682,10 @@ ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname,
/* keep the catalog indexes up to date */
CatalogUpdateIndexes(attRelation, newtuple);
+ /* Update initial privileges for extensions */
+ recordExtensionInitPriv(relOid, RelationRelationId, attnum,
+ ACL_NUM(new_acl) > 0 ? new_acl : NULL);
+
/* Update the shared dependency ACL info */
updateAclDependencies(RelationRelationId, relOid, attnum,
ownerId,
@@ -1939,6 +1947,9 @@ ExecGrant_Relation(InternalGrant *istmt)
/* keep the catalog indexes up to date */
CatalogUpdateIndexes(relation, newtuple);
+ /* Update initial privileges for extensions */
+ recordExtensionInitPriv(relOid, RelationRelationId, 0, new_acl);
+
/* Update the shared dependency ACL info */
updateAclDependencies(RelationRelationId, relOid, 0,
ownerId,
@@ -2254,6 +2265,10 @@ ExecGrant_Fdw(InternalGrant *istmt)
/* keep the catalog indexes up to date */
CatalogUpdateIndexes(relation, newtuple);
+ /* Update initial privileges for extensions */
+ recordExtensionInitPriv(fdwid, ForeignDataWrapperRelationId, 0,
+ new_acl);
+
/* Update the shared dependency ACL info */
updateAclDependencies(ForeignDataWrapperRelationId,
HeapTupleGetOid(tuple), 0,
@@ -2379,6 +2394,9 @@ ExecGrant_ForeignServer(InternalGrant *istmt)
/* keep the catalog indexes up to date */
CatalogUpdateIndexes(relation, newtuple);
+ /* Update initial privileges for extensions */
+ recordExtensionInitPriv(srvid, ForeignServerRelationId, 0, new_acl);
+
/* Update the shared dependency ACL info */
updateAclDependencies(ForeignServerRelationId,
HeapTupleGetOid(tuple), 0,
@@ -2503,6 +2521,9 @@ ExecGrant_Function(InternalGrant *istmt)
/* keep the catalog indexes up to date */
CatalogUpdateIndexes(relation, newtuple);
+ /* Update initial privileges for extensions */
+ recordExtensionInitPriv(funcId, ProcedureRelationId, 0, new_acl);
+
/* Update the shared dependency ACL info */
updateAclDependencies(ProcedureRelationId, funcId, 0,
ownerId,
@@ -2633,6 +2654,9 @@ ExecGrant_Language(InternalGrant *istmt)
/* keep the catalog indexes up to date */
CatalogUpdateIndexes(relation, newtuple);
+ /* Update initial privileges for extensions */
+ recordExtensionInitPriv(langId, LanguageRelationId, 0, new_acl);
+
/* Update the shared dependency ACL info */
updateAclDependencies(LanguageRelationId, HeapTupleGetOid(tuple), 0,
ownerId,
@@ -2772,6 +2796,9 @@ ExecGrant_Largeobject(InternalGrant *istmt)
/* keep the catalog indexes up to date */
CatalogUpdateIndexes(relation, newtuple);
+ /* Update initial privileges for extensions */
+ recordExtensionInitPriv(loid, LargeObjectRelationId, 0, new_acl);
+
/* Update the shared dependency ACL info */
updateAclDependencies(LargeObjectRelationId,
HeapTupleGetOid(tuple), 0,
@@ -2897,6 +2924,9 @@ ExecGrant_Namespace(InternalGrant *istmt)
/* keep the catalog indexes up to date */
CatalogUpdateIndexes(relation, newtuple);
+ /* Update initial privileges for extensions */
+ recordExtensionInitPriv(nspid, NamespaceRelationId, 0, new_acl);
+
/* Update the shared dependency ACL info */
updateAclDependencies(NamespaceRelationId, HeapTupleGetOid(tuple), 0,
ownerId,
@@ -3158,6 +3188,9 @@ ExecGrant_Type(InternalGrant *istmt)
/* keep the catalog indexes up to date */
CatalogUpdateIndexes(relation, newtuple);
+ /* Update initial privileges for extensions */
+ recordExtensionInitPriv(typId, TypeRelationId, 0, new_acl);
+
/* Update the shared dependency ACL info */
updateAclDependencies(TypeRelationId, typId, 0,
ownerId,
@@ -5174,3 +5207,119 @@ get_user_default_acl(GrantObjectType objtype, Oid ownerId, Oid nsp_oid)
return result;
}
+
+/*
+ * Record initial ACL for an extension object
+ *
+ * This will perform a wholesale replacement of the entire ACL for the object
+ * passed in, therefore be sure to pass in the complete new ACL to use.
+ *
+ * Can be called at any time, we check if 'creating_extension' is set and, if
+ * not, exit immediately.
+ *
+ * Pass in the object OID, the OID of the class (the OID of the table which
+ * the object is defined in) and the 'sub' id of the object (objsubid), if
+ * any. If there is no 'sub' id (they are currently only used for columns of
+ * tables) then pass in '0'. Finally, pass in the complete ACL to store.
+ *
+ * If an ACL already exists for this object/sub-object then we will replace
+ * it with what is passed in.
+ *
+ * Passing in NULL for 'new_acl' will result in the entry for the object being
+ * removed, if one is found.
+ */
+static void
+recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid, Acl *new_acl)
+{
+ Relation relation;
+ ScanKeyData key[3];
+ SysScanDesc scan;
+ HeapTuple tuple;
+ HeapTuple oldtuple;
+
+ if (!creating_extension)
+ return;
+
+ relation = heap_open(InitPrivsRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_init_privs_objoid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(objoid));
+ ScanKeyInit(&key[1],
+ Anum_pg_init_privs_classoid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(classoid));
+ ScanKeyInit(&key[2],
+ Anum_pg_init_privs_objsubid,
+ BTEqualStrategyNumber, F_INT4EQ,
+ Int32GetDatum(objsubid));
+
+ scan = systable_beginscan(relation, InitPrivsObjIndexId, true,
+ NULL, 3, key);
+
+ /* There should exist only one entry or none. */
+ oldtuple = systable_getnext(scan);
+
+ systable_endscan(scan);
+
+ /* If we find an entry, update it with the latest ACL. */
+ if (HeapTupleIsValid(oldtuple))
+ {
+ Datum values[Natts_pg_init_privs];
+ bool nulls[Natts_pg_init_privs];
+ bool replace[Natts_pg_init_privs];
+
+ /* If we have a new ACL to set, then update the row with it. */
+ if (new_acl)
+ {
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ MemSet(replace, false, sizeof(replace));
+
+ values[Anum_pg_init_privs_privs - 1] = PointerGetDatum(new_acl);
+ replace[Anum_pg_init_privs_privs - 1] = true;
+
+ oldtuple = heap_modify_tuple(oldtuple, RelationGetDescr(relation),
+ values, nulls, replace);
+
+ simple_heap_update(relation, &oldtuple->t_self, oldtuple);
+
+ /* keep the catalog indexes up to date */
+ CatalogUpdateIndexes(relation, oldtuple);
+ }
+ else
+ /* new_acl is NULL, so delete the entry we found. */
+ simple_heap_delete(relation, &oldtuple->t_self);
+ }
+ else
+ {
+ /* No entry found, so add it. */
+ Datum values[Natts_pg_init_privs];
+ bool nulls[Natts_pg_init_privs];
+
+ MemSet(nulls, false, sizeof(nulls));
+
+ values[Anum_pg_init_privs_objoid - 1] = ObjectIdGetDatum(objoid);
+ values[Anum_pg_init_privs_classoid - 1] = ObjectIdGetDatum(classoid);
+ values[Anum_pg_init_privs_objsubid - 1] = Int32GetDatum(objsubid);
+
+ /* This function only handles initial privileges of extensions */
+ values[Anum_pg_init_privs_privtype - 1] =
+ CharGetDatum(INITPRIVS_EXTENSION);
+
+ values[Anum_pg_init_privs_privs - 1] = PointerGetDatum(new_acl);
+
+ tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
+
+ simple_heap_insert(relation, tuple);
+
+ /* keep the catalog indexes up to date */
+ CatalogUpdateIndexes(relation, tuple);
+ }
+
+ /* prevent error when processing objects multiple times */
+ CommandCounterIncrement();
+
+ heap_close(relation, RowExclusiveLock);
+}
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 79595a9d230..a6180a64f21 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -39,6 +39,7 @@
#include "catalog/pg_extension.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
+#include "catalog/pg_init_privs.h"
#include "catalog/pg_language.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_namespace.h"
@@ -196,6 +197,7 @@ static bool object_address_present_add_flags(const ObjectAddress *object,
static bool stack_address_present_add_flags(const ObjectAddress *object,
int flags,
ObjectAddressStack *stack);
+static void DeleteInitPrivs(const ObjectAddress *object);
/*
@@ -1106,12 +1108,13 @@ deleteOneObject(const ObjectAddress *object, Relation *depRel, int flags)
/*
- * Delete any comments or security labels associated with this object.
- * (This is a convenient place to do these things, rather than having
- * every object type know to do it.)
+ * Delete any comments, security labels, or initial privileges associated
+ * with this object. (This is a convenient place to do these things,
+ * rather than having every object type know to do it.)
*/
DeleteComments(object->objectId, object->classId, object->objectSubId);
DeleteSecurityLabel(object);
+ DeleteInitPrivs(object);
/*
* CommandCounterIncrement here to ensure that preceding changes are all
@@ -2432,3 +2435,40 @@ getObjectClass(const ObjectAddress *object)
elog(ERROR, "unrecognized object class: %u", object->classId);
return OCLASS_CLASS; /* keep compiler quiet */
}
+
+/*
+ * delete initial ACL for extension objects
+ */
+static void
+DeleteInitPrivs(const ObjectAddress *object)
+{
+ Relation relation;
+ ScanKeyData key[3];
+ SysScanDesc scan;
+ HeapTuple oldtuple;
+
+ relation = heap_open(InitPrivsRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_init_privs_objoid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->objectId));
+ ScanKeyInit(&key[1],
+ Anum_pg_init_privs_classoid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(object->classId));
+ ScanKeyInit(&key[2],
+ Anum_pg_init_privs_objsubid,
+ BTEqualStrategyNumber, F_INT4EQ,
+ Int32GetDatum(object->objectSubId));
+
+ scan = systable_beginscan(relation, InitPrivsObjIndexId, true,
+ NULL, 3, key);
+
+ while (HeapTupleIsValid(oldtuple = systable_getnext(scan)))
+ simple_heap_delete(relation, &oldtuple->t_self);
+
+ systable_endscan(scan);
+
+ heap_close(relation, RowExclusiveLock);
+}
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index ed3ba7b6248..858667b3941 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -1989,6 +1989,11 @@ setup_dictionary(FILE *cmdfd)
* Some objects may require different permissions by default, so we
* make sure we don't overwrite privilege sets that have already been
* set (NOT NULL).
+ *
+ * Also populate pg_init_privs to save what the privileges are at init
+ * time. This is used by pg_dump to allow users to change privileges
+ * on catalog objects and to have those privilege changes preserved
+ * across dump/reload and pg_upgrade.
*/
static void
setup_privileges(FILE *cmdfd)
@@ -2002,6 +2007,144 @@ setup_privileges(FILE *cmdfd)
"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n",
"GRANT CREATE, USAGE ON SCHEMA public TO PUBLIC;\n\n",
"REVOKE ALL ON pg_largeobject FROM PUBLIC;\n\n",
+ "INSERT INTO pg_init_privs "
+ " (objoid, classoid, objsubid, initprivs, privtype)"
+ " SELECT"
+ " oid,"
+ " (SELECT oid FROM pg_class WHERE relname = 'pg_class'),"
+ " 0,"
+ " relacl,"
+ " 'i'"
+ " FROM"
+ " pg_class"
+ " WHERE"
+ " relacl IS NOT NULL"
+ " AND relkind IN ('r', 'v', 'm', 'S');",
+ "INSERT INTO pg_init_privs "
+ " (objoid, classoid, objsubid, initprivs, privtype)"
+ " SELECT"
+ " pg_class.oid,"
+ " (SELECT oid FROM pg_class WHERE relname = 'pg_class'),"
+ " pg_attribute.attnum,"
+ " pg_attribute.attacl,"
+ " 'i'"
+ " FROM"
+ " pg_class"
+ " JOIN pg_attribute ON (pg_class.oid = pg_attribute.attrelid)"
+ " WHERE"
+ " pg_attribute.attacl IS NOT NULL"
+ " AND pg_class.relkind IN ('r', 'v', 'm', 'S');",
+ "INSERT INTO pg_init_privs "
+ " (objoid, classoid, objsubid, initprivs, privtype)"
+ " SELECT"
+ " oid,"
+ " (SELECT oid FROM pg_class WHERE relname = 'pg_proc'),"
+ " 0,"
+ " proacl,"
+ " 'i'"
+ " FROM"
+ " pg_proc"
+ " WHERE"
+ " proacl IS NOT NULL;",
+ "INSERT INTO pg_init_privs "
+ " (objoid, classoid, objsubid, initprivs, privtype)"
+ " SELECT"
+ " oid,"
+ " (SELECT oid FROM pg_class WHERE relname = 'pg_type'),"
+ " 0,"
+ " typacl,"
+ " 'i'"
+ " FROM"
+ " pg_type"
+ " WHERE"
+ " typacl IS NOT NULL;",
+ "INSERT INTO pg_init_privs "
+ " (objoid, classoid, objsubid, initprivs, privtype)"
+ " SELECT"
+ " oid,"
+ " (SELECT oid FROM pg_class WHERE relname = 'pg_language'),"
+ " 0,"
+ " lanacl,"
+ " 'i'"
+ " FROM"
+ " pg_language"
+ " WHERE"
+ " lanacl IS NOT NULL;",
+ "INSERT INTO pg_init_privs "
+ " (objoid, classoid, objsubid, initprivs, privtype)"
+ " SELECT"
+ " oid,"
+ " (SELECT oid FROM pg_class WHERE "
+ " relname = 'pg_largeobject_metadata'),"
+ " 0,"
+ " lomacl,"
+ " 'i'"
+ " FROM"
+ " pg_largeobject_metadata"
+ " WHERE"
+ " lomacl IS NOT NULL;",
+ "INSERT INTO pg_init_privs "
+ " (objoid, classoid, objsubid, initprivs, privtype)"
+ " SELECT"
+ " oid,"
+ " (SELECT oid FROM pg_class WHERE relname = 'pg_namespace'),"
+ " 0,"
+ " nspacl,"
+ " 'i'"
+ " FROM"
+ " pg_namespace"
+ " WHERE"
+ " nspacl IS NOT NULL;",
+ "INSERT INTO pg_init_privs "
+ " (objoid, classoid, objsubid, initprivs, privtype)"
+ " SELECT"
+ " oid,"
+ " (SELECT oid FROM pg_class WHERE relname = 'pg_database'),"
+ " 0,"
+ " datacl,"
+ " 'i'"
+ " FROM"
+ " pg_database"
+ " WHERE"
+ " datacl IS NOT NULL;",
+ "INSERT INTO pg_init_privs "
+ " (objoid, classoid, objsubid, initprivs, privtype)"
+ " SELECT"
+ " oid,"
+ " (SELECT oid FROM pg_class WHERE relname = 'pg_tablespace'),"
+ " 0,"
+ " spcacl,"
+ " 'i'"
+ " FROM"
+ " pg_tablespace"
+ " WHERE"
+ " spcacl IS NOT NULL;",
+ "INSERT INTO pg_init_privs "
+ " (objoid, classoid, objsubid, initprivs, privtype)"
+ " SELECT"
+ " oid,"
+ " (SELECT oid FROM pg_class WHERE "
+ " relname = 'pg_foreign_data_wrapper'),"
+ " 0,"
+ " fdwacl,"
+ " 'i'"
+ " FROM"
+ " pg_foreign_data_wrapper"
+ " WHERE"
+ " fdwacl IS NOT NULL;",
+ "INSERT INTO pg_init_privs "
+ " (objoid, classoid, objsubid, initprivs, privtype)"
+ " SELECT"
+ " oid,"
+ " (SELECT oid FROM pg_class "
+ " WHERE relname = 'pg_foreign_server'),"
+ " 0,"
+ " srvacl,"
+ " 'i'"
+ " FROM"
+ " pg_foreign_server"
+ " WHERE"
+ " srvacl IS NOT NULL;",
NULL
};
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index ab2c1a8e68c..ca5eb3d4178 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -162,6 +162,9 @@ DECLARE_UNIQUE_INDEX(pg_inherits_relid_seqno_index, 2680, on pg_inherits using b
DECLARE_INDEX(pg_inherits_parent_index, 2187, on pg_inherits using btree(inhparent oid_ops));
#define InheritsParentIndexId 2187
+DECLARE_UNIQUE_INDEX(pg_init_privs_o_c_o_index, 3395, on pg_init_privs using btree(objoid oid_ops, classoid oid_ops, objsubid int4_ops));
+#define InitPrivsObjIndexId 3395
+
DECLARE_UNIQUE_INDEX(pg_language_name_index, 2681, on pg_language using btree(lanname name_ops));
#define LanguageNameIndexId 2681
DECLARE_UNIQUE_INDEX(pg_language_oid_index, 2682, on pg_language using btree(oid oid_ops));
diff --git a/src/include/catalog/pg_init_privs.h b/src/include/catalog/pg_init_privs.h
new file mode 100644
index 00000000000..e13edd3ff51
--- /dev/null
+++ b/src/include/catalog/pg_init_privs.h
@@ -0,0 +1,101 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_init_privs.h
+ * definition of the system "initial privileges" relation (pg_init_privs)
+ *
+ * NOTE: an object is identified by the OID of the row that primarily
+ * defines the object, plus the OID of the table that that row appears in.
+ * For example, a function is identified by the OID of its pg_proc row
+ * plus the pg_class OID of table pg_proc. This allows unique identification
+ * of objects without assuming that OIDs are unique across tables.
+ *
+ * Since attributes don't have OIDs of their own, we identify an attribute
+ * privilege by the objoid+classoid of its parent table, plus an "objsubid"
+ * giving the attribute column number. "objsubid" must be zero in a privilege
+ * for a table itself, so that it is distinct from any column privilege.
+ * Currently, objsubid is unused and zero for all other kinds of objects.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_init_privs.h
+ *
+ * NOTES
+ * the genbki.pl script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ * XXX do NOT break up DATA() statements into multiple lines!
+ * the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_INIT_PRIVS_H
+#define PG_INIT_PRIVS_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ * pg_init_privs definition. cpp turns this into
+ * typedef struct FormData_pg_init_privs
+ * ----------------
+ */
+#define InitPrivsRelationId 3394
+
+CATALOG(pg_init_privs,3394) BKI_WITHOUT_OIDS
+{
+ Oid objoid; /* OID of object itself */
+ Oid classoid; /* OID of table containing object */
+ int32 objsubid; /* column number, or 0 if not used */
+ char privtype; /* from initdb or extension? */
+
+#ifdef CATALOG_VARLEN /* variable-length fields start here */
+ aclitem initprivs[1] BKI_FORCE_NOT_NULL; /* initial privs on
+ * object */
+#endif
+} FormData_pg_init_privs;
+
+/* ----------------
+ * Form_pg_init_privs corresponds to a pointer to a tuple with
+ * the format of pg_init_privs relation.
+ * ----------------
+ */
+typedef FormData_pg_init_privs *Form_pg_init_privs;
+
+/* ----------------
+ * compiler constants for pg_init_privs
+ * ----------------
+ */
+#define Natts_pg_init_privs 5
+#define Anum_pg_init_privs_objoid 1
+#define Anum_pg_init_privs_classoid 2
+#define Anum_pg_init_privs_objsubid 3
+#define Anum_pg_init_privs_privtype 4
+#define Anum_pg_init_privs_privs 5
+
+/*
+ * It is important to know if the initial privileges are from initdb or from an
+ * extension. This enum is used to provide that differentiation and the two
+ * places which populate this table (initdb and during CREATE EXTENSION, see
+ * recordExtensionInitPriv()) know to use the correct values.
+ */
+
+typedef enum InitPrivsType
+{
+ INITPRIVS_INITDB = 'i',
+ INITPRIVS_EXTENSION = 'e'
+} InitPrivsType;
+
+/* ----------------
+ * initial contents of pg_init_privs
+ * ----------------
+ */
+
+/*
+ * Because the contents of this table depend on what is done with the other
+ * objects in the system (and, in particular, may change due to changes is
+ * system_views.sql), there is no initialization here.
+ *
+ * The initial contents are loaded near the end of initdb.
+ */
+
+#endif /* PG_INIT_PRIVS_H */
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 2c5be4bae4b..4d81ba7dac2 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -110,6 +110,7 @@ pg_foreign_server|t
pg_foreign_table|t
pg_index|t
pg_inherits|t
+pg_init_privs|t
pg_language|t
pg_largeobject|t
pg_largeobject_metadata|t