[v9.2] Object access hooks with arguments support (v1)

Started by Kohei KaiGaiover 14 years ago20 messages
#1Kohei KaiGai
kaigai@kaigai.gr.jp
1 attachment(s)

The attached patch is a draft to support arguments in addition to
OAT_* enum and object identifiers.

The existing object_access_hook enables loadable modules to acquire
control when objects are referenced. The first guest of this hook is
contrib/sepgsql for assignment of default security label on newly
created objects. Right now, OAT_POST_CREATE is the all supported
object access type. However, we plan to enhance this mechanism onto
other widespread purpose; such as comprehensive DDL permissions
supported by loadable modules.

This patch is a groundwork to utilize this hook for object creation
permission checks, not only default labeling.
At the v9.1 development cycle, I proposed an idea that defines both
OAT_CREATE hook prior to system catalog modification and
OAT_POST_CREATE hook as currently we have. This design enables to
check permission next to the existing pg_xxx_aclcheck() or
pg_xxx_ownercheck(), and raise an error before system catalog updates.
However, it was painful to deliver private datum set on OAT_CREATE to
the OAT_POST_CREATE due to the code complexity.

The other idea tried to do all the necessary things in OAT_POST_CREATE
hook, and it had been merged, because loadable modules can pull
properties of the new object from system catalogs by the supplied
object identifiers. Thus, contrib/sepgsql assigns a default security
label on new object using OAT_POST_CREATE hook.
However, I have two concern on the existing hook to implement
permission check for object creation. The first one is the entry of
system catalog is not visible using SnaphotNow, so we had to open and
scan system catalog again, instead of syscache mechanism. The second
one is more significant. A part of information to make access control
decision is not available within contents of the system catalog
entries. For example, we hope to skip permission check when
heap_create_with_catalog() was launched by make_new_heap() because the
new relation is just used to swap later.

Thus, I'd like to propose to support argument of object_access_hook to
inform the loadable modules additional contextual information on its
invocations; to solve these concerns.

Regarding to the first concern, fortunately, most of existing
OAT_POST_CREATE hook is deployed just after insert or update of system
catalogs, but before CCI. So, it is helpful for the loadable modules
to deliver Form_pg_xxxx data to reference properties of the new
object, instead of open and scan the catalog again.
In the draft patch, I enhanced OAT_POST_CREATE hook commonly to take
an argument that points to the Form_pg_xxxx data of the new object.

Regarding to the second concern, I added a few contextual information
as second or later arguments in a part of object classes. Right now, I
hope the following contextual information shall be provided to
OAT_POST_CREATE hook to implement permission checks of object
creation.

* pg_class - TupleDesc structure of the new relation
I want to reference of pg_attribute, not only pg_class.

* pg_class - A flag to show whether the relation is defined for
rebuilding, or not.
I want not to apply permission check in the case when it is invoked
from make_new_heap(), because it just create a dummy table as a part
of internal process. All the necessary permission checks should be
done at ALTER TABLE or CLUSTER.

* pg_class - A flag to show whether the relation is created with
SELECT INTO, or not.
I want to check "insert" permission of the new table, created by
SELECT INTO, because DML hook is not available to check this case.

* pg_type - A flag to show whether the type is defined as implicit
array, or not.
I want not to apply permission check on creation of implicit array type.

* pg_database - Oid of the source (template) database.
I want to fetch security label of the source database to compute a
default label of the new database.

* pg_trigger - A flag to show whether the trigger is used to FK
constraint, or not.
I want not to apply permission check on creation of FK constraint. It
should be done in ALTER TABLE level.

Sorry for this long explanation. Right now, I tend to consider it is
the best way to implement permission checks on object creation with
least invasive way.

Thanks, Any comments welcome.
--
KaiGai Kohei <kaigai@kaigai.gr.jp>

Attachments:

pgsql-v9.2-object-access-hooks-argument.v1.patchapplication/octet-stream; name=pgsql-v9.2-object-access-hooks-argument.v1.patchDownload
 contrib/sepgsql/hooks.c                    |    8 +-
 src/backend/bootstrap/bootparse.y          |    4 +-
 src/backend/catalog/heap.c                 |   30 +++--
 src/backend/catalog/index.c                |    4 +-
 src/backend/catalog/pg_collation.c         |    5 +-
 src/backend/catalog/pg_constraint.c        |    4 +-
 src/backend/catalog/pg_conversion.c        |    5 +-
 src/backend/catalog/pg_largeobject.c       |    5 +
 src/backend/catalog/pg_namespace.c         |    4 +-
 src/backend/catalog/pg_operator.c          |   13 +-
 src/backend/catalog/pg_proc.c              |    7 +-
 src/backend/catalog/pg_type.c              |   11 +-
 src/backend/catalog/toasting.c             |    4 +-
 src/backend/commands/cluster.c             |    4 +-
 src/backend/commands/dbcommands.c          |    5 +-
 src/backend/commands/extension.c           |    7 +-
 src/backend/commands/foreigncmds.c         |   22 ++--
 src/backend/commands/functioncmds.c        |    4 +-
 src/backend/commands/opclasscmds.c         |   16 +-
 src/backend/commands/proclang.c            |    5 +-
 src/backend/commands/tablecmds.c           |   13 +-
 src/backend/commands/tablespace.c          |    9 +-
 src/backend/commands/trigger.c             |   31 +++--
 src/backend/commands/tsearchcmds.c         |   17 ++-
 src/backend/commands/user.c                |    4 +-
 src/backend/executor/execMain.c            |    4 +-
 src/backend/rewrite/rewriteDefine.c        |    8 +-
 src/backend/storage/large_object/inv_api.c |    5 -
 src/include/catalog/heap.h                 |    6 +-
 src/include/catalog/objectaccess.h         |  214 +++++++++++++++++++++++++++-
 30 files changed, 374 insertions(+), 104 deletions(-)

diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 7797ccb..6e5c2d4 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -115,10 +115,14 @@ static void
 sepgsql_object_access(ObjectAccessType access,
 					  Oid classId,
 					  Oid objectId,
-					  int subId)
+					  int subId,
+					  int nargs,
+					  Datum argv[])
 {
 	if (next_object_access_hook)
-		(*next_object_access_hook) (access, classId, objectId, subId);
+		(*next_object_access_hook) (access,
+									classId, objectId, subId,
+									nargs, argv);
 
 	switch (access)
 	{
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index d0a0e92..a303454 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -248,7 +248,9 @@ Boot_CreateStmt:
 													  ONCOMMIT_NOOP,
 													  (Datum) 0,
 													  false,
-													  true);
+													  true,
+													  false,
+													  false);
 						elog(DEBUG4, "relation created with OID %u", id);
 					}
 					do_end();
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 7ec6581..3b16126 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -80,7 +80,7 @@
 Oid			binary_upgrade_next_heap_pg_class_oid = InvalidOid;
 Oid			binary_upgrade_next_toast_pg_class_oid = InvalidOid;
 
-static void AddNewRelationTuple(Relation pg_class_desc,
+static HeapTuple AddNewRelationTuple(Relation pg_class_desc,
 					Relation new_rel_desc,
 					Oid new_rel_oid,
 					Oid new_type_oid,
@@ -752,7 +752,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
  * to NULL.
  * --------------------------------
  */
-void
+HeapTuple
 InsertPgClassTuple(Relation pg_class_desc,
 				   Relation new_rel_desc,
 				   Oid new_rel_oid,
@@ -814,7 +814,7 @@ InsertPgClassTuple(Relation pg_class_desc,
 
 	CatalogUpdateIndexes(pg_class_desc, tup);
 
-	heap_freetuple(tup);
+	return tup;	/* to be freed in the caller side */
 }
 
 /* --------------------------------
@@ -824,7 +824,7 @@ InsertPgClassTuple(Relation pg_class_desc,
  *		adding a tuple to pg_class.
  * --------------------------------
  */
-static void
+static HeapTuple
 AddNewRelationTuple(Relation pg_class_desc,
 					Relation new_rel_desc,
 					Oid new_rel_oid,
@@ -894,8 +894,8 @@ AddNewRelationTuple(Relation pg_class_desc,
 	new_rel_desc->rd_att->tdtypeid = new_type_oid;
 
 	/* Now build and insert the tuple */
-	InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid,
-					   relacl, reloptions);
+	return InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid,
+							  relacl, reloptions);
 }
 
 
@@ -972,6 +972,9 @@ AddNewRelationType(const char *typeName,
  *	use_user_acl: TRUE if should look for user-defined default permissions;
  *		if FALSE, relacl is always set NULL
  *	allow_system_table_mods: TRUE to allow creation in system namespaces
+ *	is_rebuilt_relation : TRUE if the relation is defined to rebuild
+ *		a particular existing relation.
+ *	is_select_into : TRUE if the relation is created by SELECT INTO.
  *
  * Returns the OID of the new relation
  * --------------------------------
@@ -995,10 +998,13 @@ heap_create_with_catalog(const char *relname,
 						 OnCommitAction oncommit,
 						 Datum reloptions,
 						 bool use_user_acl,
-						 bool allow_system_table_mods)
+						 bool allow_system_table_mods,
+						 bool is_rebuilt_relation,
+						 bool is_select_into)
 {
 	Relation	pg_class_desc;
 	Relation	new_rel_desc;
+	HeapTuple	new_rel_tup;
 	Acl		   *relacl;
 	Oid			existing_relid;
 	Oid			old_type_oid;
@@ -1209,7 +1215,7 @@ heap_create_with_catalog(const char *relname,
 	 * creating the same relation name in parallel but hadn't committed yet
 	 * when we checked for a duplicate name above.
 	 */
-	AddNewRelationTuple(pg_class_desc,
+	new_rel_tup = AddNewRelationTuple(pg_class_desc,
 						new_rel_desc,
 						relid,
 						new_type_oid,
@@ -1281,7 +1287,12 @@ heap_create_with_catalog(const char *relname,
 	}
 
 	/* Post creation hook for new relation */
-	InvokeObjectAccessHook(OAT_POST_CREATE, RelationRelationId, relid, 0);
+	InvokeObjectAccessHook4(OAT_POST_CREATE,
+							RelationRelationId, relid, 0,
+							PointerGetDatum(GETSTRUCT(new_rel_tup)),
+							PointerGetDatum(tupdesc),
+							BoolGetDatum(is_rebuilt_relation),
+							BoolGetDatum(is_select_into));
 
 	/*
 	 * Store any supplied constraints and defaults.
@@ -1322,6 +1333,7 @@ heap_create_with_catalog(const char *relname,
 	 * ok, the relation has been cataloged, so close our relations and return
 	 * the OID of the newly created relation.
 	 */
+	heap_freetuple(new_rel_tup);
 	heap_close(new_rel_desc, NoLock);	/* do not unlock till end of xact */
 	heap_close(pg_class_desc, RowExclusiveLock);
 
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 75b4c14..3a0dd67 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -697,6 +697,7 @@ index_create(Relation heapRelation,
 	Relation	pg_class;
 	Relation	indexRelation;
 	TupleDesc	indexTupDesc;
+	HeapTuple	indexTup;
 	bool		shared_relation;
 	bool		mapped_relation;
 	bool		is_exclusion;
@@ -847,10 +848,11 @@ index_create(Relation heapRelation,
 	/*
 	 * store index's pg_class entry
 	 */
-	InsertPgClassTuple(pg_class, indexRelation,
+	indexTup = InsertPgClassTuple(pg_class, indexRelation,
 					   RelationGetRelid(indexRelation),
 					   (Datum) 0,
 					   reloptions);
+	heap_freetuple(indexTup);
 
 	/* done with pg_class */
 	heap_close(pg_class, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_collation.c b/src/backend/catalog/pg_collation.c
index ce9bfae..9b6a0f0 100644
--- a/src/backend/catalog/pg_collation.c
+++ b/src/backend/catalog/pg_collation.c
@@ -135,8 +135,9 @@ CollationCreate(const char *collname, Oid collnamespace,
 	recordDependencyOnCurrentExtension(&myself, false);
 
 	/* Post creation hook for new collation */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   CollationRelationId, oid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							CollationRelationId, oid, 0,
+							PointerGetDatum(GETSTRUCT(tup)));
 
 	heap_freetuple(tup);
 	heap_close(rel, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 6997994..084743e 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -364,7 +364,9 @@ CreateConstraintEntry(const char *constraintName,
 	}
 
 	/* Post creation hook for new constraint */
-	InvokeObjectAccessHook(OAT_POST_CREATE, ConstraintRelationId, conOid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							ConstraintRelationId, conOid, 0,
+							PointerGetDatum(GETSTRUCT(tup)));
 
 	return conOid;
 }
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index c84dbc6..72acc17 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -136,8 +136,9 @@ ConversionCreate(const char *conname, Oid connamespace,
 	recordDependencyOnCurrentExtension(&myself, false);
 
 	/* Post creation hook for new conversion */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   ConversionRelationId, HeapTupleGetOid(tup), 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							ConversionRelationId, HeapTupleGetOid(tup), 0,
+							PointerGetDatum(GETSTRUCT(tup)));
 
 	heap_freetuple(tup);
 	heap_close(rel, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_largeobject.c b/src/backend/catalog/pg_largeobject.c
index 6d6c9e9..75867dc 100644
--- a/src/backend/catalog/pg_largeobject.c
+++ b/src/backend/catalog/pg_largeobject.c
@@ -20,6 +20,7 @@
 #include "catalog/catalog.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
 #include "catalog/pg_authid.h"
 #include "catalog/pg_largeobject.h"
 #include "catalog/pg_largeobject_metadata.h"
@@ -71,6 +72,10 @@ LargeObjectCreate(Oid loid)
 
 	CatalogUpdateIndexes(pg_lo_meta, ntup);
 
+	/* Post creation hook for new large object */
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							LargeObjectRelationId, loid_new, 0,
+							PointerGetDatum(GETSTRUCT(ntup)));
 	heap_freetuple(ntup);
 
 	heap_close(pg_lo_meta, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_namespace.c b/src/backend/catalog/pg_namespace.c
index ceebac2..a21cefe 100644
--- a/src/backend/catalog/pg_namespace.c
+++ b/src/backend/catalog/pg_namespace.c
@@ -86,7 +86,9 @@ NamespaceCreate(const char *nspName, Oid ownerId)
 	recordDependencyOnCurrentExtension(&myself, false);
 
 	/* Post creation hook for new schema */
-	InvokeObjectAccessHook(OAT_POST_CREATE, NamespaceRelationId, nspoid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							NamespaceRelationId, nspoid, 0,
+							PointerGetDatum(GETSTRUCT(tup)));
 
 	return nspoid;
 }
diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c
index 772e5d4..4571293 100644
--- a/src/backend/catalog/pg_operator.c
+++ b/src/backend/catalog/pg_operator.c
@@ -271,11 +271,11 @@ OperatorShellMake(const char *operatorName,
 	/* Add dependencies for the entry */
 	makeOperatorDependencies(tup);
 
-	heap_freetuple(tup);
-
 	/* Post creation hook for new shell operator */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   OperatorRelationId, operatorObjectId, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							OperatorRelationId, operatorObjectId, 0,
+							PointerGetDatum(GETSTRUCT(tup)));
+	heap_freetuple(tup);
 
 	/*
 	 * Make sure the tuple is visible for subsequent lookups/updates.
@@ -543,8 +543,9 @@ OperatorCreate(const char *operatorName,
 	makeOperatorDependencies(tup);
 
 	/* Post creation hook for new operator */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   OperatorRelationId, operatorObjectId, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							OperatorRelationId, operatorObjectId, 0,
+							PointerGetDatum(GETSTRUCT(tup)));
 
 	heap_close(pg_operator_desc, RowExclusiveLock);
 
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index f2f08721..d31f21c 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -620,10 +620,11 @@ ProcedureCreate(const char *procedureName,
 	/* dependency on extension */
 	recordDependencyOnCurrentExtension(&myself, is_update);
 
-	heap_freetuple(tup);
-
 	/* Post creation hook for new function */
-	InvokeObjectAccessHook(OAT_POST_CREATE, ProcedureRelationId, retval, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							ProcedureRelationId, retval, 0,
+							PointerGetDatum(GETSTRUCT(tup)));
+	heap_freetuple(tup);
 
 	heap_close(rel, RowExclusiveLock);
 
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index ea52f67..9e8049e 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -161,7 +161,10 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
 								 false);
 
 	/* Post creation hook for new shell type */
-	InvokeObjectAccessHook(OAT_POST_CREATE, TypeRelationId, typoid, 0);
+	InvokeObjectAccessHook2(OAT_POST_CREATE,
+							TypeRelationId, typoid, 0,
+							PointerGetDatum(GETSTRUCT(tup)),
+							BoolGetDatum(false));	/* not an implicit array */
 
 	/*
 	 * clean up and return the type-oid
@@ -465,8 +468,10 @@ TypeCreate(Oid newTypeOid,
 								 rebuildDeps);
 
 	/* Post creation hook for new type */
-	InvokeObjectAccessHook(OAT_POST_CREATE, TypeRelationId, typeObjectId, 0);
-
+	InvokeObjectAccessHook2(OAT_POST_CREATE,
+							TypeRelationId, typeObjectId, 0,
+							PointerGetDatum(GETSTRUCT(tup)),
+							BoolGetDatum(isImplicitArray));
 	/*
 	 * finish up
 	 */
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index a09a3ad..e8b42f1 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -228,7 +228,9 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
 										   ONCOMMIT_NOOP,
 										   reloptions,
 										   false,
-										   true);
+										   true,
+										   false,
+										   false);
 	Assert(toast_relid != InvalidOid);
 
 	/* make the toast relation visible, else heap_open will fail */
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 670d29e..8c8ba71 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -653,7 +653,9 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace)
 										  ONCOMMIT_NOOP,
 										  reloptions,
 										  false,
-										  true);
+										  true,
+										  true,
+										  false);
 	Assert(OIDNewHeap != InvalidOid);
 
 	ReleaseSysCache(tuple);
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 93240ef..59a17f7 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -520,7 +520,10 @@ createdb(const CreatedbStmt *stmt)
 	copyTemplateDependencies(src_dboid, dboid);
 
 	/* Post creation hook for new database */
-	InvokeObjectAccessHook(OAT_POST_CREATE, DatabaseRelationId, dboid, 0);
+	InvokeObjectAccessHook2(OAT_POST_CREATE,
+							DatabaseRelationId, dboid, 0,
+							PointerGetDatum(GETSTRUCT(tuple)),
+							ObjectIdGetDatum(src_dboid));
 
 	/*
 	 * Force a checkpoint before starting the copy. This will force dirty
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 9b9bb7d..d6ef9eb 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1519,7 +1519,6 @@ InsertExtensionTuple(const char *extName, Oid extOwner,
 	extensionOid = simple_heap_insert(rel, tuple);
 	CatalogUpdateIndexes(rel, tuple);
 
-	heap_freetuple(tuple);
 	heap_close(rel, RowExclusiveLock);
 
 	/*
@@ -1549,8 +1548,10 @@ InsertExtensionTuple(const char *extName, Oid extOwner,
 		recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL);
 	}
 	/* Post creation hook for new extension */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   ExtensionRelationId, extensionOid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							ExtensionRelationId, extensionOid, 0,
+							PointerGetDatum(GETSTRUCT(tuple)));
+	heap_freetuple(tuple);
 
 	return extensionOid;
 }
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 3a3c131..f03fa23 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -489,8 +489,6 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
 	fdwId = simple_heap_insert(rel, tuple);
 	CatalogUpdateIndexes(rel, tuple);
 
-	heap_freetuple(tuple);
-
 	/* record dependencies */
 	myself.classId = ForeignDataWrapperRelationId;
 	myself.objectId = fdwId;
@@ -518,9 +516,11 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
 	recordDependencyOnCurrentExtension(&myself, false);
 
 	/* Post creation hook for new foreign data wrapper */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   ForeignDataWrapperRelationId, fdwId, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							ForeignDataWrapperRelationId, fdwId, 0,
+							PointerGetDatum(GETSTRUCT(tuple)));
 
+	heap_freetuple(tuple);
 	heap_close(rel, RowExclusiveLock);
 }
 
@@ -841,8 +841,6 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
 
 	CatalogUpdateIndexes(rel, tuple);
 
-	heap_freetuple(tuple);
-
 	/* record dependencies */
 	myself.classId = ForeignServerRelationId;
 	myself.objectId = srvId;
@@ -859,8 +857,11 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
 	recordDependencyOnCurrentExtension(&myself, false);
 
 	/* Post creation hook for new foreign server */
-	InvokeObjectAccessHook(OAT_POST_CREATE, ForeignServerRelationId, srvId, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							ForeignServerRelationId, srvId, 0,
+							PointerGetDatum(GETSTRUCT(tuple)));
 
+	heap_freetuple(tuple);
 	heap_close(rel, RowExclusiveLock);
 }
 
@@ -1116,8 +1117,6 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
 
 	CatalogUpdateIndexes(rel, tuple);
 
-	heap_freetuple(tuple);
-
 	/* Add dependency on the server */
 	myself.classId = UserMappingRelationId;
 	myself.objectId = umId;
@@ -1138,8 +1137,11 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
 	recordDependencyOnCurrentExtension(&myself, false);
 
 	/* Post creation hook for new user mapping */
-	InvokeObjectAccessHook(OAT_POST_CREATE, UserMappingRelationId, umId, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							UserMappingRelationId, umId, 0,
+							PointerGetDatum(GETSTRUCT(tuple)));
 
+	heap_freetuple(tuple);
 	heap_close(rel, RowExclusiveLock);
 }
 
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 92abd44..601b034 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -1769,7 +1769,9 @@ CreateCast(CreateCastStmt *stmt)
 	recordDependencyOnCurrentExtension(&myself, false);
 
 	/* Post creation hook for new cast */
-	InvokeObjectAccessHook(OAT_POST_CREATE, CastRelationId, castid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							CastRelationId, castid, 0,
+							PointerGetDatum(tuple));
 
 	heap_freetuple(tuple);
 
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 2bb0d4c..056deb7 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -289,8 +289,6 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
 
 	CatalogUpdateIndexes(rel, tup);
 
-	heap_freetuple(tup);
-
 	/*
 	 * Create dependencies for the opfamily proper.  Note: we do not create a
 	 * dependency link to the AM, because we don't currently support DROP
@@ -313,9 +311,11 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
 	recordDependencyOnCurrentExtension(&myself, false);
 
 	/* Post creation hook for new operator family */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   OperatorFamilyRelationId, opfamilyoid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							OperatorFamilyRelationId, opfamilyoid, 0,
+							PointerGetDatum(GETSTRUCT(tup)));
 
+	heap_freetuple(tup);
 	heap_close(rel, RowExclusiveLock);
 
 	return opfamilyoid;
@@ -662,8 +662,6 @@ DefineOpClass(CreateOpClassStmt *stmt)
 
 	CatalogUpdateIndexes(rel, tup);
 
-	heap_freetuple(tup);
-
 	/*
 	 * Now add tuples to pg_amop and pg_amproc tying in the operators and
 	 * functions.  Dependencies on them are inserted, too.
@@ -716,9 +714,11 @@ DefineOpClass(CreateOpClassStmt *stmt)
 	recordDependencyOnCurrentExtension(&myself, false);
 
 	/* Post creation hook for new operator class */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   OperatorClassRelationId, opclassoid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							OperatorClassRelationId, opclassoid, 0,
+							PointerGetDatum(GETSTRUCT(tup)));
 
+	heap_freetuple(tup);
 	heap_close(rel, RowExclusiveLock);
 }
 
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
index 98770c5..3a1cc74 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -430,8 +430,9 @@ create_proc_lang(const char *languageName, bool replace,
 	}
 
 	/* Post creation hook for new procedural language */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   LanguageRelationId, myself.objectId, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							LanguageRelationId, myself.objectId, 0,
+							PointerGetDatum(GETSTRUCT(tup)));
 
 	heap_close(rel, RowExclusiveLock);
 }
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 4509cda..441e42b 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -600,7 +600,9 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
 										  stmt->oncommit,
 										  reloptions,
 										  true,
-										  allowSystemTableMods);
+										  allowSystemTableMods,
+										  false,
+										  false);
 
 	/* Store inheritance information for new rel. */
 	StoreCatalogInheritance(relationId, inheritOids);
@@ -4367,12 +4369,13 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	/* keep catalog indexes current */
 	CatalogUpdateIndexes(pgclass, reltup);
 
-	heap_freetuple(reltup);
-
 	/* Post creation hook for new attribute */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   RelationRelationId, myrelid, newattnum);
+	InvokeObjectAccessHook2(OAT_POST_CREATE,
+							RelationRelationId, myrelid, newattnum,
+							PointerGetDatum(&attribute),
+							PointerGetDatum(GETSTRUCT(reltup)));
 
+	heap_freetuple(reltup);
 	heap_close(pgclass, RowExclusiveLock);
 
 	/* Make the attribute's catalog entry visible */
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 09ecabb..4372eaa 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -330,14 +330,15 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
 
 	CatalogUpdateIndexes(rel, tuple);
 
-	heap_freetuple(tuple);
-
 	/* Record dependency on owner */
 	recordDependencyOnOwner(TableSpaceRelationId, tablespaceoid, ownerId);
 
 	/* Post creation hook for new tablespace */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   TableSpaceRelationId, tablespaceoid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							TableSpaceRelationId, tablespaceoid, 0,
+							PointerGetDatum(GETSTRUCT(tuple)));
+
+	heap_freetuple(tuple);
 
 	create_tablespace_directories(location, tablespaceoid);
 
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 4c31f19..85ae505 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -134,6 +134,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
 	ScanKeyData key;
 	Relation	pgrel;
 	HeapTuple	tuple;
+	HeapTuple	reltup;
 	Oid			fargtypes[1];	/* dummy */
 	Oid			funcoid;
 	Oid			funcrettype;
@@ -630,32 +631,27 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
 
 	CatalogUpdateIndexes(tgrel, tuple);
 
-	heap_freetuple(tuple);
 	heap_close(tgrel, RowExclusiveLock);
 
-	pfree(DatumGetPointer(values[Anum_pg_trigger_tgname - 1]));
-	pfree(DatumGetPointer(values[Anum_pg_trigger_tgargs - 1]));
-	pfree(DatumGetPointer(values[Anum_pg_trigger_tgattr - 1]));
-
 	/*
 	 * Update relation's pg_class entry.  Crucial side-effect: other backends
 	 * (and this one too!) are sent SI message to make them rebuild relcache
 	 * entries.
 	 */
 	pgrel = heap_open(RelationRelationId, RowExclusiveLock);
-	tuple = SearchSysCacheCopy1(RELOID,
+	reltup = SearchSysCacheCopy1(RELOID,
 								ObjectIdGetDatum(RelationGetRelid(rel)));
-	if (!HeapTupleIsValid(tuple))
+	if (!HeapTupleIsValid(reltup))
 		elog(ERROR, "cache lookup failed for relation %u",
 			 RelationGetRelid(rel));
 
-	((Form_pg_class) GETSTRUCT(tuple))->relhastriggers = true;
+	((Form_pg_class) GETSTRUCT(reltup))->relhastriggers = true;
 
-	simple_heap_update(pgrel, &tuple->t_self, tuple);
+	simple_heap_update(pgrel, &reltup->t_self, reltup);
 
-	CatalogUpdateIndexes(pgrel, tuple);
+	CatalogUpdateIndexes(pgrel, reltup);
 
-	heap_freetuple(tuple);
+	heap_freetuple(reltup);
 	heap_close(pgrel, RowExclusiveLock);
 
 	/*
@@ -747,8 +743,17 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
 							   DEPENDENCY_NORMAL);
 
 	/* Post creation hook for new trigger */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   TriggerRelationId, trigoid, 0);
+	InvokeObjectAccessHook2(OAT_POST_CREATE,
+							TriggerRelationId, trigoid, 0,
+							PointerGetDatum(GETSTRUCT(tuple)),
+							BoolGetDatum(isInternal));
+
+	/* Release contents of pg_trigger tuple */
+	pfree(DatumGetPointer(values[Anum_pg_trigger_tgname - 1]));
+	pfree(DatumGetPointer(values[Anum_pg_trigger_tgargs - 1]));
+	pfree(DatumGetPointer(values[Anum_pg_trigger_tgattr - 1]));
+
+	heap_freetuple(tuple);
 
 	/* Keep lock on target rel until end of xact */
 	heap_close(rel, NoLock);
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index deac106..ec48d97 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -275,7 +275,9 @@ DefineTSParser(List *names, List *parameters)
 	makeParserDependencies(tup);
 
 	/* Post creation hook for new text search parser */
-	InvokeObjectAccessHook(OAT_POST_CREATE, TSParserRelationId, prsOid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							TSParserRelationId, prsOid, 0,
+							PointerGetDatum(GETSTRUCT(tup)));
 
 	heap_freetuple(tup);
 
@@ -627,8 +629,9 @@ DefineTSDictionary(List *names, List *parameters)
 	makeDictionaryDependencies(tup);
 
 	/* Post creation hook for new text search dictionary */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   TSDictionaryRelationId, dictOid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							TSDictionaryRelationId, dictOid, 0,
+							PointerGetDatum(GETSTRUCT(tup)));
 
 	heap_freetuple(tup);
 
@@ -1169,7 +1172,9 @@ DefineTSTemplate(List *names, List *parameters)
 	makeTSTemplateDependencies(tup);
 
 	/* Post creation hook for new text search template */
-	InvokeObjectAccessHook(OAT_POST_CREATE, TSTemplateRelationId, dictOid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							TSTemplateRelationId, dictOid, 0,
+							PointerGetDatum(GETSTRUCT(tup)));
 
 	heap_freetuple(tup);
 
@@ -1611,7 +1616,9 @@ DefineTSConfiguration(List *names, List *parameters)
 	makeConfigurationDependencies(tup, false, mapRel);
 
 	/* Post creation hook for new text search configuration */
-	InvokeObjectAccessHook(OAT_POST_CREATE, TSConfigRelationId, cfgOid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							TSConfigRelationId, cfgOid, 0,
+							PointerGetDatum(GETSTRUCT(tup)));
 
 	heap_freetuple(tup);
 
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 0367b20..505a010 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -434,7 +434,9 @@ CreateRole(CreateRoleStmt *stmt)
 				GetUserId(), false);
 
 	/* Post creation hook for new role */
-	InvokeObjectAccessHook(OAT_POST_CREATE, AuthIdRelationId, roleid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							AuthIdRelationId, roleid, 0,
+							PointerGetDatum(GETSTRUCT(tuple)));
 
 	/*
 	 * Close pg_authid, but keep lock till commit.
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index eacd863..092473c 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -2486,7 +2486,9 @@ OpenIntoRel(QueryDesc *queryDesc)
 											  into->onCommit,
 											  reloptions,
 											  true,
-											  allowSystemTableMods);
+											  allowSystemTableMods,
+											  false,
+											  true);
 	Assert(intoRelationId != InvalidOid);
 
 	/*
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 17db70e..a5cb7f7 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -137,8 +137,6 @@ InsertRule(char *rulname,
 	/* Need to update indexes in either case */
 	CatalogUpdateIndexes(pg_rewrite_desc, tup);
 
-	heap_freetuple(tup);
-
 	/* If replacing, get rid of old dependencies and make new ones */
 	if (is_update)
 		deleteDependencyRecordsFor(RewriteRelationId, rewriteObjectId, false);
@@ -177,9 +175,11 @@ InsertRule(char *rulname,
 	}
 
 	/* Post creation hook for new rule */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   RewriteRelationId, rewriteObjectId, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							RewriteRelationId, rewriteObjectId, 0,
+							PointerGetDatum(GETSTRUCT(tup)));
 
+	heap_freetuple(tup);
 	heap_close(pg_rewrite_desc, RowExclusiveLock);
 
 	return rewriteObjectId;
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c
index e0441f5..0c2a2f1 100644
--- a/src/backend/storage/large_object/inv_api.c
+++ b/src/backend/storage/large_object/inv_api.c
@@ -38,7 +38,6 @@
 #include "catalog/catalog.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
-#include "catalog/objectaccess.h"
 #include "catalog/pg_largeobject.h"
 #include "catalog/pg_largeobject_metadata.h"
 #include "commands/comment.h"
@@ -219,10 +218,6 @@ inv_create(Oid lobjId)
 	recordDependencyOnOwner(LargeObjectRelationId,
 							lobjId_new, GetUserId());
 
-	/* Post creation hook for new large object */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   LargeObjectRelationId, lobjId_new, 0);
-
 	/*
 	 * Advance command counter to make new tuple visible to later operations.
 	 */
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index aee2d88..8b013df 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -65,7 +65,9 @@ extern Oid heap_create_with_catalog(const char *relname,
 						 OnCommitAction oncommit,
 						 Datum reloptions,
 						 bool use_user_acl,
-						 bool allow_system_table_mods);
+						 bool allow_system_table_mods,
+						 bool is_rebuilt_relation,
+						 bool is_select_into);
 
 extern void heap_drop_with_catalog(Oid relid);
 
@@ -81,7 +83,7 @@ extern void InsertPgAttributeTuple(Relation pg_attribute_rel,
 					   Form_pg_attribute new_attribute,
 					   CatalogIndexState indstate);
 
-extern void InsertPgClassTuple(Relation pg_class_desc,
+extern HeapTuple InsertPgClassTuple(Relation pg_class_desc,
 				   Relation new_rel_desc,
 				   Oid new_rel_oid,
 				   Datum relacl,
diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h
index 2925475..3d14e79 100644
--- a/src/include/catalog/objectaccess.h
+++ b/src/include/catalog/objectaccess.h
@@ -23,7 +23,7 @@
  */
 typedef enum ObjectAccessType
 {
-	OAT_POST_CREATE,
+	OAT_POST_CREATE,	/* Post-creation of database object */
 } ObjectAccessType;
 
 /*
@@ -33,14 +33,216 @@ typedef enum ObjectAccessType
 typedef void (*object_access_hook_type) (ObjectAccessType access,
 													 Oid classId,
 													 Oid objectId,
-													 int subId);
+													 int subId,
+													 int nargs,
+													 Datum argv[]);
 
 extern PGDLLIMPORT object_access_hook_type object_access_hook;
 
-#define InvokeObjectAccessHook(access,classId,objectId,subId)			\
-	do {																\
-		if (object_access_hook)											\
-			(*object_access_hook)((access),(classId),(objectId),(subId)); \
+#define InvokeObjectAccessHook0(access,classId,objectId,subId)	\
+	do {														\
+		if (object_access_hook)									\
+			(*object_access_hook)((access),						\
+								  (classId),(objectId),(subId),	\
+								  0, NULL);						\
+	} while(0)
+
+#define InvokeObjectAccessHook1(access,classId,objectId,subId,	\
+								arg0)							\
+	do {														\
+		if (object_access_hook)									\
+		{														\
+			Datum	____temp_arg_buf[1];						\
+																\
+			____temp_arg_buf[0] = (arg0);						\
+																\
+			(*object_access_hook)((access),						\
+								  (classId),(objectId),(subId), \
+								  1, ____temp_arg_buf);			\
+		}														\
+	} while(0)
+
+#define InvokeObjectAccessHook2(access,classId,objectId,subId,	\
+								arg0, arg1)						\
+	do {														\
+		if (object_access_hook)									\
+		{														\
+			Datum	____temp_arg_buf[2];						\
+																\
+			____temp_arg_buf[0] = (arg0);						\
+			____temp_arg_buf[1] = (arg1);						\
+																\
+			(*object_access_hook)((access),						\
+								  (classId),(objectId),(subId), \
+								  2, ____temp_arg_buf);			\
+		}														\
+	} while(0)
+
+#define InvokeObjectAccessHook3(access,classId,objectId,subId,	\
+								arg0, arg1, arg2)				\
+	do {														\
+		if (object_access_hook)									\
+		{														\
+			Datum	____temp_arg_buf[3];						\
+																\
+			____temp_arg_buf[0] = (arg0);						\
+			____temp_arg_buf[1] = (arg1);						\
+			____temp_arg_buf[2] = (arg2);						\
+																\
+			(*object_access_hook)((access),						\
+								  (classId),(objectId),(subId), \
+								  3, ____temp_arg_buf);			\
+		}														\
+	} while(0)
+
+#define InvokeObjectAccessHook4(access,classId,objectId,subId,	\
+								arg0, arg1, arg2, arg3)			\
+	do {														\
+		if (object_access_hook)									\
+		{														\
+			Datum	____temp_arg_buf[4];						\
+																\
+			____temp_arg_buf[0] = (arg0);						\
+			____temp_arg_buf[1] = (arg1);						\
+			____temp_arg_buf[2] = (arg2);						\
+			____temp_arg_buf[3] = (arg3);						\
+																\
+			(*object_access_hook)((access),						\
+								  (classId),(objectId),(subId), \
+								  4, ____temp_arg_buf);			\
+		}														\
+	} while(0)
+
+#define InvokeObjectAccessHook5(access,classId,objectId,subId,	\
+								arg0, arg1, arg2, arg3, arg4)	\
+	do {														\
+		if (object_access_hook)									\
+		{														\
+			Datum	____temp_arg_buf[5];						\
+																\
+			____temp_arg_buf[0] = (arg0);						\
+			____temp_arg_buf[1] = (arg1);						\
+			____temp_arg_buf[2] = (arg2);						\
+			____temp_arg_buf[3] = (arg3);						\
+			____temp_arg_buf[4] = (arg4);						\
+																\
+			(*object_access_hook)((access),						\
+								  (classId),(objectId),(subId), \
+								  5, ____temp_arg_buf);			\
+		}														\
+	} while(0)
+
+#define InvokeObjectAccessHook6(access,classId,objectId,subId,	\
+								arg0, arg1, arg2, arg3, arg4,	\
+								arg5)							\
+	do {														\
+		if (object_access_hook)									\
+		{														\
+			Datum	____temp_arg_buf[6];						\
+																\
+			____temp_arg_buf[0] = (arg0);						\
+			____temp_arg_buf[1] = (arg1);						\
+			____temp_arg_buf[2] = (arg2);						\
+			____temp_arg_buf[3] = (arg3);						\
+			____temp_arg_buf[4] = (arg4);						\
+			____temp_arg_buf[5] = (arg5);						\
+																\
+			(*object_access_hook)((access),						\
+								  (classId),(objectId),(subId), \
+								  6, ____temp_arg_buf);			\
+		}														\
+	} while(0)
+
+#define InvokeObjectAccessHook7(access,classId,objectId,subId,	\
+								arg0, arg1, arg2, arg3, arg4,	\
+								arg5, arg6)						\
+	do {														\
+		if (object_access_hook)									\
+		{														\
+			Datum	____temp_arg_buf[7];						\
+																\
+			____temp_arg_buf[0] = (arg0);						\
+			____temp_arg_buf[1] = (arg1);						\
+			____temp_arg_buf[2] = (arg2);						\
+			____temp_arg_buf[3] = (arg3);						\
+			____temp_arg_buf[4] = (arg4);						\
+			____temp_arg_buf[5] = (arg5);						\
+			____temp_arg_buf[6] = (arg6);						\
+																\
+			(*object_access_hook)((access),						\
+								  (classId),(objectId),(subId), \
+								  7, ____temp_arg_buf);			\
+		}														\
+	} while(0)
+
+#define InvokeObjectAccessHook8(access,classId,objectId,subId,	\
+								arg0, arg1, arg2, arg3, arg4,	\
+								arg5, arg6, arg7)				\
+	do {														\
+		if (object_access_hook)									\
+		{														\
+			Datum	____temp_arg_buf[8];						\
+																\
+			____temp_arg_buf[0] = (arg0);						\
+			____temp_arg_buf[1] = (arg1);						\
+			____temp_arg_buf[2] = (arg2);						\
+			____temp_arg_buf[3] = (arg3);						\
+			____temp_arg_buf[4] = (arg4);						\
+			____temp_arg_buf[5] = (arg5);						\
+			____temp_arg_buf[6] = (arg6);						\
+			____temp_arg_buf[7] = (arg7);						\
+																\
+			(*object_access_hook)((access),						\
+								  (classId),(objectId),(subId), \
+								  8, ____temp_arg_buf);			\
+		}														\
+	} while(0)
+
+#define InvokeObjectAccessHook8(access,classId,objectId,subId,	\
+								arg0, arg1, arg2, arg3, arg4,	\
+								arg5, arg6, arg7)				\
+	do {														\
+		if (object_access_hook)									\
+		{														\
+			Datum	____temp_arg_buf[8];						\
+																\
+			____temp_arg_buf[0] = (arg0);						\
+			____temp_arg_buf[1] = (arg1);						\
+			____temp_arg_buf[2] = (arg2);						\
+			____temp_arg_buf[3] = (arg3);						\
+			____temp_arg_buf[4] = (arg4);						\
+			____temp_arg_buf[5] = (arg5);						\
+			____temp_arg_buf[6] = (arg6);						\
+			____temp_arg_buf[7] = (arg7);						\
+																\
+			(*object_access_hook)((access),						\
+								  (classId),(objectId),(subId), \
+								  8, ____temp_arg_buf);			\
+		}														\
+	} while(0)
+
+#define InvokeObjectAccessHook9(access,classId,objectId,subId,	\
+								arg0, arg1, arg2, arg3, arg4,	\
+								arg5, arg6, arg7, arg8)			\
+	do {														\
+		if (object_access_hook)									\
+		{														\
+			Datum	____temp_arg_buf[9];						\
+																\
+			____temp_arg_buf[0] = (arg0);						\
+			____temp_arg_buf[1] = (arg1);						\
+			____temp_arg_buf[2] = (arg2);						\
+			____temp_arg_buf[3] = (arg3);						\
+			____temp_arg_buf[4] = (arg4);						\
+			____temp_arg_buf[5] = (arg5);						\
+			____temp_arg_buf[6] = (arg6);						\
+			____temp_arg_buf[7] = (arg7);						\
+			____temp_arg_buf[8] = (arg8);						\
+																\
+			(*object_access_hook)((access),						\
+								  (classId),(objectId),(subId), \
+								  9, ____temp_arg_buf);			\
+		}														\
 	} while(0)
 
 #endif   /* OBJECTACCESS_H */
#2Kohei KaiGai
kaigai@kaigai.gr.jp
In reply to: Kohei KaiGai (#1)
1 attachment(s)
Re: [v9.2] Object access hooks with arguments support (v1)

I noticed that the previous revision does not provide any way to inform
the modules name of foreign server, even if foreign table was created,
on the OAT_POST_CREATE hook.
So, I modified the invocation at heap_create_with_catalog to deliver
this information to the modules.

Rest of parts were unchanged, except for rebasing to the latest git
master.

2011/8/28 Kohei KaiGai <kaigai@kaigai.gr.jp>:

The attached patch is a draft to support arguments in addition to
OAT_* enum and object identifiers.

The existing object_access_hook enables loadable modules to acquire
control when objects are referenced. The first guest of this hook is
contrib/sepgsql for assignment of default security label on newly
created objects. Right now, OAT_POST_CREATE is the all supported
object access type. However, we plan to enhance this mechanism onto
other widespread purpose; such as comprehensive DDL permissions
supported by loadable modules.

This patch is a groundwork to utilize this hook for object creation
permission checks, not only default labeling.
At the v9.1 development cycle, I proposed an idea that defines both
OAT_CREATE hook prior to system catalog modification and
OAT_POST_CREATE hook as currently we have. This design enables to
check permission next to the existing pg_xxx_aclcheck() or
pg_xxx_ownercheck(), and raise an error before system catalog updates.
However, it was painful to deliver private datum set on OAT_CREATE to
the OAT_POST_CREATE due to the code complexity.

The other idea tried to do all the necessary things in OAT_POST_CREATE
hook, and it had been merged, because loadable modules can pull
properties of the new object from system catalogs by the supplied
object identifiers. Thus, contrib/sepgsql assigns a default security
label on new object using OAT_POST_CREATE hook.
However, I have two concern on the existing hook to implement
permission check for object creation. The first one is the entry of
system catalog is not visible using SnaphotNow, so we had to open and
scan system catalog again, instead of syscache mechanism. The second
one is more significant. A part of information to make access control
decision is not available within contents of the system catalog
entries. For example, we hope to skip permission check when
heap_create_with_catalog() was launched by make_new_heap() because the
new relation is just used to swap later.

Thus, I'd like to propose to support argument of object_access_hook to
inform the loadable modules additional contextual information on its
invocations; to solve these concerns.

Regarding to the first concern, fortunately, most of existing
OAT_POST_CREATE hook is deployed just after insert or update of system
catalogs, but before CCI. So, it is helpful for the loadable modules
to deliver Form_pg_xxxx data to reference properties of the new
object, instead of open and scan the catalog again.
In the draft patch, I enhanced OAT_POST_CREATE hook commonly to take
an argument that points to the Form_pg_xxxx data of the new object.

Regarding to the second concern, I added a few contextual information
as second or later arguments in a part of object classes. Right now, I
hope the following contextual information shall be provided to
OAT_POST_CREATE hook to implement permission checks of object
creation.

* pg_class - TupleDesc structure of the new relation
I want to reference of pg_attribute, not only pg_class.

* pg_class - A flag to show whether the relation is defined for
rebuilding, or not.
I want not to apply permission check in the case when it is invoked
from make_new_heap(), because it just create a dummy table as a part
of internal process. All the necessary permission checks should be
done at ALTER TABLE or CLUSTER.

And, name of the foreign server being used by the foreign table
being created just a moment before.

* pg_class - A flag to show whether the relation is created with
SELECT INTO, or not.
I want to check "insert" permission of the new table, created by
SELECT INTO, because DML hook is not available to check this case.

* pg_type - A flag to show whether the type is defined as implicit
array, or not.
I want not to apply permission check on creation of implicit array type.

* pg_database - Oid of the source (template) database.
I want to fetch security label of the source database to compute a
default label of the new database.

* pg_trigger - A flag to show whether the trigger is used to FK
constraint, or not.
I want not to apply permission check on creation of FK constraint. It
should be done in ALTER TABLE level.

Sorry for this long explanation. Right now, I tend to consider it is
the best way to implement permission checks on object creation with
least invasive way.

Thanks, Any comments welcome.
--
KaiGai Kohei <kaigai@kaigai.gr.jp>

--
KaiGai Kohei <kaigai@kaigai.gr.jp>

Attachments:

pgsql-v9.2-object-access-hooks-argument.v2.patchapplication/octet-stream; name=pgsql-v9.2-object-access-hooks-argument.v2.patchDownload
 contrib/sepgsql/hooks.c                    |    8 +-
 src/backend/bootstrap/bootparse.y          |    4 +-
 src/backend/catalog/heap.c                 |   29 +++--
 src/backend/catalog/index.c                |    4 +-
 src/backend/catalog/pg_collation.c         |    5 +-
 src/backend/catalog/pg_constraint.c        |    4 +-
 src/backend/catalog/pg_conversion.c        |    5 +-
 src/backend/catalog/pg_largeobject.c       |    5 +
 src/backend/catalog/pg_namespace.c         |    4 +-
 src/backend/catalog/pg_operator.c          |   13 +-
 src/backend/catalog/pg_proc.c              |    7 +-
 src/backend/catalog/pg_type.c              |    9 +-
 src/backend/catalog/toasting.c             |    4 +-
 src/backend/commands/cluster.c             |    4 +-
 src/backend/commands/dbcommands.c          |    5 +-
 src/backend/commands/extension.c           |    7 +-
 src/backend/commands/foreigncmds.c         |   22 ++--
 src/backend/commands/functioncmds.c        |    4 +-
 src/backend/commands/opclasscmds.c         |   16 +-
 src/backend/commands/proclang.c            |    5 +-
 src/backend/commands/tablecmds.c           |   21 ++-
 src/backend/commands/tablespace.c          |    9 +-
 src/backend/commands/trigger.c             |   31 +++--
 src/backend/commands/tsearchcmds.c         |   17 ++-
 src/backend/commands/user.c                |    4 +-
 src/backend/executor/execMain.c            |    4 +-
 src/backend/rewrite/rewriteDefine.c        |    8 +-
 src/backend/storage/large_object/inv_api.c |    5 -
 src/include/catalog/heap.h                 |    6 +-
 src/include/catalog/objectaccess.h         |  214 +++++++++++++++++++++++++++-
 30 files changed, 379 insertions(+), 104 deletions(-)

diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 331bbd7..cdd2631 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -116,10 +116,14 @@ static void
 sepgsql_object_access(ObjectAccessType access,
 					  Oid classId,
 					  Oid objectId,
-					  int subId)
+					  int subId,
+					  int nargs,
+					  Datum argv[])
 {
 	if (next_object_access_hook)
-		(*next_object_access_hook) (access, classId, objectId, subId);
+		(*next_object_access_hook) (access,
+									classId, objectId, subId,
+									nargs, argv);
 
 	switch (access)
 	{
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index d0a0e92..b7ed832 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -248,7 +248,9 @@ Boot_CreateStmt:
 													  ONCOMMIT_NOOP,
 													  (Datum) 0,
 													  false,
-													  true);
+													  true,
+													  false,
+													  NULL);
 						elog(DEBUG4, "relation created with OID %u", id);
 					}
 					do_end();
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 2aaf775..e723c33 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -74,7 +74,7 @@
 Oid			binary_upgrade_next_heap_pg_class_oid = InvalidOid;
 Oid			binary_upgrade_next_toast_pg_class_oid = InvalidOid;
 
-static void AddNewRelationTuple(Relation pg_class_desc,
+static HeapTuple AddNewRelationTuple(Relation pg_class_desc,
 					Relation new_rel_desc,
 					Oid new_rel_oid,
 					Oid new_type_oid,
@@ -746,7 +746,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
  * to NULL.
  * --------------------------------
  */
-void
+HeapTuple
 InsertPgClassTuple(Relation pg_class_desc,
 				   Relation new_rel_desc,
 				   Oid new_rel_oid,
@@ -808,7 +808,7 @@ InsertPgClassTuple(Relation pg_class_desc,
 
 	CatalogUpdateIndexes(pg_class_desc, tup);
 
-	heap_freetuple(tup);
+	return tup;	/* to be freed in the caller side */
 }
 
 /* --------------------------------
@@ -818,7 +818,7 @@ InsertPgClassTuple(Relation pg_class_desc,
  *		adding a tuple to pg_class.
  * --------------------------------
  */
-static void
+static HeapTuple
 AddNewRelationTuple(Relation pg_class_desc,
 					Relation new_rel_desc,
 					Oid new_rel_oid,
@@ -888,8 +888,8 @@ AddNewRelationTuple(Relation pg_class_desc,
 	new_rel_desc->rd_att->tdtypeid = new_type_oid;
 
 	/* Now build and insert the tuple */
-	InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid,
-					   relacl, reloptions);
+	return InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid,
+							  relacl, reloptions);
 }
 
 
@@ -966,6 +966,8 @@ AddNewRelationType(const char *typeName,
  *	use_user_acl: TRUE if should look for user-defined default permissions;
  *		if FALSE, relacl is always set NULL
  *	allow_system_table_mods: TRUE to allow creation in system namespaces
+ *	is_select_into: TRUE if table creation by SELECT INTO
+ *	fdw_srvname: NULL, or name of foreign server if foreign table.
  *
  * Returns the OID of the new relation
  * --------------------------------
@@ -989,10 +991,13 @@ heap_create_with_catalog(const char *relname,
 						 OnCommitAction oncommit,
 						 Datum reloptions,
 						 bool use_user_acl,
-						 bool allow_system_table_mods)
+						 bool allow_system_table_mods,
+						 bool is_select_into,
+						 const char *fdw_srvname)
 {
 	Relation	pg_class_desc;
 	Relation	new_rel_desc;
+	HeapTuple	new_rel_tup;
 	Acl		   *relacl;
 	Oid			existing_relid;
 	Oid			old_type_oid;
@@ -1203,7 +1208,7 @@ heap_create_with_catalog(const char *relname,
 	 * creating the same relation name in parallel but hadn't committed yet
 	 * when we checked for a duplicate name above.
 	 */
-	AddNewRelationTuple(pg_class_desc,
+	new_rel_tup = AddNewRelationTuple(pg_class_desc,
 						new_rel_desc,
 						relid,
 						new_type_oid,
@@ -1275,7 +1280,12 @@ heap_create_with_catalog(const char *relname,
 	}
 
 	/* Post creation hook for new relation */
-	InvokeObjectAccessHook(OAT_POST_CREATE, RelationRelationId, relid, 0);
+	InvokeObjectAccessHook4(OAT_POST_CREATE,
+							RelationRelationId, relid, 0,
+							PointerGetDatum(new_rel_tup),
+							PointerGetDatum(tupdesc),
+							BoolGetDatum(is_select_into),
+							CStringGetDatum(fdw_srvname));
 
 	/*
 	 * Store any supplied constraints and defaults.
@@ -1316,6 +1326,7 @@ heap_create_with_catalog(const char *relname,
 	 * ok, the relation has been cataloged, so close our relations and return
 	 * the OID of the newly created relation.
 	 */
+	heap_freetuple(new_rel_tup);
 	heap_close(new_rel_desc, NoLock);	/* do not unlock till end of xact */
 	heap_close(pg_class_desc, RowExclusiveLock);
 
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 67ade8f..fefcc27 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -692,6 +692,7 @@ index_create(Relation heapRelation,
 	Relation	pg_class;
 	Relation	indexRelation;
 	TupleDesc	indexTupDesc;
+	HeapTuple	indexTup;
 	bool		shared_relation;
 	bool		mapped_relation;
 	bool		is_exclusion;
@@ -842,10 +843,11 @@ index_create(Relation heapRelation,
 	/*
 	 * store index's pg_class entry
 	 */
-	InsertPgClassTuple(pg_class, indexRelation,
+	indexTup = InsertPgClassTuple(pg_class, indexRelation,
 					   RelationGetRelid(indexRelation),
 					   (Datum) 0,
 					   reloptions);
+	heap_freetuple(indexTup);
 
 	/* done with pg_class */
 	heap_close(pg_class, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_collation.c b/src/backend/catalog/pg_collation.c
index ce9bfae..46e0d80 100644
--- a/src/backend/catalog/pg_collation.c
+++ b/src/backend/catalog/pg_collation.c
@@ -135,8 +135,9 @@ CollationCreate(const char *collname, Oid collnamespace,
 	recordDependencyOnCurrentExtension(&myself, false);
 
 	/* Post creation hook for new collation */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   CollationRelationId, oid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							CollationRelationId, oid, 0,
+							PointerGetDatum(tup));
 
 	heap_freetuple(tup);
 	heap_close(rel, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 6997994..d4ea458 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -364,7 +364,9 @@ CreateConstraintEntry(const char *constraintName,
 	}
 
 	/* Post creation hook for new constraint */
-	InvokeObjectAccessHook(OAT_POST_CREATE, ConstraintRelationId, conOid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							ConstraintRelationId, conOid, 0,
+							PointerGetDatum(tup));
 
 	return conOid;
 }
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index e480aeb..38dd4eb 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -134,8 +134,9 @@ ConversionCreate(const char *conname, Oid connamespace,
 	recordDependencyOnCurrentExtension(&myself, false);
 
 	/* Post creation hook for new conversion */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   ConversionRelationId, HeapTupleGetOid(tup), 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							ConversionRelationId, HeapTupleGetOid(tup), 0,
+							PointerGetDatum(tup));
 
 	heap_freetuple(tup);
 	heap_close(rel, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_largeobject.c b/src/backend/catalog/pg_largeobject.c
index 24cc0d4..194df23 100644
--- a/src/backend/catalog/pg_largeobject.c
+++ b/src/backend/catalog/pg_largeobject.c
@@ -19,6 +19,7 @@
 #include "access/sysattr.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
 #include "catalog/pg_largeobject.h"
 #include "catalog/pg_largeobject_metadata.h"
 #include "miscadmin.h"
@@ -67,6 +68,10 @@ LargeObjectCreate(Oid loid)
 
 	CatalogUpdateIndexes(pg_lo_meta, ntup);
 
+	/* Post creation hook for new large object */
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							LargeObjectRelationId, loid_new, 0,
+							PointerGetDatum(ntup));
 	heap_freetuple(ntup);
 
 	heap_close(pg_lo_meta, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_namespace.c b/src/backend/catalog/pg_namespace.c
index ceebac2..dfb6789 100644
--- a/src/backend/catalog/pg_namespace.c
+++ b/src/backend/catalog/pg_namespace.c
@@ -86,7 +86,9 @@ NamespaceCreate(const char *nspName, Oid ownerId)
 	recordDependencyOnCurrentExtension(&myself, false);
 
 	/* Post creation hook for new schema */
-	InvokeObjectAccessHook(OAT_POST_CREATE, NamespaceRelationId, nspoid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							NamespaceRelationId, nspoid, 0,
+							PointerGetDatum(tup));
 
 	return nspoid;
 }
diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c
index 1440464..ace5789 100644
--- a/src/backend/catalog/pg_operator.c
+++ b/src/backend/catalog/pg_operator.c
@@ -271,11 +271,11 @@ OperatorShellMake(const char *operatorName,
 	/* Add dependencies for the entry */
 	makeOperatorDependencies(tup);
 
-	heap_freetuple(tup);
-
 	/* Post creation hook for new shell operator */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   OperatorRelationId, operatorObjectId, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							OperatorRelationId, operatorObjectId, 0,
+							PointerGetDatum(tup));
+	heap_freetuple(tup);
 
 	/*
 	 * Make sure the tuple is visible for subsequent lookups/updates.
@@ -543,8 +543,9 @@ OperatorCreate(const char *operatorName,
 	makeOperatorDependencies(tup);
 
 	/* Post creation hook for new operator */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   OperatorRelationId, operatorObjectId, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							OperatorRelationId, operatorObjectId, 0,
+							PointerGetDatum(tup));
 
 	heap_close(pg_operator_desc, RowExclusiveLock);
 
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index f2b78b6..42a8c27 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -619,10 +619,11 @@ ProcedureCreate(const char *procedureName,
 	/* dependency on extension */
 	recordDependencyOnCurrentExtension(&myself, is_update);
 
-	heap_freetuple(tup);
-
 	/* Post creation hook for new function */
-	InvokeObjectAccessHook(OAT_POST_CREATE, ProcedureRelationId, retval, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							ProcedureRelationId, retval, 0,
+							PointerGetDatum(tup));
+	heap_freetuple(tup);
 
 	heap_close(rel, RowExclusiveLock);
 
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index ea52f67..f4d22de 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -161,7 +161,9 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
 								 false);
 
 	/* Post creation hook for new shell type */
-	InvokeObjectAccessHook(OAT_POST_CREATE, TypeRelationId, typoid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							TypeRelationId, typoid, 0,
+							PointerGetDatum(tup));
 
 	/*
 	 * clean up and return the type-oid
@@ -465,8 +467,9 @@ TypeCreate(Oid newTypeOid,
 								 rebuildDeps);
 
 	/* Post creation hook for new type */
-	InvokeObjectAccessHook(OAT_POST_CREATE, TypeRelationId, typeObjectId, 0);
-
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							TypeRelationId, typeObjectId, 0,
+							PointerGetDatum(tup));
 	/*
 	 * finish up
 	 */
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index a938c98..9c1f2f9 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -226,7 +226,9 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
 										   ONCOMMIT_NOOP,
 										   reloptions,
 										   false,
-										   true);
+										   true,
+										   false,
+										   NULL);
 	Assert(toast_relid != InvalidOid);
 
 	/* make the toast relation visible, else heap_open will fail */
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 8200d20..7bd178c 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -645,7 +645,9 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace)
 										  ONCOMMIT_NOOP,
 										  reloptions,
 										  false,
-										  true);
+										  true,
+										  false,
+										  NULL);
 	Assert(OIDNewHeap != InvalidOid);
 
 	ReleaseSysCache(tuple);
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 2c05814..3b50ed2 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -515,7 +515,10 @@ createdb(const CreatedbStmt *stmt)
 	copyTemplateDependencies(src_dboid, dboid);
 
 	/* Post creation hook for new database */
-	InvokeObjectAccessHook(OAT_POST_CREATE, DatabaseRelationId, dboid, 0);
+	InvokeObjectAccessHook2(OAT_POST_CREATE,
+							DatabaseRelationId, dboid, 0,
+							PointerGetDatum(tuple),
+							ObjectIdGetDatum(src_dboid));
 
 	/*
 	 * Force a checkpoint before starting the copy. This will force dirty
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 9ffce85..4c7047c 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1525,7 +1525,6 @@ InsertExtensionTuple(const char *extName, Oid extOwner,
 	extensionOid = simple_heap_insert(rel, tuple);
 	CatalogUpdateIndexes(rel, tuple);
 
-	heap_freetuple(tuple);
 	heap_close(rel, RowExclusiveLock);
 
 	/*
@@ -1555,8 +1554,10 @@ InsertExtensionTuple(const char *extName, Oid extOwner,
 		recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL);
 	}
 	/* Post creation hook for new extension */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   ExtensionRelationId, extensionOid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							ExtensionRelationId, extensionOid, 0,
+							PointerGetDatum(tuple));
+	heap_freetuple(tuple);
 
 	return extensionOid;
 }
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index d9c27d1..0b6edd0 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -488,8 +488,6 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
 	fdwId = simple_heap_insert(rel, tuple);
 	CatalogUpdateIndexes(rel, tuple);
 
-	heap_freetuple(tuple);
-
 	/* record dependencies */
 	myself.classId = ForeignDataWrapperRelationId;
 	myself.objectId = fdwId;
@@ -517,9 +515,11 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
 	recordDependencyOnCurrentExtension(&myself, false);
 
 	/* Post creation hook for new foreign data wrapper */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   ForeignDataWrapperRelationId, fdwId, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							ForeignDataWrapperRelationId, fdwId, 0,
+							PointerGetDatum(tuple));
 
+	heap_freetuple(tuple);
 	heap_close(rel, RowExclusiveLock);
 }
 
@@ -840,8 +840,6 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
 
 	CatalogUpdateIndexes(rel, tuple);
 
-	heap_freetuple(tuple);
-
 	/* record dependencies */
 	myself.classId = ForeignServerRelationId;
 	myself.objectId = srvId;
@@ -858,8 +856,11 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
 	recordDependencyOnCurrentExtension(&myself, false);
 
 	/* Post creation hook for new foreign server */
-	InvokeObjectAccessHook(OAT_POST_CREATE, ForeignServerRelationId, srvId, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							ForeignServerRelationId, srvId, 0,
+							PointerGetDatum(tuple));
 
+	heap_freetuple(tuple);
 	heap_close(rel, RowExclusiveLock);
 }
 
@@ -1115,8 +1116,6 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
 
 	CatalogUpdateIndexes(rel, tuple);
 
-	heap_freetuple(tuple);
-
 	/* Add dependency on the server */
 	myself.classId = UserMappingRelationId;
 	myself.objectId = umId;
@@ -1137,8 +1136,11 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
 	recordDependencyOnCurrentExtension(&myself, false);
 
 	/* Post creation hook for new user mapping */
-	InvokeObjectAccessHook(OAT_POST_CREATE, UserMappingRelationId, umId, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							UserMappingRelationId, umId, 0,
+							PointerGetDatum(tuple));
 
+	heap_freetuple(tuple);
 	heap_close(rel, RowExclusiveLock);
 }
 
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 92abd44..601b034 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -1769,7 +1769,9 @@ CreateCast(CreateCastStmt *stmt)
 	recordDependencyOnCurrentExtension(&myself, false);
 
 	/* Post creation hook for new cast */
-	InvokeObjectAccessHook(OAT_POST_CREATE, CastRelationId, castid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							CastRelationId, castid, 0,
+							PointerGetDatum(tuple));
 
 	heap_freetuple(tuple);
 
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 3dbc932..61e240c 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -288,8 +288,6 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
 
 	CatalogUpdateIndexes(rel, tup);
 
-	heap_freetuple(tup);
-
 	/*
 	 * Create dependencies for the opfamily proper.  Note: we do not create a
 	 * dependency link to the AM, because we don't currently support DROP
@@ -312,9 +310,11 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
 	recordDependencyOnCurrentExtension(&myself, false);
 
 	/* Post creation hook for new operator family */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   OperatorFamilyRelationId, opfamilyoid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							OperatorFamilyRelationId, opfamilyoid, 0,
+							PointerGetDatum(tup));
 
+	heap_freetuple(tup);
 	heap_close(rel, RowExclusiveLock);
 
 	return opfamilyoid;
@@ -661,8 +661,6 @@ DefineOpClass(CreateOpClassStmt *stmt)
 
 	CatalogUpdateIndexes(rel, tup);
 
-	heap_freetuple(tup);
-
 	/*
 	 * Now add tuples to pg_amop and pg_amproc tying in the operators and
 	 * functions.  Dependencies on them are inserted, too.
@@ -715,9 +713,11 @@ DefineOpClass(CreateOpClassStmt *stmt)
 	recordDependencyOnCurrentExtension(&myself, false);
 
 	/* Post creation hook for new operator class */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   OperatorClassRelationId, opclassoid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							OperatorClassRelationId, opclassoid, 0,
+							PointerGetDatum(tup));
 
+	heap_freetuple(tup);
 	heap_close(rel, RowExclusiveLock);
 }
 
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
index 98770c5..cfb419f 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -430,8 +430,9 @@ create_proc_lang(const char *languageName, bool replace,
 	}
 
 	/* Post creation hook for new procedural language */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   LanguageRelationId, myself.objectId, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							LanguageRelationId, myself.objectId, 0,
+							PointerGetDatum(tup));
 
 	heap_close(rel, RowExclusiveLock);
 }
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 1e8ad2b..25a573d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -414,6 +414,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
 	AttrNumber	attnum;
 	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
 	Oid			ofTypeId;
+	const char *fdw_srvname = NULL;
 
 	/*
 	 * Truncate relname to appropriate length (probably a waste of time, as
@@ -576,6 +577,13 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
 	}
 
 	/*
+	 * When new relation is a foreign table, we also provide name of the
+	 * foreign server to inform it post object creation hook
+	 */
+	if (IsA(stmt, CreateForeignTableStmt))
+		fdw_srvname = ((CreateForeignTableStmt *)stmt)->servername;
+
+	/*
 	 * Create the relation.  Inherited defaults and constraints are passed in
 	 * for immediate handling --- since they don't need parsing, they can be
 	 * stored immediately.
@@ -599,7 +607,9 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
 										  stmt->oncommit,
 										  reloptions,
 										  true,
-										  allowSystemTableMods);
+										  allowSystemTableMods,
+										  false,
+										  fdw_srvname);
 
 	/* Store inheritance information for new rel. */
 	StoreCatalogInheritance(relationId, inheritOids);
@@ -4377,12 +4387,13 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	/* keep catalog indexes current */
 	CatalogUpdateIndexes(pgclass, reltup);
 
-	heap_freetuple(reltup);
-
 	/* Post creation hook for new attribute */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   RelationRelationId, myrelid, newattnum);
+	InvokeObjectAccessHook2(OAT_POST_CREATE,
+							RelationRelationId, myrelid, newattnum,
+							PointerGetDatum(reltup),
+							PointerGetDatum(&attribute));
 
+	heap_freetuple(reltup);
 	heap_close(pgclass, RowExclusiveLock);
 
 	/* Make the attribute's catalog entry visible */
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index d223f8c..17b5de3 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -325,14 +325,15 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
 
 	CatalogUpdateIndexes(rel, tuple);
 
-	heap_freetuple(tuple);
-
 	/* Record dependency on owner */
 	recordDependencyOnOwner(TableSpaceRelationId, tablespaceoid, ownerId);
 
 	/* Post creation hook for new tablespace */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   TableSpaceRelationId, tablespaceoid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							TableSpaceRelationId, tablespaceoid, 0,
+							PointerGetDatum(tuple));
+
+	heap_freetuple(tuple);
 
 	create_tablespace_directories(location, tablespaceoid);
 
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 06d368e..fbadfd1 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -133,6 +133,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
 	ScanKeyData key;
 	Relation	pgrel;
 	HeapTuple	tuple;
+	HeapTuple	reltup;
 	Oid			fargtypes[1];	/* dummy */
 	Oid			funcoid;
 	Oid			funcrettype;
@@ -629,32 +630,27 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
 
 	CatalogUpdateIndexes(tgrel, tuple);
 
-	heap_freetuple(tuple);
 	heap_close(tgrel, RowExclusiveLock);
 
-	pfree(DatumGetPointer(values[Anum_pg_trigger_tgname - 1]));
-	pfree(DatumGetPointer(values[Anum_pg_trigger_tgargs - 1]));
-	pfree(DatumGetPointer(values[Anum_pg_trigger_tgattr - 1]));
-
 	/*
 	 * Update relation's pg_class entry.  Crucial side-effect: other backends
 	 * (and this one too!) are sent SI message to make them rebuild relcache
 	 * entries.
 	 */
 	pgrel = heap_open(RelationRelationId, RowExclusiveLock);
-	tuple = SearchSysCacheCopy1(RELOID,
+	reltup = SearchSysCacheCopy1(RELOID,
 								ObjectIdGetDatum(RelationGetRelid(rel)));
-	if (!HeapTupleIsValid(tuple))
+	if (!HeapTupleIsValid(reltup))
 		elog(ERROR, "cache lookup failed for relation %u",
 			 RelationGetRelid(rel));
 
-	((Form_pg_class) GETSTRUCT(tuple))->relhastriggers = true;
+	((Form_pg_class) GETSTRUCT(reltup))->relhastriggers = true;
 
-	simple_heap_update(pgrel, &tuple->t_self, tuple);
+	simple_heap_update(pgrel, &reltup->t_self, reltup);
 
-	CatalogUpdateIndexes(pgrel, tuple);
+	CatalogUpdateIndexes(pgrel, reltup);
 
-	heap_freetuple(tuple);
+	heap_freetuple(reltup);
 	heap_close(pgrel, RowExclusiveLock);
 
 	/*
@@ -746,8 +742,17 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
 							   DEPENDENCY_NORMAL);
 
 	/* Post creation hook for new trigger */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   TriggerRelationId, trigoid, 0);
+	InvokeObjectAccessHook2(OAT_POST_CREATE,
+							TriggerRelationId, trigoid, 0,
+							PointerGetDatum(tuple),
+							BoolGetDatum(isInternal));
+
+	/* Release contents of pg_trigger tuple */
+	pfree(DatumGetPointer(values[Anum_pg_trigger_tgname - 1]));
+	pfree(DatumGetPointer(values[Anum_pg_trigger_tgargs - 1]));
+	pfree(DatumGetPointer(values[Anum_pg_trigger_tgattr - 1]));
+
+	heap_freetuple(tuple);
 
 	/* Keep lock on target rel until end of xact */
 	heap_close(rel, NoLock);
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index 16e6940..895023b 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -271,7 +271,9 @@ DefineTSParser(List *names, List *parameters)
 	makeParserDependencies(tup);
 
 	/* Post creation hook for new text search parser */
-	InvokeObjectAccessHook(OAT_POST_CREATE, TSParserRelationId, prsOid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							TSParserRelationId, prsOid, 0,
+							PointerGetDatum(tup));
 
 	heap_freetuple(tup);
 
@@ -623,8 +625,9 @@ DefineTSDictionary(List *names, List *parameters)
 	makeDictionaryDependencies(tup);
 
 	/* Post creation hook for new text search dictionary */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   TSDictionaryRelationId, dictOid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							TSDictionaryRelationId, dictOid, 0,
+							PointerGetDatum(tup));
 
 	heap_freetuple(tup);
 
@@ -1165,7 +1168,9 @@ DefineTSTemplate(List *names, List *parameters)
 	makeTSTemplateDependencies(tup);
 
 	/* Post creation hook for new text search template */
-	InvokeObjectAccessHook(OAT_POST_CREATE, TSTemplateRelationId, dictOid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							TSTemplateRelationId, dictOid, 0,
+							PointerGetDatum(tup));
 
 	heap_freetuple(tup);
 
@@ -1607,7 +1612,9 @@ DefineTSConfiguration(List *names, List *parameters)
 	makeConfigurationDependencies(tup, false, mapRel);
 
 	/* Post creation hook for new text search configuration */
-	InvokeObjectAccessHook(OAT_POST_CREATE, TSConfigRelationId, cfgOid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							TSConfigRelationId, cfgOid, 0,
+							PointerGetDatum(tup));
 
 	heap_freetuple(tup);
 
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index fa312cb..a518d15 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -434,7 +434,9 @@ CreateRole(CreateRoleStmt *stmt)
 				GetUserId(), false);
 
 	/* Post creation hook for new role */
-	InvokeObjectAccessHook(OAT_POST_CREATE, AuthIdRelationId, roleid, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							AuthIdRelationId, roleid, 0,
+							PointerGetDatum(tuple));
 
 	/*
 	 * Close pg_authid, but keep lock till commit.
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index fd7a9ed..b790fd5 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -2486,7 +2486,9 @@ OpenIntoRel(QueryDesc *queryDesc)
 											  into->onCommit,
 											  reloptions,
 											  true,
-											  allowSystemTableMods);
+											  allowSystemTableMods,
+											  true,
+											  NULL);
 	Assert(intoRelationId != InvalidOid);
 
 	/*
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 17db70e..f13c209 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -137,8 +137,6 @@ InsertRule(char *rulname,
 	/* Need to update indexes in either case */
 	CatalogUpdateIndexes(pg_rewrite_desc, tup);
 
-	heap_freetuple(tup);
-
 	/* If replacing, get rid of old dependencies and make new ones */
 	if (is_update)
 		deleteDependencyRecordsFor(RewriteRelationId, rewriteObjectId, false);
@@ -177,9 +175,11 @@ InsertRule(char *rulname,
 	}
 
 	/* Post creation hook for new rule */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   RewriteRelationId, rewriteObjectId, 0);
+	InvokeObjectAccessHook1(OAT_POST_CREATE,
+							RewriteRelationId, rewriteObjectId, 0,
+							PointerGetDatum(tup));
 
+	heap_freetuple(tup);
 	heap_close(pg_rewrite_desc, RowExclusiveLock);
 
 	return rewriteObjectId;
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c
index fed6d2e..bd6e14f 100644
--- a/src/backend/storage/large_object/inv_api.c
+++ b/src/backend/storage/large_object/inv_api.c
@@ -37,7 +37,6 @@
 #include "access/xact.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
-#include "catalog/objectaccess.h"
 #include "catalog/pg_largeobject.h"
 #include "catalog/pg_largeobject_metadata.h"
 #include "libpq/libpq-fs.h"
@@ -215,10 +214,6 @@ inv_create(Oid lobjId)
 	recordDependencyOnOwner(LargeObjectRelationId,
 							lobjId_new, GetUserId());
 
-	/* Post creation hook for new large object */
-	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   LargeObjectRelationId, lobjId_new, 0);
-
 	/*
 	 * Advance command counter to make new tuple visible to later operations.
 	 */
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index aee2d88..1cc4b5c 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -65,7 +65,9 @@ extern Oid heap_create_with_catalog(const char *relname,
 						 OnCommitAction oncommit,
 						 Datum reloptions,
 						 bool use_user_acl,
-						 bool allow_system_table_mods);
+						 bool allow_system_table_mods,
+						 bool is_select_into,
+						 const char *fdw_srvname);
 
 extern void heap_drop_with_catalog(Oid relid);
 
@@ -81,7 +83,7 @@ extern void InsertPgAttributeTuple(Relation pg_attribute_rel,
 					   Form_pg_attribute new_attribute,
 					   CatalogIndexState indstate);
 
-extern void InsertPgClassTuple(Relation pg_class_desc,
+extern HeapTuple InsertPgClassTuple(Relation pg_class_desc,
 				   Relation new_rel_desc,
 				   Oid new_rel_oid,
 				   Datum relacl,
diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h
index 2925475..3d14e79 100644
--- a/src/include/catalog/objectaccess.h
+++ b/src/include/catalog/objectaccess.h
@@ -23,7 +23,7 @@
  */
 typedef enum ObjectAccessType
 {
-	OAT_POST_CREATE,
+	OAT_POST_CREATE,	/* Post-creation of database object */
 } ObjectAccessType;
 
 /*
@@ -33,14 +33,216 @@ typedef enum ObjectAccessType
 typedef void (*object_access_hook_type) (ObjectAccessType access,
 													 Oid classId,
 													 Oid objectId,
-													 int subId);
+													 int subId,
+													 int nargs,
+													 Datum argv[]);
 
 extern PGDLLIMPORT object_access_hook_type object_access_hook;
 
-#define InvokeObjectAccessHook(access,classId,objectId,subId)			\
-	do {																\
-		if (object_access_hook)											\
-			(*object_access_hook)((access),(classId),(objectId),(subId)); \
+#define InvokeObjectAccessHook0(access,classId,objectId,subId)	\
+	do {														\
+		if (object_access_hook)									\
+			(*object_access_hook)((access),						\
+								  (classId),(objectId),(subId),	\
+								  0, NULL);						\
+	} while(0)
+
+#define InvokeObjectAccessHook1(access,classId,objectId,subId,	\
+								arg0)							\
+	do {														\
+		if (object_access_hook)									\
+		{														\
+			Datum	____temp_arg_buf[1];						\
+																\
+			____temp_arg_buf[0] = (arg0);						\
+																\
+			(*object_access_hook)((access),						\
+								  (classId),(objectId),(subId), \
+								  1, ____temp_arg_buf);			\
+		}														\
+	} while(0)
+
+#define InvokeObjectAccessHook2(access,classId,objectId,subId,	\
+								arg0, arg1)						\
+	do {														\
+		if (object_access_hook)									\
+		{														\
+			Datum	____temp_arg_buf[2];						\
+																\
+			____temp_arg_buf[0] = (arg0);						\
+			____temp_arg_buf[1] = (arg1);						\
+																\
+			(*object_access_hook)((access),						\
+								  (classId),(objectId),(subId), \
+								  2, ____temp_arg_buf);			\
+		}														\
+	} while(0)
+
+#define InvokeObjectAccessHook3(access,classId,objectId,subId,	\
+								arg0, arg1, arg2)				\
+	do {														\
+		if (object_access_hook)									\
+		{														\
+			Datum	____temp_arg_buf[3];						\
+																\
+			____temp_arg_buf[0] = (arg0);						\
+			____temp_arg_buf[1] = (arg1);						\
+			____temp_arg_buf[2] = (arg2);						\
+																\
+			(*object_access_hook)((access),						\
+								  (classId),(objectId),(subId), \
+								  3, ____temp_arg_buf);			\
+		}														\
+	} while(0)
+
+#define InvokeObjectAccessHook4(access,classId,objectId,subId,	\
+								arg0, arg1, arg2, arg3)			\
+	do {														\
+		if (object_access_hook)									\
+		{														\
+			Datum	____temp_arg_buf[4];						\
+																\
+			____temp_arg_buf[0] = (arg0);						\
+			____temp_arg_buf[1] = (arg1);						\
+			____temp_arg_buf[2] = (arg2);						\
+			____temp_arg_buf[3] = (arg3);						\
+																\
+			(*object_access_hook)((access),						\
+								  (classId),(objectId),(subId), \
+								  4, ____temp_arg_buf);			\
+		}														\
+	} while(0)
+
+#define InvokeObjectAccessHook5(access,classId,objectId,subId,	\
+								arg0, arg1, arg2, arg3, arg4)	\
+	do {														\
+		if (object_access_hook)									\
+		{														\
+			Datum	____temp_arg_buf[5];						\
+																\
+			____temp_arg_buf[0] = (arg0);						\
+			____temp_arg_buf[1] = (arg1);						\
+			____temp_arg_buf[2] = (arg2);						\
+			____temp_arg_buf[3] = (arg3);						\
+			____temp_arg_buf[4] = (arg4);						\
+																\
+			(*object_access_hook)((access),						\
+								  (classId),(objectId),(subId), \
+								  5, ____temp_arg_buf);			\
+		}														\
+	} while(0)
+
+#define InvokeObjectAccessHook6(access,classId,objectId,subId,	\
+								arg0, arg1, arg2, arg3, arg4,	\
+								arg5)							\
+	do {														\
+		if (object_access_hook)									\
+		{														\
+			Datum	____temp_arg_buf[6];						\
+																\
+			____temp_arg_buf[0] = (arg0);						\
+			____temp_arg_buf[1] = (arg1);						\
+			____temp_arg_buf[2] = (arg2);						\
+			____temp_arg_buf[3] = (arg3);						\
+			____temp_arg_buf[4] = (arg4);						\
+			____temp_arg_buf[5] = (arg5);						\
+																\
+			(*object_access_hook)((access),						\
+								  (classId),(objectId),(subId), \
+								  6, ____temp_arg_buf);			\
+		}														\
+	} while(0)
+
+#define InvokeObjectAccessHook7(access,classId,objectId,subId,	\
+								arg0, arg1, arg2, arg3, arg4,	\
+								arg5, arg6)						\
+	do {														\
+		if (object_access_hook)									\
+		{														\
+			Datum	____temp_arg_buf[7];						\
+																\
+			____temp_arg_buf[0] = (arg0);						\
+			____temp_arg_buf[1] = (arg1);						\
+			____temp_arg_buf[2] = (arg2);						\
+			____temp_arg_buf[3] = (arg3);						\
+			____temp_arg_buf[4] = (arg4);						\
+			____temp_arg_buf[5] = (arg5);						\
+			____temp_arg_buf[6] = (arg6);						\
+																\
+			(*object_access_hook)((access),						\
+								  (classId),(objectId),(subId), \
+								  7, ____temp_arg_buf);			\
+		}														\
+	} while(0)
+
+#define InvokeObjectAccessHook8(access,classId,objectId,subId,	\
+								arg0, arg1, arg2, arg3, arg4,	\
+								arg5, arg6, arg7)				\
+	do {														\
+		if (object_access_hook)									\
+		{														\
+			Datum	____temp_arg_buf[8];						\
+																\
+			____temp_arg_buf[0] = (arg0);						\
+			____temp_arg_buf[1] = (arg1);						\
+			____temp_arg_buf[2] = (arg2);						\
+			____temp_arg_buf[3] = (arg3);						\
+			____temp_arg_buf[4] = (arg4);						\
+			____temp_arg_buf[5] = (arg5);						\
+			____temp_arg_buf[6] = (arg6);						\
+			____temp_arg_buf[7] = (arg7);						\
+																\
+			(*object_access_hook)((access),						\
+								  (classId),(objectId),(subId), \
+								  8, ____temp_arg_buf);			\
+		}														\
+	} while(0)
+
+#define InvokeObjectAccessHook8(access,classId,objectId,subId,	\
+								arg0, arg1, arg2, arg3, arg4,	\
+								arg5, arg6, arg7)				\
+	do {														\
+		if (object_access_hook)									\
+		{														\
+			Datum	____temp_arg_buf[8];						\
+																\
+			____temp_arg_buf[0] = (arg0);						\
+			____temp_arg_buf[1] = (arg1);						\
+			____temp_arg_buf[2] = (arg2);						\
+			____temp_arg_buf[3] = (arg3);						\
+			____temp_arg_buf[4] = (arg4);						\
+			____temp_arg_buf[5] = (arg5);						\
+			____temp_arg_buf[6] = (arg6);						\
+			____temp_arg_buf[7] = (arg7);						\
+																\
+			(*object_access_hook)((access),						\
+								  (classId),(objectId),(subId), \
+								  8, ____temp_arg_buf);			\
+		}														\
+	} while(0)
+
+#define InvokeObjectAccessHook9(access,classId,objectId,subId,	\
+								arg0, arg1, arg2, arg3, arg4,	\
+								arg5, arg6, arg7, arg8)			\
+	do {														\
+		if (object_access_hook)									\
+		{														\
+			Datum	____temp_arg_buf[9];						\
+																\
+			____temp_arg_buf[0] = (arg0);						\
+			____temp_arg_buf[1] = (arg1);						\
+			____temp_arg_buf[2] = (arg2);						\
+			____temp_arg_buf[3] = (arg3);						\
+			____temp_arg_buf[4] = (arg4);						\
+			____temp_arg_buf[5] = (arg5);						\
+			____temp_arg_buf[6] = (arg6);						\
+			____temp_arg_buf[7] = (arg7);						\
+			____temp_arg_buf[8] = (arg8);						\
+																\
+			(*object_access_hook)((access),						\
+								  (classId),(objectId),(subId), \
+								  9, ____temp_arg_buf);			\
+		}														\
 	} while(0)
 
 #endif   /* OBJECTACCESS_H */
#3Kohei KaiGai
kaigai@kaigai.gr.jp
In reply to: Kohei KaiGai (#2)
Re: [v9.2] Object access hooks with arguments support (v1)

BTW, I remember that I was suggested the object-access-hooks to acquire
controls around changes of system catalogs are also useful to implement
clustering features, not only enhanced security features, when I had a talk
at PGcon2001.

It might be my mistake that I categorized this patch at the "security" topic.
If someone volunteers to review this patch from the different viewpoint, not
only security features, it is quite helpful for us.

Thanks,

2011/9/29 Kohei KaiGai <kaigai@kaigai.gr.jp>:

I noticed that the previous revision does not provide any way to inform
the modules name of foreign server, even if foreign table was created,
on the OAT_POST_CREATE hook.
So, I modified the invocation at heap_create_with_catalog to deliver
this information to the modules.

Rest of parts were unchanged, except for rebasing to the latest git
master.

2011/8/28 Kohei KaiGai <kaigai@kaigai.gr.jp>:

The attached patch is a draft to support arguments in addition to
OAT_* enum and object identifiers.

The existing object_access_hook enables loadable modules to acquire
control when objects are referenced. The first guest of this hook is
contrib/sepgsql for assignment of default security label on newly
created objects. Right now, OAT_POST_CREATE is the all supported
object access type. However, we plan to enhance this mechanism onto
other widespread purpose; such as comprehensive DDL permissions
supported by loadable modules.

This patch is a groundwork to utilize this hook for object creation
permission checks, not only default labeling.
At the v9.1 development cycle, I proposed an idea that defines both
OAT_CREATE hook prior to system catalog modification and
OAT_POST_CREATE hook as currently we have. This design enables to
check permission next to the existing pg_xxx_aclcheck() or
pg_xxx_ownercheck(), and raise an error before system catalog updates.
However, it was painful to deliver private datum set on OAT_CREATE to
the OAT_POST_CREATE due to the code complexity.

The other idea tried to do all the necessary things in OAT_POST_CREATE
hook, and it had been merged, because loadable modules can pull
properties of the new object from system catalogs by the supplied
object identifiers. Thus, contrib/sepgsql assigns a default security
label on new object using OAT_POST_CREATE hook.
However, I have two concern on the existing hook to implement
permission check for object creation. The first one is the entry of
system catalog is not visible using SnaphotNow, so we had to open and
scan system catalog again, instead of syscache mechanism. The second
one is more significant. A part of information to make access control
decision is not available within contents of the system catalog
entries. For example, we hope to skip permission check when
heap_create_with_catalog() was launched by make_new_heap() because the
new relation is just used to swap later.

Thus, I'd like to propose to support argument of object_access_hook to
inform the loadable modules additional contextual information on its
invocations; to solve these concerns.

Regarding to the first concern, fortunately, most of existing
OAT_POST_CREATE hook is deployed just after insert or update of system
catalogs, but before CCI. So, it is helpful for the loadable modules
to deliver Form_pg_xxxx data to reference properties of the new
object, instead of open and scan the catalog again.
In the draft patch, I enhanced OAT_POST_CREATE hook commonly to take
an argument that points to the Form_pg_xxxx data of the new object.

Regarding to the second concern, I added a few contextual information
as second or later arguments in a part of object classes. Right now, I
hope the following contextual information shall be provided to
OAT_POST_CREATE hook to implement permission checks of object
creation.

* pg_class - TupleDesc structure of the new relation
I want to reference of pg_attribute, not only pg_class.

* pg_class - A flag to show whether the relation is defined for
rebuilding, or not.
I want not to apply permission check in the case when it is invoked
from make_new_heap(), because it just create a dummy table as a part
of internal process. All the necessary permission checks should be
done at ALTER TABLE or CLUSTER.

And, name of the foreign server being used by the foreign table
being created just a moment before.

* pg_class - A flag to show whether the relation is created with
SELECT INTO, or not.
I want to check "insert" permission of the new table, created by
SELECT INTO, because DML hook is not available to check this case.

* pg_type - A flag to show whether the type is defined as implicit
array, or not.
I want not to apply permission check on creation of implicit array type.

* pg_database - Oid of the source (template) database.
I want to fetch security label of the source database to compute a
default label of the new database.

* pg_trigger - A flag to show whether the trigger is used to FK
constraint, or not.
I want not to apply permission check on creation of FK constraint. It
should be done in ALTER TABLE level.

Sorry for this long explanation. Right now, I tend to consider it is
the best way to implement permission checks on object creation with
least invasive way.

Thanks, Any comments welcome.
--
KaiGai Kohei <kaigai@kaigai.gr.jp>

--
KaiGai Kohei <kaigai@kaigai.gr.jp>

--
KaiGai Kohei <kaigai@kaigai.gr.jp>

#4Robert Haas
robertmhaas@gmail.com
In reply to: Kohei KaiGai (#2)
Re: [v9.2] Object access hooks with arguments support (v1)

On Thu, Sep 29, 2011 at 4:52 PM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:

I noticed that the previous revision does not provide any way to inform
the modules name of foreign server, even if foreign table was created,
on the OAT_POST_CREATE hook.
So, I modified the invocation at heap_create_with_catalog to deliver
this information to the modules.

Rest of parts were unchanged, except for rebasing to the latest git
master.

I've never really been totally sanguine with the idea of making object
access hooks take arguments, and all of my general concerns seem to
apply to the way you've set this patch up. In particular:

1. Type safety goes out the window. What you're essentially proposing
here is that we should have one hook function can be used for almost
anything at all and can be getting up to 9 arguments of any type
whatsoever. The hook will need to know how to interpret all of its
arguments depending on the context in which it was called. The
compiler will be totally unable to do any static type-checking, so
there will be absolutely no warning if the interface is changed
incompatibly on either side. Instead, you'll probably just crash the
database server at runtime.

2. The particular choice of data being passed to the object access
hooks appears capricious and arbitrary. Here's an example:

        /* Post creation hook for new relation */
-       InvokeObjectAccessHook(OAT_POST_CREATE, RelationRelationId, relid, 0);
+       InvokeObjectAccessHook4(OAT_POST_CREATE,
+
RelationRelationId, relid, 0,
+
PointerGetDatum(new_rel_tup),
+
PointerGetDatum(tupdesc),
+
BoolGetDatum(is_select_into),
+
CStringGetDatum(fdw_srvname));

Now, I am sure that you have good reasons for wanting to pass those
particular things to the object access hook rather than any other
local variable or argument that might happen to be lying around at
this point in the code, but they are not documented. If someone adds
a new argument to this function, or removes an argument that's being
passed, they will have no idea what to do about this. Moreover, if
you did document it, I think it would boil down to "this is what
sepgsql happens to need", and I don't think that's an acceptable
answer. We have repeatedly refused to adopt that approach in the
past.

(This particular case is worse than average, because new_rel_tup is
coming from InsertPgClassTuple, which therefore has to be modified,
along with AddNewRelationTuple and index_create, to get the tuple back
up to the call site where, apparently, it is needed.)

I am not exactly sure what the right way to solve this problem is, but
I don't think this is it.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#5Kohei KaiGai
kaigai@kaigai.gr.jp
In reply to: Robert Haas (#4)
Re: [v9.2] Object access hooks with arguments support (v1)

Robert,

I agree with it is a reasonable argument that compiler cannot raise warnings
if all the arguments are delivered as Datum. In fact, I also tried to implement
this feature with InvokeObjectAccessHook() defined as function.

The first needed point to be improved is that we hope compiler to raise
warnnings if and when number or type of arguments are changed in the
future version.
If so, how about an idea to define data types to inform modules the context
information in, such as:

struct ObjectAccessInfoData {
ObjectAccessType oa_type;
ObjectAddress oa_address;
union {
struct {
HeapTuple newtuple;
TupleDesc tupdesc; /* only if create a new relation */
:
} post_create; /* additional info for OAT_POST_CREATE event */
}
}

Even if its invocation shall be wrapped up by macro, compiler can
raise warnings when caller set values on the ObjectAccessInfoData
structure.

It will also help the second points partially. The objectaccess.h will perform
a placeholder to describe specification of the argument. That clearly informs
developers what informations are available on the hook and what was lacked.

Moreover, if
you did document it, I think it would boil down to "this is what
sepgsql happens to need", and I don't think that's an acceptable
answer. ?We have repeatedly refused to adopt that approach in the
past.

Unfortunately, I still don't have a clear answer for this point.
However, in general terms, it is impossible to design any interface without
knowledge of its usage more or less.

We have several other hooks being available to loadable modules,
but I don't believe that we designed these hooks without knowledge
of use-cases, more or less.

At least, this proposition enables modules being informed using
commonly used data type (such as HeapTuple, TupleDesc), compared
to the past approach that tried to push all the necessary information
into argument list individually.

I think the answer of this matter is on the middle-of-position between
"we should not know anything about sepgsql" and "we should know
everything about sepgsql", neither of them....

(This particular case is worse than average, because new_rel_tup is
coming from InsertPgClassTuple, which therefore has to be modified,
along with AddNewRelationTuple and index_create, to get the tuple back
up to the call site where, apparently, it is needed.)

It might be a wrong design. All we want to inform was stored within
new_rel_desc in heap_create_with_catalog(). So, I should design the
hook to deliver new_rel_desc, instead of HeapTuple itself that need to
pull up from InsertPgClassTuple.

Thanks,

2011/10/12 Robert Haas <robertmhaas@gmail.com>:

On Thu, Sep 29, 2011 at 4:52 PM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:

I noticed that the previous revision does not provide any way to inform
the modules name of foreign server, even if foreign table was created,
on the OAT_POST_CREATE hook.
So, I modified the invocation at heap_create_with_catalog to deliver
this information to the modules.

Rest of parts were unchanged, except for rebasing to the latest git
master.

I've never really been totally sanguine with the idea of making object
access hooks take arguments, and all of my general concerns seem to
apply to the way you've set this patch up.  In particular:

1. Type safety goes out the window.  What you're essentially proposing
here is that we should have one hook function can be used for almost
anything at all and can be getting up to 9 arguments of any type
whatsoever.  The hook will need to know how to interpret all of its
arguments depending on the context in which it was called.  The
compiler will be totally unable to do any static type-checking, so
there will be absolutely no warning if the interface is changed
incompatibly on either side.  Instead, you'll probably just crash the
database server at runtime.

2. The particular choice of data being passed to the object access
hooks appears capricious and arbitrary.  Here's an example:

       /* Post creation hook for new relation */
-       InvokeObjectAccessHook(OAT_POST_CREATE, RelationRelationId, relid, 0);
+       InvokeObjectAccessHook4(OAT_POST_CREATE,
+
RelationRelationId, relid, 0,
+
PointerGetDatum(new_rel_tup),
+
PointerGetDatum(tupdesc),
+
BoolGetDatum(is_select_into),
+
CStringGetDatum(fdw_srvname));

Now, I am sure that you have good reasons for wanting to pass those
particular things to the object access hook rather than any other
local variable or argument that might happen to be lying around at
this point in the code, but they are not documented.  If someone adds
a new argument to this function, or removes an argument that's being
passed, they will have no idea what to do about this.  Moreover, if
you did document it, I think it would boil down to "this is what
sepgsql happens to need", and I don't think that's an acceptable
answer.  We have repeatedly refused to adopt that approach in the
past.

(This particular case is worse than average, because new_rel_tup is
coming from InsertPgClassTuple, which therefore has to be modified,
along with AddNewRelationTuple and index_create, to get the tuple back
up to the call site where, apparently, it is needed.)

I am not exactly sure what the right way to solve this problem is, but
I don't think this is it.

--
KaiGai Kohei <kaigai@kaigai.gr.jp>

#6Robert Haas
robertmhaas@gmail.com
In reply to: Kohei KaiGai (#5)
Re: [v9.2] Object access hooks with arguments support (v1)

On Thu, Oct 13, 2011 at 6:48 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:

   struct ObjectAccessInfoData {
       ObjectAccessType   oa_type;
       ObjectAddress         oa_address;
       union {
           struct {
               HeapTuple       newtuple;
               TupleDesc       tupdesc;  /* only if create a new relation */
                   :
           } post_create;      /* additional info for OAT_POST_CREATE event */
       }
   }

That's possibly an improvement over just passing everything opaquely,
but it still doesn't seem very good. I mean, this is C, not Perl or
Python. Anything where you pass a bunch of arguments of indeterminate
type and hope that the person you're calling can figure it out is
inherently pretty dangerous. I had it in mind that the object access
hook mechanism could serve as a simple and generic way of letting an
external module know that an event of a certain type has occurred on a
certain object, and to let that module gain control. But if we have
to pass a raft of arguments in, then it's not generic any more - it's
just a bunch of things that are probably really need to be separate
hooks shoved into a single function.

Moreover, if
you did document it, I think it would boil down to "this is what
sepgsql happens to need", and I don't think that's an acceptable
answer. ?We have repeatedly refused to adopt that approach in the
past.

Unfortunately, I still don't have a clear answer for this point.
However, in general terms, it is impossible to design any interface without
knowledge of its usage more or less.

Well, that's true. But the hook also has to be maintainable. ISTM
that there's no reasonable way for someone making a modification to
the code to know whether the additional local variable that they just
added to the function should be passed to the hook, or not. Here, at
least as far as I can see, there's no guiding principle that would
enable future contributors to make an intelligent decision about that.
And if someone wanted to write another client for the hook, it's not
very obvious whether the particular things you've chosen to pass here
would be relevant or not. I think if you look over the code you'll
find that there's nowhere else that we have a hook that looks anything
like what you're proposing here.

At least, this proposition enables modules being informed using
commonly used data type (such as HeapTuple, TupleDesc), compared
to the past approach that tried to push all the necessary information
into argument list individually.

That does seem like a good direction to go in, but you're still
passing a lot of other stuff in there. I guess my feeling is that if
we can't boil down the argument list to a short list of things that
are more-or-less common to all the call sites, we probably need to
rethink the whole design.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#7Kohei KaiGai
kaigai@kaigai.gr.jp
In reply to: Robert Haas (#6)
Re: [v9.2] Object access hooks with arguments support (v1)

2011/10/18 Robert Haas <robertmhaas@gmail.com>:

On Thu, Oct 13, 2011 at 6:48 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:

   struct ObjectAccessInfoData {
       ObjectAccessType   oa_type;
       ObjectAddress         oa_address;
       union {
           struct {
               HeapTuple       newtuple;
               TupleDesc       tupdesc;  /* only if create a new relation */
                   :
           } post_create;      /* additional info for OAT_POST_CREATE event */
       }
   }

That's possibly an improvement over just passing everything opaquely,
but it still doesn't seem very good.  I mean, this is C, not Perl or
Python.  Anything where you pass a bunch of arguments of indeterminate
type and hope that the person you're calling can figure it out is
inherently pretty dangerous.  I had it in mind that the object access
hook mechanism could serve as a simple and generic way of letting an
external module know that an event of a certain type has occurred on a
certain object, and to let that module gain control.  But if we have
to pass a raft of arguments in, then it's not generic any more - it's
just a bunch of things that are probably really need to be separate
hooks shoved into a single function.

I got an inspiration of this idea from APIs of foreign-data-wrapper that
trusts values of each fields that contains function pointers.
Indeed, we may have a risk of backend crash, if we try to run modules
built towards v9.1 on the pgsql-v9.2devel. However, it is not avoidable,
unless we rewrite whole of the code by Java?, Python? or something? :-(

At least, we don't promise binary compatible across major versions?
If so, I don't think dangerousness is a reasonable reason why the hook
does not take any additional arguments, as long as auther of modules
can be noticed by compiler warnings.

The point to be focused on is how do we implement the target feature
(DDL permissions by sepgsql in my case) with simpleness.

Moreover, if
you did document it, I think it would boil down to "this is what
sepgsql happens to need", and I don't think that's an acceptable
answer. ?We have repeatedly refused to adopt that approach in the
past.

Unfortunately, I still don't have a clear answer for this point.
However, in general terms, it is impossible to design any interface without
knowledge of its usage more or less.

Well, that's true.  But the hook also has to be maintainable.  ISTM
that there's no reasonable way for someone making a modification to
the code to know whether the additional local variable that they just
added to the function should be passed to the hook, or not.  Here, at
least as far as I can see, there's no guiding principle that would
enable future contributors to make an intelligent decision about that.
 And if someone wanted to write another client for the hook, it's not
very obvious whether the particular things you've chosen to pass here
would be relevant or not.  I think if you look over the code you'll
find that there's nowhere else that we have a hook that looks anything
like what you're proposing here.

In the case when the second client of the hook is proposed, a straight-
forward approach is to expand the above ObjectAccessInfoData to
satisfy the requirement of new one, if existing arguments are not enough.
Because existing modules ignore the new fields, it is harmless.

Linux adopts this approch to host multiple security modules without
confusion, although one makes decision by security label and one
other makes decision by pathname of files; they require different
information on the point to make its decision, because they ignore
unrelevant arguments; such as pathname in selinux.

I remember you worry about the number of arguments getting increased
infinity, however, it is an extreme situation. We are not an A.I to commit
proposed patches automatically, so auther of modules (including me)
will explain why the new arguments are additionally needed here, like as
pathname based security module did in Linux kernel development.

At least, this proposition enables modules being informed using
commonly used data type (such as HeapTuple, TupleDesc), compared
to the past approach that tried to push all the necessary information
into argument list individually.

That does seem like a good direction to go in, but you're still
passing a lot of other stuff in there.  I guess my feeling is that if
we can't boil down the argument list to a short list of things that
are more-or-less common to all the call sites, we probably need to
rethink the whole design.

Honestly, the arguments of object_access_hook is a method to implement
security model of sepgsql, not a purpose of mine. So, an alternative idea is
quite welcome to implement sepgsql.
However, I think it is less invasive way than other ideas right now.

For example, I hope sepgsql to perform as follows when user create a new table.
- It computes a default security label that needs Oid of the namespace.
- It checks db_table:{create} permission on the security label being computed.
- In addition, it checks db_table:{insert} permission when SELECT INTO
- Also, it checks these permissions on columns being newly created.
- However, It does not check permissions when the tables are internally created,
such as toast tables or temporary relations created by make_new_heap().

Although table creation is the most complex case, it allows us to implement
with just three additional aguments. I tried to implement to deploy separate
hooks on the code path on DefineRelation and OpenIntoRel, but I didn't feel
it is enough simple than the latest approach.

Unfortunately, I have no idea to find out these "contextual information" being
commonly used for all the object classes, so I tried to define my example
ObjectAccessInfoData structure with optional fields depending on event
type and object class.

Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>

#8Robert Haas
robertmhaas@gmail.com
In reply to: Kohei KaiGai (#7)
Re: [v9.2] Object access hooks with arguments support (v1)

On Tue, Oct 18, 2011 at 11:25 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:

For example, I hope sepgsql to perform as follows when user create a new table.
- It computes a default security label that needs Oid of the namespace.
- It checks db_table:{create} permission on the security label being computed.
- In addition, it checks db_table:{insert} permission when SELECT INTO
- Also, it checks these permissions on columns being newly created.
- However, It does not check permissions when the tables are internally created,
 such as toast tables or temporary relations created by make_new_heap().

That's superficially reasonable, but I don't feel good about passing
is_select_info to the object access hook here. If insert permission
is required there, then it ought to be getting checked by selinux at
the same place that it's getting checked for at the DAC level. If we
get into a situation where sepgsql is checking permissions using some
system that's totally different from what we do for DAC, I think
that's going to produce confusing and illogical results. This is
ground we've been over before. Similarly with fdw_srvname. The only
excuse for passing that is to handle a permissions check for foreign
data wrappers that isn't being done for DAC: if there is a DAC
permissions check, then the MAC one should be done there also, not
someplace totally different.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#9Kohei KaiGai
kaigai@kaigai.gr.jp
In reply to: Robert Haas (#8)
Re: [v9.2] Object access hooks with arguments support (v1)

2011/10/18 Robert Haas <robertmhaas@gmail.com>:

On Tue, Oct 18, 2011 at 11:25 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:

For example, I hope sepgsql to perform as follows when user create a new table.
- It computes a default security label that needs Oid of the namespace.
- It checks db_table:{create} permission on the security label being computed.
- In addition, it checks db_table:{insert} permission when SELECT INTO
- Also, it checks these permissions on columns being newly created.
- However, It does not check permissions when the tables are internally created,
 such as toast tables or temporary relations created by make_new_heap().

That's superficially reasonable, but I don't feel good about passing
is_select_info to the object access hook here.  If insert permission
is required there, then it ought to be getting checked by selinux at
the same place that it's getting checked for at the DAC level.  If we
get into a situation where sepgsql is checking permissions using some
system that's totally different from what we do for DAC, I think
that's going to produce confusing and illogical results.  This is
ground we've been over before.  Similarly with fdw_srvname.  The only
excuse for passing that is to handle a permissions check for foreign
data wrappers that isn't being done for DAC: if there is a DAC
permissions check, then the MAC one should be done there also, not
someplace totally different.

If you are suggesting DAC and MAC permissions should be checked
on the same place like as we already doing at ExecCheckRTPerms(),
I'd like to agree with the suggestion, rather than all the checks within
object_access_hook, although it will take code reworking around
access controls.

In the example table creation, heap_create_with_catalog() is invoked
by 5 routines, however, 3 of them are just internal usages, so it is not
preferable to apply permission checks on table creation....

If we may have a hook on table creation next to the place currently
we checks permission on the namespace being created, it is quite
helpful to implement sepgsql.

BTW, it seems to me SELECT INTO should also check insert permission
on DAC level, because it become an incorrect assumption that owner of
table has insert permission on its creation time.

postgres=# ALTER DEFAULT PRIVILEGES FOR USER tak GRANT select ON TABLES TO tak;
ALTER DEFAULT PRIVILEGES
postgres=# \ddp
Default access privileges
Owner | Schema | Type | Access privileges
-------+--------+-------+-------------------
tak | | table | tak=r/tak
(1 row)

postgres=# SET SESSION AUTHORIZATION tak;
SET
postgres=> SELECT * INTO t1 FROM (VALUES(1,'aaa'), (2,'bbb'), (3,'ccc')) AS v;
SELECT 3
postgres=> SELECT * FROM t1;
column1 | column2
---------+---------
1 | aaa
2 | bbb
3 | ccc
(3 rows)

postgres=> INSERT INTO t1 VALUES (4,'ddd');
ERROR: permission denied for relation t1

Oops!
--
KaiGai Kohei <kaigai@kaigai.gr.jp>

#10Robert Haas
robertmhaas@gmail.com
In reply to: Kohei KaiGai (#9)
Re: [v9.2] Object access hooks with arguments support (v1)

On Tue, Oct 18, 2011 at 1:23 PM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:

If you are suggesting DAC and MAC permissions should be checked
on the same place like as we already doing at ExecCheckRTPerms(),
I'd like to agree with the suggestion, rather than all the checks within
object_access_hook, although it will take code reworking around
access controls.

Agreed.

In the example table creation, heap_create_with_catalog() is invoked
by 5 routines, however, 3 of them are just internal usages, so it is not
preferable to apply permission checks on table creation....

Some wit once made the remark that if a function has 10 arguments, it
has 11 arguments, meaning that functions with very large numbers of
arguments typically indicate a poor choice of abstraction boundary.
That's pretty much what I think is going on with
heap_create_with_catalog(). I think maybe some refactoring is in
order there, though I am not sure quite what.

If we may have a hook on table creation next to the place currently
we checks permission on the namespace being created, it is quite
helpful to implement sepgsql.

Makes sense.

BTW, it seems to me SELECT INTO should also check insert permission
on DAC level, because it become an incorrect assumption that owner of
table has insert permission on its creation time.

postgres=# ALTER DEFAULT PRIVILEGES FOR USER tak GRANT select ON TABLES TO tak;
ALTER DEFAULT PRIVILEGES
postgres=# \ddp
        Default access privileges
 Owner | Schema | Type  | Access privileges
-------+--------+-------+-------------------
 tak   |        | table | tak=r/tak
(1 row)

postgres=# SET SESSION AUTHORIZATION tak;
SET
postgres=> SELECT * INTO t1 FROM (VALUES(1,'aaa'), (2,'bbb'), (3,'ccc')) AS v;
SELECT 3
postgres=> SELECT * FROM t1;
 column1 | column2
---------+---------
      1 | aaa
      2 | bbb
      3 | ccc
(3 rows)

postgres=> INSERT INTO t1 VALUES (4,'ddd');
ERROR:  permission denied for relation t1

Oops!

You could make the argument that there's no real security hole there,
because the user could give himself those same privileges right back.
You could also make the argument that a SELECT .. INTO is not the same
as an INSERT and therefore INSERT privileges shouldn't be checked. I
think there are valid arguments on both sides, but my main point is
that we shouldn't have core do it one way and sepgsql do it the other
way. That's going to be impossible to maintain and doesn't really
make any logical sense anyway.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#11Kohei KaiGai
kaigai@kaigai.gr.jp
In reply to: Robert Haas (#10)
Re: [v9.2] Object access hooks with arguments support (v1)

2011/10/18 Robert Haas <robertmhaas@gmail.com>:

In the example table creation, heap_create_with_catalog() is invoked
by 5 routines, however, 3 of them are just internal usages, so it is not
preferable to apply permission checks on table creation....

Some wit once made the remark that if a function has 10 arguments, it
has 11 arguments, meaning that functions with very large numbers of
arguments typically indicate a poor choice of abstraction boundary.
That's pretty much what I think is going on with
heap_create_with_catalog().  I think maybe some refactoring is in
order there, though I am not sure quite what.

Sorry, are you complained about the number of arguments in
heap_create_with_catalog? or hooks of security checks?

If we try to rework access control logic around DefineRelation and
OpenIntoRel to host both of DAC and MAC, it will takes a long
arguments list because an entry of pg_class catalog is not
constructed at the timing, so we need to inform the routine
all the parameters of new relation required to both DAC and MAC.
Thus, it takes a long argument list, however, number of arguments
of heap_create_with_catalog will not increased so much. (Maybe,
it additionally requires a private data to deliver security labels to be
assigned on the new relation.)

If we relocate logics of DAC into heap_create_with_catalog(), it will
need additional two arguments, though it has 19 arguments right now.
It is not a strange idea to inform routines that modified catalogs
whether this context needs permission checks, or not, because
CreateTrigger has a flag of "isInternal" to skip permission check.

BTW, it seems to me SELECT INTO should also check insert permission
on DAC level, because it become an incorrect assumption that owner of
table has insert permission on its creation time.

:

You could make the argument that there's no real security hole there,
because the user could give himself those same privileges right back.
You could also make the argument that a SELECT .. INTO is not the same
as an INSERT and therefore INSERT privileges shouldn't be checked.  I
think there are valid arguments on both sides, but my main point is
that we shouldn't have core do it one way and sepgsql do it the other
way.  That's going to be impossible to maintain and doesn't really
make any logical sense anyway.

My point is that we should minimize the code complexity, redundancy
or others between DAC and MAC implementation, however, it is not
possible to get their different to zero due to the differences of their
standpoint.

Yes, I never say SELECT INTO without DAC checks cause actual
security hole, because owner can change its access permissions by
himself, unlike MAC.
Please suppose that there are two security labels: confidential table
(TC) and public table (PT). Typical MAC policy (including selinux)
does not allow users who can read from tables with TC to write out
data into tables with PT, because PT is readable for public as literal,
so confidential data may be leaked to public domain in the result.

It is a significant characteristic of MAC; called as data-flow-control.
So, it damages significant part of its worth, if MAC could not distinct
CREATE TABLE from SELECT INTO in PostgreSQL, and it is the
reason why I strongly requires a flag of contextual information here.

Although you say it is not possible to maintain, doesn't the above
introduction help us to understand nothing why MAC needs to
distinct SELECT INTO from CREATE TABLE?, and why it needs
a flag to distinct two cases?

Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>

#12Robert Haas
robertmhaas@gmail.com
In reply to: Kohei KaiGai (#11)
Re: [v9.2] Object access hooks with arguments support (v1)

On Wed, Oct 19, 2011 at 6:18 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:

2011/10/18 Robert Haas <robertmhaas@gmail.com>:

In the example table creation, heap_create_with_catalog() is invoked
by 5 routines, however, 3 of them are just internal usages, so it is not
preferable to apply permission checks on table creation....

Some wit once made the remark that if a function has 10 arguments, it
has 11 arguments, meaning that functions with very large numbers of
arguments typically indicate a poor choice of abstraction boundary.
That's pretty much what I think is going on with
heap_create_with_catalog().  I think maybe some refactoring is in
order there, though I am not sure quite what.

Sorry, are you complained about the number of arguments in
heap_create_with_catalog? or hooks of security checks?

I'm just saying that heap_create_with_catalog() is big and complex and
does a lot of different things. The fact it does security checks even
though it's also sometimes called as an internal function strikes me
as a modularity violation. Normally I would expect to have a
high-level routine (which is invoked more or less directly from SQL)
that does security checks and then calls a low-level routine (that
actually does the work) if they pass. Other parts of the code that
want to perform the same operation without the security checks can
call the low-level function directly. But that's not the way it's set
up here.

Yes, I never say SELECT INTO without DAC checks cause actual
security hole, because owner can change its access permissions by
himself, unlike MAC.
Please suppose that there are two security labels: confidential table
(TC) and public table (PT). Typical MAC policy (including selinux)
does not allow users who can read from tables with TC to write out
data into tables with PT, because PT is readable for public as literal,
so confidential data may be leaked to public domain in the result.

It is a significant characteristic of MAC; called as data-flow-control.
So, it damages significant part of its worth, if MAC could not distinct
CREATE TABLE from SELECT INTO in PostgreSQL, and it is the
reason why I strongly requires a flag of contextual information here.

Although you say it is not possible to maintain, doesn't the above
introduction help us to understand nothing why MAC needs to
distinct SELECT INTO from CREATE TABLE?, and why it needs
a flag to distinct two cases?

Sure. But what is going to happen when someone needs to modify that
code in a year or two? In order to understand what to do with the
object access hook, they're going to need to understand all those
details you just mentioned. And those details do not exist in the
patch as submitted. Worse, let's suppose we'd committed a patch like
the one you have here before foreign tables went in. Then, whoever
added foreign tables would have needed to know to add the foreign
table server name to the object access hook invocation, because
apparently sepgsql needs that. How would they know they needed to do
that? I've committed practically all of the sepgsql-related patches,
and I would not have known that, so it seems likely to me that other
people adding new functionality to the server wouldn't know it either.

When someone comes along in another year or two and adds materialized
views, will they need to pass some additional data to the object
access hook? Probably, but I bet you're the only one who can quickly
figure out what it is. That's no good. We're not going to make
changes to PostgreSQL core that only you can maintain, and that are
likely to be broken by future commits. At this point I feel pretty
good that someone can look at the stuff that we've done so far with
SECURITY LABEL and the object access hooks, and if they add a new
object type, they can make those things apply to the new object type
too by copying what's already there, without making any reference to
the sepgsql code. There's a clear abstraction boundary, and people
who are working on one side of the boundary do not need to understand
the details of what happens on the other side of the boundary.

In this particular case, I think it might be reasonable to change the
DAC behavior, so that a CREATE TABLE AS SELECT or SELECT INTO requires
insert privileges on the new table as well as permission to create it
in the first place. I don't particularly see any reason to require
different privileges for CREATE TABLE followed by INSERT .. SELECT
than what we require when the two commands are rolled into one. Prior
to 9.0, this case couldn't arise, because we didn't have default
privileges, so I'm inclined to think that the way it works now is a
historical accident rather than a deliberate design decision.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#13Kohei KaiGai
kaigai@kaigai.gr.jp
In reply to: Robert Haas (#12)
Re: [v9.2] Object access hooks with arguments support (v1)

When someone comes along in another year or two and adds materialized
views, will they need to pass some additional data to the object
access hook?  Probably, but I bet you're the only one who can quickly
figure out what it is.  That's no good.  We're not going to make
changes to PostgreSQL core that only you can maintain, and that are
likely to be broken by future commits.  At this point I feel pretty
good that someone can look at the stuff that we've done so far with
SECURITY LABEL and the object access hooks, and if they add a new
object type, they can make those things apply to the new object type
too by copying what's already there, without making any reference to
the sepgsql code.  There's a clear abstraction boundary, and people
who are working on one side of the boundary do not need to understand
the details of what happens on the other side of the boundary.

I had checked my older implementation based on 8.4.x or 9.0.x that
includes all the features that I want to implement.
At least, it does not require so much different information from ones
needed by DAC model, although SELECT INTO was an exception.
It might be quite natural because both works similar things.

For example, sepgsql required Oid of source database to compute
default security label on new database at createdb(). It was used to
permission checks in DAC model also.
For another example, sepgsql also required Oids of type-functions
to check permissions on them at DefineType(). It was also used to
DAC model except for these checks were commented out by
#ifdef NOT_USED because of superuser() was already checked.

So, how do we launch this efforts according to the principles:
- Hooks being used to security checks also should be deployed
around existing DAC checks.
- The delivered arguments should not be model specific.

I don't have clear idea to rework existing routines like as I proposed
long before; that wrap-up a series of DAC checks and entrypoint of
MAC hooks into a single function.

A straightforward idea is to deploy object-access-hook around existing
DAC checks with new OAT_* label, such as OAT_CREATE.
In the case of relation creation, it shall be DefineRelation() and OpenIntoRel()
to bypass "internal" invocation of heap_create_with_catalog().

Please tell me if we have different idea of code reworking to simplify
deployment of the hooks.

In this particular case, I think it might be reasonable to change the
DAC behavior, so that a CREATE TABLE AS SELECT or SELECT INTO requires
insert privileges on the new table as well as permission to create it
in the first place.  I don't particularly see any reason to require
different privileges for CREATE TABLE followed by INSERT .. SELECT
than what we require when the two commands are rolled into one.  Prior
to 9.0, this case couldn't arise, because we didn't have default
privileges, so I'm inclined to think that the way it works now is a
historical accident rather than a deliberate design decision.

It will help this mismatch between DAC and MAC.
I'll submit it as a separate patch to handle this behavior.
Probably, all we need to do here is invocation of ExecCheckRTPerms().

Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>

#14Robert Haas
robertmhaas@gmail.com
In reply to: Kohei KaiGai (#13)
Re: [v9.2] Object access hooks with arguments support (v1)

On Fri, Oct 21, 2011 at 12:44 PM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:

I had checked my older implementation based on 8.4.x or 9.0.x that
includes all the features that I want to implement.
At least, it does not require so much different information from ones
needed by DAC model, although SELECT INTO was an exception.
It might be quite natural because both works similar things.

OK. It seems like it might be helpful to put together a list of all
of the things you want to check permissions on, maybe on a wiki page
somewhere, and indicate in there which ones are done and which ones
are not done, and what additional information you think is needed in
each case, and flag any MAC/DAC discrepancies that you are concerned
about. I think that might help us reach agreement on the best way
forward. Also, then, as we get things committed, we can track
progress versus that roadmap.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#15Kohei KaiGai
kaigai@kaigai.gr.jp
In reply to: Robert Haas (#14)
Re: [v9.2] Object access hooks with arguments support (v1)

2011/10/21 Robert Haas <robertmhaas@gmail.com>:

On Fri, Oct 21, 2011 at 12:44 PM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:

I had checked my older implementation based on 8.4.x or 9.0.x that
includes all the features that I want to implement.
At least, it does not require so much different information from ones
needed by DAC model, although SELECT INTO was an exception.
It might be quite natural because both works similar things.

OK.  It seems like it might be helpful to put together a list of all
of the things you want to check permissions on, maybe on a wiki page
somewhere, and indicate in there which ones are done and which ones
are not done, and what additional information you think is needed in
each case, and flag any MAC/DAC discrepancies that you are concerned
about.  I think that might help us reach agreement on the best way
forward.  Also, then, as we get things committed, we can track
progress versus that roadmap.

I tried to summarize permission checks of DAC/MAC on several object classes
that are allowed to assign security label right now.
http://wiki.postgresql.org/index.php?title=SEPostgreSQL/Permissions

In most of checks, required contextual information by SELinux are commonly
used to DAC also, as listed.

On the creation of relations, SELinux requires relkind and TupleDesc to
identify relation type and columns. Unlike a flag of select-into, I'm not sure
whether it is a model specific and hard to manage without knowledge about
sepgsql internal.

In some cases, DAC uses superuser privilege as a substitute for individual
permission checks, such as DefineType().
It seems to me its original implementation checked CREATE permission of
the namespace and ownership of the functions that implements type input,
output or others, then, we commented out these code because superuser
is obviously allowed these checks.
However, MAC does not have concept of superuser, so, SELinux wants to
check db_procedure:{install} for each functions, thus, it requires oid of
the functions to be used for the new type.
Of course, we can see here is no difference between DAC and MAC, if we
consider DAC implicitly allows these checks by superuser().

I think it is a good start to implement first ddl permissions on creation of
the seven object classes as listed; also to proof the concept; 1) we put
permission checks around existing DAC checks, 2) we deliver contextual
data (basically) used in existing DAC also.

I guess DROP or some of ALTER code reworking should be done prior to
deploy object_access_hook around their permission checks, to minimize
maintain efforts.

Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>

#16Robert Haas
robertmhaas@gmail.com
In reply to: Kohei KaiGai (#15)
Re: [v9.2] Object access hooks with arguments support (v1)

On Tue, Nov 1, 2011 at 1:32 PM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:

I tried to summarize permission checks of DAC/MAC on several object classes
that are allowed to assign security label right now.
http://wiki.postgresql.org/index.php?title=SEPostgreSQL/Permissions

In most of checks, required contextual information by SELinux are commonly
used to DAC also, as listed.

What's up with this:

"a flag to inform whether CASCADE or RESTRICT"

That doesn't seem like it should be needed.

We should consider whether CREATE TABLE should be considered to
consist of creating a table and then n attributes, rather than trying
to shove the attribute information wholesale into the create table
check.

I guess DROP or some of ALTER code reworking should be done prior to
deploy object_access_hook around their permission checks, to minimize
maintain efforts.

+1.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#17Kohei KaiGai
kaigai@kaigai.gr.jp
In reply to: Robert Haas (#16)
Re: [v9.2] Object access hooks with arguments support (v1)

2011/11/1 Robert Haas <robertmhaas@gmail.com>:

On Tue, Nov 1, 2011 at 1:32 PM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:

I tried to summarize permission checks of DAC/MAC on several object classes
that are allowed to assign security label right now.
http://wiki.postgresql.org/index.php?title=SEPostgreSQL/Permissions

In most of checks, required contextual information by SELinux are commonly
used to DAC also, as listed.

What's up with this:

"a flag to inform whether CASCADE or RESTRICT"

That doesn't seem like it should be needed.

Ah, it is needed to determine the scope of objects to be deleted.
If a table being referenced by views, deletion of this table with cascade
involves the views, even if these are owned by others.
DAC does not check permissions to drop on depending objects,
so, it is a headache for me.

However, the worth of drop permission checks on involved objects
is not perfectly clear, because the user is allowed to drop the table
being reference by views at least in above example. If so, views to
reference non-existent table is nonsense.
I'm not 100% sure why existing DAC does not check permissions
on depending objects even if these are owned by other users.
I'd like to know the reason of this behavior.

We should consider whether CREATE TABLE should be considered to
consist of creating a table and then n attributes, rather than trying
to shove the attribute information wholesale into the create table
check.

I don't see tangible difference between them except for simpleness of
implementation. One point we should consider is the timing to store
security label of table and columns.

If creation checks of table/columns are consolidated into one hook,
we can check permissions to create them and write-back default
security labels of them into one private datum to be delivered to
post-creation hook.

If creation checks of table/columns are separated into individual
invocation, the later column-checks needs security label of the
new table as contextual information, however, it requires us to
know internals of sepgsql why it needs table's label to check
permission to create a column, because both of checks shall
be done DefineRelation / OpenIntoRel prior to heap_create_with_catalog
that invokes post-creation hook.

So, I think it works better with the approach that also delivers
TupleDesc on creation of relation checks, rather than separated
invocations.

Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>

#18Kohei KaiGai
kaigai@kaigai.gr.jp
In reply to: Kohei KaiGai (#17)
1 attachment(s)
Re: [v9.2] Object access hooks with arguments support (v1)

I'm under works on the prep-object-creation hook with contextual
arguments that are
commonly used to existing DAC checks.

One heahache of mine is that some object classes takes long distance to collect
all the information being necessary on creation check; such as DefineType().
In this function, it checks superuser privilege (and CREATE on the namespace
implicitly), then it makes a shell type or a base type after name
resolve of type
handlers (that also checks ownership of functions implicitly). It
means the place
where we can put a prep-creation hook is restricted to the point next to all the
oid of type-handlers are looked-up, however, it is too late to apply checks
before TypeShellMake().

I hope to make clear the matter again. The reason why we didn't want to put
permission check on OAT_POST_CREATE hook is that it takes random
flags (like "is_select_into") on its argument to avoid unnecessary permission
checks.

If sepgsql would apply permission checks db_procedure:{install} on the
OAT_POST_CREATE hook based on the funcion-oid within new entry of
system catalog, we can relocate OAT_PREP_CREATE hook more conceptually
right place, such as just after the pg_namespace_aclcheck() of DefineType().
On the other hand, we may need to answer why these information are NOT
delivered on the OAT_PREP_CREATE hook without knowledge of sepgsql
internal.

Please some ideas.

Thanks,

2011/11/1 Kohei KaiGai <kaigai@kaigai.gr.jp>:

2011/11/1 Robert Haas <robertmhaas@gmail.com>:

On Tue, Nov 1, 2011 at 1:32 PM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:

I tried to summarize permission checks of DAC/MAC on several object classes
that are allowed to assign security label right now.
http://wiki.postgresql.org/index.php?title=SEPostgreSQL/Permissions

In most of checks, required contextual information by SELinux are commonly
used to DAC also, as listed.

What's up with this:

"a flag to inform whether CASCADE or RESTRICT"

That doesn't seem like it should be needed.

Ah, it is needed to determine the scope of objects to be deleted.
If a table being referenced by views, deletion of this table with cascade
involves the views, even if these are owned by others.
DAC does not check permissions to drop on depending objects,
so, it is a headache for me.

However, the worth of drop permission checks on involved objects
is not perfectly clear, because the user is allowed to drop the table
being reference by views at least in above example. If so, views to
reference non-existent table is nonsense.
I'm not 100% sure why existing DAC does not check permissions
on depending objects even if these are owned by other users.
I'd like to know the reason of this behavior.

We should consider whether CREATE TABLE should be considered to
consist of creating a table and then n attributes, rather than trying
to shove the attribute information wholesale into the create table
check.

I don't see tangible difference between them except for simpleness of
implementation. One point we should consider is the timing to store
security label of table and columns.

If creation checks of table/columns are consolidated into one hook,
we can check permissions to create them and write-back default
security labels of them into one private datum to be delivered to
post-creation hook.

If creation checks of table/columns are separated into individual
invocation, the later column-checks needs security label of the
new table as contextual information, however, it requires us to
know internals of sepgsql why it needs table's label to check
permission to create a column, because both of checks shall
be done DefineRelation / OpenIntoRel prior to heap_create_with_catalog
that invokes post-creation hook.

So, I think it works better with the approach that also delivers
TupleDesc on creation of relation checks, rather than separated
invocations.

Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>

--
KaiGai Kohei <kaigai@kaigai.gr.jp>

Attachments:

psql-v9.2-prep-create-hook.v0.patchapplication/octet-stream; name=psql-v9.2-prep-create-hook.v0.patchDownload
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index d0a0e92..fc4970d 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -248,7 +248,8 @@ Boot_CreateStmt:
 													  ONCOMMIT_NOOP,
 													  (Datum) 0,
 													  false,
-													  true);
+													  true,
+													  0);
 						elog(DEBUG4, "relation created with OID %u", id);
 					}
 					do_end();
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index e11d896..1a6a0bc 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -89,7 +89,8 @@ static Oid AddNewRelationType(const char *typeName,
 				   char new_rel_kind,
 				   Oid ownerid,
 				   Oid new_row_type,
-				   Oid new_array_type);
+				   Oid new_array_type,
+				   Datum hook_private);
 static void RelationRemoveInheritance(Oid relid);
 static void StoreRelCheck(Relation rel, char *ccname, Node *expr,
 			  bool is_validated, bool is_local, int inhcount);
@@ -910,7 +911,8 @@ AddNewRelationType(const char *typeName,
 				   char new_rel_kind,
 				   Oid ownerid,
 				   Oid new_row_type,
-				   Oid new_array_type)
+				   Oid new_array_type,
+				   Datum hook_private)
 {
 	return
 		TypeCreate(new_row_type,	/* optional predetermined OID */
@@ -943,7 +945,8 @@ AddNewRelationType(const char *typeName,
 				   -1,			/* typmod */
 				   0,			/* array dimensions for typBaseType */
 				   false,		/* Type NOT NULL */
-				   InvalidOid); /* rowtypes never have a collation */
+				   InvalidOid,	/* rowtypes never have a collation */
+				   hook_private);	/* opaque of object_access_hook */
 }
 
 /* --------------------------------
@@ -993,7 +996,8 @@ heap_create_with_catalog(const char *relname,
 						 OnCommitAction oncommit,
 						 Datum reloptions,
 						 bool use_user_acl,
-						 bool allow_system_table_mods)
+						 bool allow_system_table_mods,
+						 Datum hook_private)
 {
 	Relation	pg_class_desc;
 	Relation	new_rel_desc;
@@ -1154,7 +1158,8 @@ heap_create_with_catalog(const char *relname,
 									  relkind,
 									  ownerid,
 									  reltypeid,
-									  new_array_oid);
+									  new_array_oid,
+									  hook_private);
 
 	/*
 	 * Now make the array type if wanted.
@@ -1195,7 +1200,8 @@ heap_create_with_catalog(const char *relname,
 				   -1,			/* typmod */
 				   0,			/* array dimensions for typBaseType */
 				   false,		/* Type NOT NULL */
-				   InvalidOid); /* rowtypes never have a collation */
+				   InvalidOid,	/* rowtypes never have a collation */
+				   hook_private);	/* opaque of the object_access_hook */
 
 		pfree(relarrayname);
 	}
@@ -1279,7 +1285,8 @@ heap_create_with_catalog(const char *relname,
 	}
 
 	/* Post creation hook for new relation */
-	InvokeObjectAccessHook(OAT_POST_CREATE, RelationRelationId, relid, 0);
+	InvokeObjectAccessHook(OAT_POST_CREATE,
+						   RelationRelationId, relid, 0, hook_private);
 
 	/*
 	 * Store any supplied constraints and defaults.
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index fcc90fe..acbce90 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -21,6 +21,7 @@
 
 #include "access/xact.h"
 #include "catalog/dependency.h"
+#include "catalog/objectaccess.h"
 #include "catalog/pg_authid.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_conversion.h"
@@ -3384,9 +3385,12 @@ InitTempTableNamespace(void)
 	char		namespaceName[NAMEDATALEN];
 	Oid			namespaceId;
 	Oid			toastspaceId;
+	Datum		hook_private = 0;
 
 	Assert(!OidIsValid(myTempNamespace));
 
+	snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId);
+
 	/*
 	 * First, do permission check to see if we are authorized to make temp
 	 * tables.	We use a nonstandard error message here since "databasename:
@@ -3404,6 +3408,10 @@ InitTempTableNamespace(void)
 				 errmsg("permission denied to create temporary tables in database \"%s\"",
 						get_database_name(MyDatabaseId))));
 
+	/* Prep-creation hook for new temp-schema */
+	InvokeObjectPrepCreateHook(NamespaceRelationId,
+							   &hook_private, namespaceName);
+
 	/*
 	 * Do not allow a Hot Standby slave session to make temp tables.  Aside
 	 * from problems with modifying the system catalogs, there is a naming
@@ -3419,8 +3427,6 @@ InitTempTableNamespace(void)
 				(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
 				 errmsg("cannot create temporary tables during recovery")));
 
-	snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId);
-
 	namespaceId = get_namespace_oid(namespaceName, true);
 	if (!OidIsValid(namespaceId))
 	{
@@ -3432,7 +3438,9 @@ InitTempTableNamespace(void)
 		 * temp tables.  This works because the places that access the temp
 		 * namespace for my own backend skip permissions checks on it.
 		 */
-		namespaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID);
+		namespaceId = NamespaceCreate(namespaceName,
+									  BOOTSTRAP_SUPERUSERID,
+									  hook_private);
 		/* Advance command counter to make namespace visible */
 		CommandCounterIncrement();
 	}
@@ -3456,7 +3464,9 @@ InitTempTableNamespace(void)
 	toastspaceId = get_namespace_oid(namespaceName, true);
 	if (!OidIsValid(toastspaceId))
 	{
-		toastspaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID);
+		toastspaceId = NamespaceCreate(namespaceName,
+									   BOOTSTRAP_SUPERUSERID,
+									   hook_private);
 		/* Advance command counter to make namespace visible */
 		CommandCounterIncrement();
 	}
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index 86e8c6b..38b661d 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -50,7 +50,8 @@ AggregateCreate(const char *aggName,
 				List *aggfinalfnName,
 				List *aggsortopName,
 				Oid aggTransType,
-				const char *agginitval)
+				const char *agginitval,
+				Datum hook_private)
 {
 	Relation	aggdesc;
 	HeapTuple	tup;
@@ -229,7 +230,8 @@ AggregateCreate(const char *aggName,
 							  NIL,		/* parameterDefaults */
 							  PointerGetDatum(NULL),	/* proconfig */
 							  1,	/* procost */
-							  0);		/* prorows */
+							  0,	/* prorows */
+							  hook_private);	/* opaque of hook */
 
 	/*
 	 * Okay to create the pg_aggregate entry.
diff --git a/src/backend/catalog/pg_collation.c b/src/backend/catalog/pg_collation.c
index ce9bfae..1cdc006 100644
--- a/src/backend/catalog/pg_collation.c
+++ b/src/backend/catalog/pg_collation.c
@@ -136,7 +136,7 @@ CollationCreate(const char *collname, Oid collnamespace,
 
 	/* Post creation hook for new collation */
 	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   CollationRelationId, oid, 0);
+						   CollationRelationId, oid, 0, 0);
 
 	heap_freetuple(tup);
 	heap_close(rel, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 6997994..8be45b6 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -364,7 +364,8 @@ CreateConstraintEntry(const char *constraintName,
 	}
 
 	/* Post creation hook for new constraint */
-	InvokeObjectAccessHook(OAT_POST_CREATE, ConstraintRelationId, conOid, 0);
+	InvokeObjectAccessHook(OAT_POST_CREATE,
+						   ConstraintRelationId, conOid, 0, 0);
 
 	return conOid;
 }
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index e480aeb..ce87f06 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -135,7 +135,7 @@ ConversionCreate(const char *conname, Oid connamespace,
 
 	/* Post creation hook for new conversion */
 	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   ConversionRelationId, HeapTupleGetOid(tup), 0);
+						   ConversionRelationId, HeapTupleGetOid(tup), 0, 0);
 
 	heap_freetuple(tup);
 	heap_close(rel, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_namespace.c b/src/backend/catalog/pg_namespace.c
index ceebac2..5d76ecf 100644
--- a/src/backend/catalog/pg_namespace.c
+++ b/src/backend/catalog/pg_namespace.c
@@ -29,7 +29,7 @@
  * ---------------
  */
 Oid
-NamespaceCreate(const char *nspName, Oid ownerId)
+NamespaceCreate(const char *nspName, Oid ownerId, Datum hook_private)
 {
 	Relation	nspdesc;
 	HeapTuple	tup;
@@ -86,7 +86,8 @@ NamespaceCreate(const char *nspName, Oid ownerId)
 	recordDependencyOnCurrentExtension(&myself, false);
 
 	/* Post creation hook for new schema */
-	InvokeObjectAccessHook(OAT_POST_CREATE, NamespaceRelationId, nspoid, 0);
+	InvokeObjectAccessHook(OAT_POST_CREATE,
+						   NamespaceRelationId, nspoid, 0, hook_private);
 
 	return nspoid;
 }
diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c
index 1440464..98e3921 100644
--- a/src/backend/catalog/pg_operator.c
+++ b/src/backend/catalog/pg_operator.c
@@ -275,7 +275,7 @@ OperatorShellMake(const char *operatorName,
 
 	/* Post creation hook for new shell operator */
 	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   OperatorRelationId, operatorObjectId, 0);
+						   OperatorRelationId, operatorObjectId, 0, 0);
 
 	/*
 	 * Make sure the tuple is visible for subsequent lookups/updates.
@@ -544,7 +544,7 @@ OperatorCreate(const char *operatorName,
 
 	/* Post creation hook for new operator */
 	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   OperatorRelationId, operatorObjectId, 0);
+						   OperatorRelationId, operatorObjectId, 0, 0);
 
 	heap_close(pg_operator_desc, RowExclusiveLock);
 
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 8378c36..f7866fc 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -85,7 +85,8 @@ ProcedureCreate(const char *procedureName,
 				List *parameterDefaults,
 				Datum proconfig,
 				float4 procost,
-				float4 prorows)
+				float4 prorows,
+				Datum hook_private)
 {
 	Oid			retval;
 	int			parameterCount;
@@ -646,7 +647,8 @@ ProcedureCreate(const char *procedureName,
 	heap_freetuple(tup);
 
 	/* Post creation hook for new function */
-	InvokeObjectAccessHook(OAT_POST_CREATE, ProcedureRelationId, retval, 0);
+	InvokeObjectAccessHook(OAT_POST_CREATE,
+						   ProcedureRelationId, retval, 0, hook_private);
 
 	heap_close(rel, RowExclusiveLock);
 
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 21d1ef3..cd7afa3 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -51,7 +51,8 @@ Oid			binary_upgrade_next_pg_type_oid = InvalidOid;
  * ----------------------------------------------------------------
  */
 Oid
-TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
+TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId,
+			  Datum hook_private)
 {
 	Relation	pg_type_desc;
 	TupleDesc	tupDesc;
@@ -161,7 +162,8 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
 								 false);
 
 	/* Post creation hook for new shell type */
-	InvokeObjectAccessHook(OAT_POST_CREATE, TypeRelationId, typoid, 0);
+	InvokeObjectAccessHook(OAT_POST_CREATE,
+						   TypeRelationId, typoid, 0, hook_private);
 
 	/*
 	 * clean up and return the type-oid
@@ -213,7 +215,8 @@ TypeCreate(Oid newTypeOid,
 		   int32 typeMod,
 		   int32 typNDims,		/* Array dimensions for baseType */
 		   bool typeNotNull,
-		   Oid typeCollation)
+		   Oid typeCollation,
+		   Datum hook_private)
 {
 	Relation	pg_type_desc;
 	Oid			typeObjectId;
@@ -465,7 +468,8 @@ TypeCreate(Oid newTypeOid,
 								 rebuildDeps);
 
 	/* Post creation hook for new type */
-	InvokeObjectAccessHook(OAT_POST_CREATE, TypeRelationId, typeObjectId, 0);
+	InvokeObjectAccessHook(OAT_POST_CREATE,
+						   TypeRelationId, typeObjectId, 0, hook_private);
 
 	/*
 	 * finish up
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 3a40e8b..148b100 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -228,7 +228,8 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
 										   ONCOMMIT_NOOP,
 										   reloptions,
 										   false,
-										   true);
+										   true,
+										   0);
 	Assert(toast_relid != InvalidOid);
 
 	/* make the toast relation visible, else heap_open will fail */
diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c
index a2122c1..35a622f 100644
--- a/src/backend/commands/aggregatecmds.c
+++ b/src/backend/commands/aggregatecmds.c
@@ -25,7 +25,9 @@
 #include "access/heapam.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
 #include "catalog/pg_aggregate.h"
+#include "catalog/pg_language.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
@@ -51,6 +53,7 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
 	char	   *aggName;
 	Oid			aggNamespace;
 	AclResult	aclresult;
+	Datum		hook_private = 0;
 	List	   *transfuncName = NIL;
 	List	   *finalfuncName = NIL;
 	List	   *sortoperatorName = NIL;
@@ -192,6 +195,11 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
 							format_type_be(transTypeId))));
 	}
 
+	/* Prep-creation hook for new aggregate function */
+	InvokeProcedurePrepCreateHook(&hook_private, aggName,
+								  buildoidvector(aggArgTypes, numArgs),
+								  aggNamespace, INTERNALlanguageId, false);
+
 	/*
 	 * Most of the argument-checking is done inside of AggregateCreate
 	 */
@@ -203,7 +211,8 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
 					finalfuncName,		/* final function name */
 					sortoperatorName,	/* sort operator name */
 					transTypeId,	/* transition data type */
-					initval);	/* initial condition */
+					initval,		/* initial condition */
+					hook_private);	/* opaque of object_access_hook */
 }
 
 
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index edec44d..93e7177 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -645,7 +645,8 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace)
 										  ONCOMMIT_NOOP,
 										  reloptions,
 										  false,
-										  true);
+										  true,
+										  0);
 	Assert(OIDNewHeap != InvalidOid);
 
 	ReleaseSysCache(tuple);
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 4551db7..58a1fd6 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -127,6 +127,7 @@ createdb(const CreatedbStmt *stmt)
 	int			dbconnlimit = -1;
 	int			notherbackends;
 	int			npreparedxacts;
+	Datum		hook_private = 0;
 	createdb_failure_params fparms;
 
 	/* Extract options from the statement node tree */
@@ -424,6 +425,10 @@ createdb(const CreatedbStmt *stmt)
 		/* Note there is no additional permission check in this path */
 	}
 
+	/* Prep-creation hook for new database */
+	InvokeDatabasePrepCreateHook(&hook_private, dbname,
+								 src_dboid, dst_deftablespace);
+
 	/*
 	 * Check for db name conflict.	This is just to give a more friendly error
 	 * message than "unique index violation".  There's a race condition but
@@ -515,7 +520,8 @@ createdb(const CreatedbStmt *stmt)
 	copyTemplateDependencies(src_dboid, dboid);
 
 	/* Post creation hook for new database */
-	InvokeObjectAccessHook(OAT_POST_CREATE, DatabaseRelationId, dboid, 0);
+	InvokeObjectAccessHook(OAT_POST_CREATE,
+						   DatabaseRelationId, dboid, 0, hook_private);
 
 	/*
 	 * Force a checkpoint before starting the copy. This will force dirty
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index c334ca9..253141b 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1558,7 +1558,7 @@ InsertExtensionTuple(const char *extName, Oid extOwner,
 	}
 	/* Post creation hook for new extension */
 	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   ExtensionRelationId, extensionOid, 0);
+						   ExtensionRelationId, extensionOid, 0, 0);
 
 	return extensionOid;
 }
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index d9c27d1..60aba65 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -518,7 +518,7 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
 
 	/* Post creation hook for new foreign data wrapper */
 	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   ForeignDataWrapperRelationId, fdwId, 0);
+						   ForeignDataWrapperRelationId, fdwId, 0, 0);
 
 	heap_close(rel, RowExclusiveLock);
 }
@@ -858,7 +858,8 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
 	recordDependencyOnCurrentExtension(&myself, false);
 
 	/* Post creation hook for new foreign server */
-	InvokeObjectAccessHook(OAT_POST_CREATE, ForeignServerRelationId, srvId, 0);
+	InvokeObjectAccessHook(OAT_POST_CREATE,
+						   ForeignServerRelationId, srvId, 0, 0);
 
 	heap_close(rel, RowExclusiveLock);
 }
@@ -1137,7 +1138,8 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
 	recordDependencyOnCurrentExtension(&myself, false);
 
 	/* Post creation hook for new user mapping */
-	InvokeObjectAccessHook(OAT_POST_CREATE, UserMappingRelationId, umId, 0);
+	InvokeObjectAccessHook(OAT_POST_CREATE,
+						   UserMappingRelationId, umId, 0, 0);
 
 	heap_close(rel, RowExclusiveLock);
 }
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 92abd44..8c65025 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -113,6 +113,7 @@ compute_return_type(TypeName *returnType, Oid languageOid,
 		char	   *typnam = TypeNameToString(returnType);
 		Oid			namespaceId;
 		AclResult	aclresult;
+		Datum		hook_private = 0;
 		char	   *typname;
 
 		/*
@@ -146,7 +147,11 @@ compute_return_type(TypeName *returnType, Oid languageOid,
 		if (aclresult != ACLCHECK_OK)
 			aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
 						   get_namespace_name(namespaceId));
-		rettype = TypeShellMake(typname, namespaceId, GetUserId());
+		/* Prep-creation hook for new range type */
+		InvokeSchemaObjectPrepCreateHook(TypeRelationId, &hook_private,
+										 typname, namespaceId);
+		rettype = TypeShellMake(typname, namespaceId,
+								GetUserId(), hook_private);
 		Assert(OidIsValid(rettype));
 	}
 
@@ -798,6 +803,7 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
 	ArrayType  *proconfig;
 	float4		procost;
 	float4		prorows;
+	Datum		hook_private = 0;
 	HeapTuple	languageTuple;
 	Form_pg_language languageStruct;
 	List	   *as_clause;
@@ -877,6 +883,10 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
 						   &parameterDefaults,
 						   &requiredResultType);
 
+	/* Prep-creation hook for new function */
+	InvokeProcedurePrepCreateHook(&hook_private, funcname, parameterTypes,
+								  namespaceId, languageOid, false);
+
 	if (stmt->returnType)
 	{
 		/* explicit RETURNS clause */
@@ -960,7 +970,8 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
 					parameterDefaults,
 					PointerGetDatum(proconfig),
 					procost,
-					prorows);
+					prorows,
+					hook_private);
 }
 
 
@@ -1769,7 +1780,7 @@ CreateCast(CreateCastStmt *stmt)
 	recordDependencyOnCurrentExtension(&myself, false);
 
 	/* Post creation hook for new cast */
-	InvokeObjectAccessHook(OAT_POST_CREATE, CastRelationId, castid, 0);
+	InvokeObjectAccessHook(OAT_POST_CREATE, CastRelationId, castid, 0, 0);
 
 	heap_freetuple(tuple);
 
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index af0de05..08ab307 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -313,7 +313,7 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
 
 	/* Post creation hook for new operator family */
 	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   OperatorFamilyRelationId, opfamilyoid, 0);
+						   OperatorFamilyRelationId, opfamilyoid, 0, 0);
 
 	heap_close(rel, RowExclusiveLock);
 
@@ -716,7 +716,7 @@ DefineOpClass(CreateOpClassStmt *stmt)
 
 	/* Post creation hook for new operator class */
 	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   OperatorClassRelationId, opclassoid, 0);
+						   OperatorClassRelationId, opclassoid, 0, 0);
 
 	heap_close(rel, RowExclusiveLock);
 }
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
index 98770c5..10758aa 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -51,7 +51,7 @@ typedef struct
 
 static void create_proc_lang(const char *languageName, bool replace,
 				 Oid languageOwner, Oid handlerOid, Oid inlineOid,
-				 Oid valOid, bool trusted);
+				 Oid valOid, bool trusted, Datum hook_private);
 static PLTemplate *find_language_template(const char *languageName);
 static void AlterLanguageOwner_internal(HeapTuple tup, Relation rel,
 							Oid newOwnerId);
@@ -71,6 +71,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 				valOid;
 	Oid			funcrettype;
 	Oid			funcargtypes[1];
+	Datum		hook_private = 0;
 
 	/*
 	 * Translate the language name to lower case
@@ -107,6 +108,10 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 							   get_database_name(MyDatabaseId));
 		}
 
+		/* Prep-creation hook for new language */
+		InvokeObjectPrepCreateHook(LanguageRelationId,
+								   &hook_private, languageName);
+
 		/*
 		 * Find or create the handler function, which we force to be in the
 		 * pg_catalog schema.  If already present, it must have the correct
@@ -146,7 +151,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 										 NIL,
 										 PointerGetDatum(NULL),
 										 1,
-										 0);
+										 0,
+										 hook_private);
 		}
 
 		/*
@@ -181,7 +187,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 											NIL,
 											PointerGetDatum(NULL),
 											1,
-											0);
+											0,
+											hook_private);
 			}
 		}
 		else
@@ -219,7 +226,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 										 NIL,
 										 PointerGetDatum(NULL),
 										 1,
-										 0);
+										 0,
+										 hook_private);
 			}
 		}
 		else
@@ -228,7 +236,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 		/* ok, create it */
 		create_proc_lang(languageName, stmt->replace, GetUserId(),
 						 handlerOid, inlineOid,
-						 valOid, pltemplate->tmpltrusted);
+						 valOid, pltemplate->tmpltrusted, hook_private);
 	}
 	else
 	{
@@ -252,6 +260,10 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 					 errmsg("must be superuser to create custom procedural language")));
 
+		/* Prep-creation hook for new language */
+		InvokeObjectPrepCreateHook(LanguageRelationId,
+								   &hook_private, languageName);
+
 		/*
 		 * Lookup the PL handler function and check that it is of the expected
 		 * return type
@@ -303,7 +315,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 		/* ok, create it */
 		create_proc_lang(languageName, stmt->replace, GetUserId(),
 						 handlerOid, inlineOid,
-						 valOid, stmt->pltrusted);
+						 valOid, stmt->pltrusted, hook_private);
 	}
 }
 
@@ -313,7 +325,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 static void
 create_proc_lang(const char *languageName, bool replace,
 				 Oid languageOwner, Oid handlerOid, Oid inlineOid,
-				 Oid valOid, bool trusted)
+				 Oid valOid, bool trusted, Datum hook_private)
 {
 	Relation	rel;
 	TupleDesc	tupDesc;
@@ -431,7 +443,8 @@ create_proc_lang(const char *languageName, bool replace,
 
 	/* Post creation hook for new procedural language */
 	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   LanguageRelationId, myself.objectId, 0);
+						   LanguageRelationId, myself.objectId, 0,
+						   hook_private);
 
 	heap_close(rel, RowExclusiveLock);
 }
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
index 8daa9d0..a59aa68 100644
--- a/src/backend/commands/schemacmds.c
+++ b/src/backend/commands/schemacmds.c
@@ -19,6 +19,7 @@
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
 #include "catalog/pg_namespace.h"
 #include "commands/dbcommands.h"
 #include "commands/schemacmds.h"
@@ -48,6 +49,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
 	Oid			owner_uid;
 	Oid			saved_uid;
 	int			save_sec_context;
+	Datum		hook_private = 0;
 	AclResult	aclresult;
 
 	GetUserIdAndSecContext(&saved_uid, &save_sec_context);
@@ -74,6 +76,10 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
 
 	check_is_member_of_role(saved_uid, owner_uid);
 
+	/* Prep-creation hook for new schema */
+	InvokeObjectPrepCreateHook(NamespaceRelationId,
+							   &hook_private, schemaName);
+
 	/* Additional check to protect reserved schema names */
 	if (!allowSystemTableMods && IsReservedName(schemaName))
 		ereport(ERROR,
@@ -94,7 +100,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
 							save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
 
 	/* Create the schema's namespace */
-	namespaceId = NamespaceCreate(schemaName, owner_uid);
+	namespaceId = NamespaceCreate(schemaName, owner_uid, hook_private);
 
 	/* Advance cmd counter to make the namespace visible */
 	CommandCounterIncrement();
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index c4622c0..a3053d6 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -413,6 +413,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
 	ListCell   *listptr;
 	AttrNumber	attnum;
 	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+	Datum		hook_private = 0;
 	Oid			ofTypeId;
 
 	/*
@@ -575,6 +576,10 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
 		}
 	}
 
+	/* Prep-creation hook for new relation */
+	InvokeRelationPrepCreateHook(&hook_private, relname, relkind,
+								 namespaceId, tablespaceId, descriptor);
+
 	/*
 	 * Create the relation.  Inherited defaults and constraints are passed in
 	 * for immediate handling --- since they don't need parsing, they can be
@@ -599,7 +604,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
 										  stmt->oncommit,
 										  reloptions,
 										  true,
-										  allowSystemTableMods);
+										  allowSystemTableMods,
+										  hook_private);
 
 	/* Store inheritance information for new rel. */
 	StoreCatalogInheritance(relationId, inheritOids);
@@ -4381,7 +4387,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 
 	/* Post creation hook for new attribute */
 	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   RelationRelationId, myrelid, newattnum);
+						   RelationRelationId, myrelid, newattnum, 0);
 
 	heap_close(pgclass, RowExclusiveLock);
 
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index d223f8c..b0672e1 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -332,7 +332,7 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
 
 	/* Post creation hook for new tablespace */
 	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   TableSpaceRelationId, tablespaceoid, 0);
+						   TableSpaceRelationId, tablespaceoid, 0, 0);
 
 	create_tablespace_directories(location, tablespaceoid);
 
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index a6e7268..daeb0a1 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -747,7 +747,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
 
 	/* Post creation hook for new trigger */
 	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   TriggerRelationId, trigoid, 0);
+						   TriggerRelationId, trigoid, 0, 0);
 
 	/* Keep lock on target rel until end of xact */
 	heap_close(rel, NoLock);
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index 5f206d8..2fb7304 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -271,7 +271,8 @@ DefineTSParser(List *names, List *parameters)
 	makeParserDependencies(tup);
 
 	/* Post creation hook for new text search parser */
-	InvokeObjectAccessHook(OAT_POST_CREATE, TSParserRelationId, prsOid, 0);
+	InvokeObjectAccessHook(OAT_POST_CREATE,
+						   TSParserRelationId, prsOid, 0, 0);
 
 	heap_freetuple(tup);
 
@@ -565,7 +566,7 @@ DefineTSDictionary(List *names, List *parameters)
 
 	/* Post creation hook for new text search dictionary */
 	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   TSDictionaryRelationId, dictOid, 0);
+						   TSDictionaryRelationId, dictOid, 0, 0);
 
 	heap_freetuple(tup);
 
@@ -1036,7 +1037,8 @@ DefineTSTemplate(List *names, List *parameters)
 	makeTSTemplateDependencies(tup);
 
 	/* Post creation hook for new text search template */
-	InvokeObjectAccessHook(OAT_POST_CREATE, TSTemplateRelationId, dictOid, 0);
+	InvokeObjectAccessHook(OAT_POST_CREATE,
+						   TSTemplateRelationId, dictOid, 0, 0);
 
 	heap_freetuple(tup);
 
@@ -1419,7 +1421,8 @@ DefineTSConfiguration(List *names, List *parameters)
 	makeConfigurationDependencies(tup, false, mapRel);
 
 	/* Post creation hook for new text search configuration */
-	InvokeObjectAccessHook(OAT_POST_CREATE, TSConfigRelationId, cfgOid, 0);
+	InvokeObjectAccessHook(OAT_POST_CREATE,
+						   TSConfigRelationId, cfgOid, 0, 0);
 
 	heap_freetuple(tup);
 
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 91488bb..f91cc02 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -38,6 +38,7 @@
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_depend.h"
@@ -104,7 +105,7 @@ static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
 					int typMod, Constraint *constr,
 					char *domainName);
 static void makeRangeConstructor(char *name, Oid namespace, Oid rettype,
-								 Oid subtype);
+								 Oid subtype, Datum hook_private);
 
 
 /*
@@ -162,6 +163,7 @@ DefineType(List *names, List *parameters)
 	Oid			array_oid;
 	Oid			typoid;
 	Oid			resulttype;
+	Datum		hook_private = 0;
 	ListCell   *pl;
 
 	/*
@@ -192,6 +194,11 @@ DefineType(List *names, List *parameters)
 					   get_namespace_name(typeNamespace));
 #endif
 
+	/* Prep-creation hook for new type */
+	InvokeSchemaObjectPrepCreateHook(TypeRelationId,
+									 &hook_private,
+									 typeName, typeNamespace);
+
 	/*
 	 * Look to see if type already exists (presumably as a shell; if not,
 	 * TypeCreate will complain).
@@ -216,7 +223,8 @@ DefineType(List *names, List *parameters)
 	 */
 	if (!OidIsValid(typoid))
 	{
-		typoid = TypeShellMake(typeName, typeNamespace, GetUserId());
+		typoid = TypeShellMake(typeName, typeNamespace,
+							   GetUserId(), hook_private);
 		/* Make new shell type visible for modification below */
 		CommandCounterIncrement();
 
@@ -583,7 +591,8 @@ DefineType(List *names, List *parameters)
 				   -1,			/* typMod (Domains only) */
 				   0,			/* Array Dimensions of typbasetype */
 				   false,		/* Type NOT NULL */
-				   collation);	/* type's collation */
+				   collation,	/* type's collation */
+				   hook_private);	/* opaque to post-creation hook */
 
 	/*
 	 * Create the array type that goes with it.
@@ -623,7 +632,8 @@ DefineType(List *names, List *parameters)
 			   -1,				/* typMod (Domains only) */
 			   0,				/* Array dimensions of typbasetype */
 			   false,			/* Type NOT NULL */
-			   collation);		/* type's collation */
+			   collation,		/* type's collation */
+			   hook_private);	/* opaque to post-creation hook */
 
 	pfree(array_type);
 }
@@ -677,6 +687,7 @@ DefineDomain(CreateDomainStmt *stmt)
 	char	   *domainName;
 	Oid			domainNamespace;
 	AclResult	aclresult;
+	Datum		hook_private = 0;
 	int16		internalLength;
 	Oid			inputProcedure;
 	Oid			outputProcedure;
@@ -719,6 +730,10 @@ DefineDomain(CreateDomainStmt *stmt)
 		aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
 					   get_namespace_name(domainNamespace));
 
+	/* Prep-creation hook for new domain */
+	InvokeSchemaObjectPrepCreateHook(TypeRelationId, &hook_private,
+									 domainName, domainNamespace);
+
 	/*
 	 * Check for collision with an existing type name.	If there is one and
 	 * it's an autogenerated array, we can rename it out of the way.
@@ -998,7 +1013,8 @@ DefineDomain(CreateDomainStmt *stmt)
 				   basetypeMod, /* typeMod value */
 				   typNDims,	/* Array dimensions for base type */
 				   typNotNull,	/* Type NOT NULL */
-				   domaincoll); /* type's collation */
+				   domaincoll,	/* type's collation */
+				   hook_private);	/* opaque to post-creation hook */
 
 	/*
 	 * Process constraints which refer to the domain ID returned by TypeCreate
@@ -1046,6 +1062,7 @@ DefineEnum(CreateEnumStmt *stmt)
 	Oid			enumNamespace;
 	Oid			enumTypeOid;
 	AclResult	aclresult;
+	Datum		hook_private = 0;
 	Oid			old_type_oid;
 	Oid			enumArrayOid;
 
@@ -1059,6 +1076,10 @@ DefineEnum(CreateEnumStmt *stmt)
 		aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
 					   get_namespace_name(enumNamespace));
 
+	/* Prep-creation hook for new enum type */
+	InvokeSchemaObjectPrepCreateHook(TypeRelationId, &hook_private,
+									 enumName, enumNamespace);
+
 	/*
 	 * Check for collision with an existing type name.	If there is one and
 	 * it's an autogenerated array, we can rename it out of the way.
@@ -1108,7 +1129,8 @@ DefineEnum(CreateEnumStmt *stmt)
 				   -1,			/* typMod (Domains only) */
 				   0,			/* Array dimensions of typbasetype */
 				   false,		/* Type NOT NULL */
-				   InvalidOid); /* type's collation */
+				   InvalidOid,	/* type's collation */
+				   hook_private);	/* opaque to post-creation hook */
 
 	/* Enter the enum's values into pg_enum */
 	EnumValuesCreate(enumTypeOid, stmt->vals);
@@ -1148,7 +1170,8 @@ DefineEnum(CreateEnumStmt *stmt)
 			   -1,				/* typMod (Domains only) */
 			   0,				/* Array dimensions of typbasetype */
 			   false,			/* Type NOT NULL */
-			   InvalidOid);		/* type's collation */
+			   InvalidOid,		/* type's collation */
+			   hook_private);	/* opaque to post-creation hook */
 
 	pfree(enumArrayName);
 }
@@ -1179,6 +1202,7 @@ DefineRange(CreateRangeStmt *stmt)
 	regproc		 rangeSubtypeDiff	  = InvalidOid;
 
 	AclResult	 aclresult;
+	Datum		 hook_private = 0;
 
 	/* Convert list of names to a name and namespace */
 	typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
@@ -1190,6 +1214,10 @@ DefineRange(CreateRangeStmt *stmt)
 		aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
 					   get_namespace_name(typeNamespace));
 
+	/* Prep-creation hook for new range type */
+	InvokeSchemaObjectPrepCreateHook(TypeRelationId, &hook_private,
+									 typeName, typeNamespace);
+
 	/*
 	 * Look to see if type already exists (presumably as a shell; if not,
 	 * TypeCreate will complain).
@@ -1214,7 +1242,8 @@ DefineRange(CreateRangeStmt *stmt)
 	 */
 	if (!OidIsValid(typoid))
 	{
-		typoid = TypeShellMake(typeName, typeNamespace, GetUserId());
+		typoid = TypeShellMake(typeName, typeNamespace,
+							   GetUserId(), hook_private);
 		/* Make new shell type visible for modification below */
 		CommandCounterIncrement();
 
@@ -1355,7 +1384,8 @@ DefineRange(CreateRangeStmt *stmt)
 				   -1,			/* typMod (Domains only) */
 				   0,			/* Array dimensions of typbasetype */
 				   false,		/* Type NOT NULL */
-				   InvalidOid);	/* typcollation */
+				   InvalidOid,	/* typcollation */
+				   hook_private);	/* opaque to post-creation hook */
 
 	/* create the entry in pg_range */
 	RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
@@ -1396,11 +1426,13 @@ DefineRange(CreateRangeStmt *stmt)
 			   -1,				/* typMod (Domains only) */
 			   0,				/* Array dimensions of typbasetype */
 			   false,			/* Type NOT NULL */
-			   InvalidOid);		/* typcollation */
+			   InvalidOid,		/* typcollation */
+			   hook_private);	/* opaque to post-creation hook */
 
 	pfree(rangeArrayName);
 
-	makeRangeConstructor(typeName, typeNamespace, typoid, rangeSubtype);
+	makeRangeConstructor(typeName, typeNamespace, typoid,
+						 rangeSubtype, hook_private);
 }
 
 /*
@@ -1413,7 +1445,8 @@ DefineRange(CreateRangeStmt *stmt)
  * offer more convenience for the user.
  */
 static void
-makeRangeConstructor(char *name, Oid namespace, Oid rangeOid, Oid subtype)
+makeRangeConstructor(char *name, Oid namespace, Oid rangeOid,
+					 Oid subtype, Datum hook_private)
 {
 	ObjectAddress		referenced;
 	Oid					constructorArgTypes[3];
@@ -1461,7 +1494,8 @@ makeRangeConstructor(char *name, Oid namespace, Oid rangeOid, Oid subtype)
 			NIL, /* parameterDefaults */
 			PointerGetDatum(NULL), /* proconfig */
 			1.0, /* procost */
-			0.0); /* prorows */
+			0.0, /* prorows */
+			hook_private);
 
 		/*
 		 * Make the constructor internally-dependent on the range type so that
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index fa312cb..cc29acb 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -434,7 +434,8 @@ CreateRole(CreateRoleStmt *stmt)
 				GetUserId(), false);
 
 	/* Post creation hook for new role */
-	InvokeObjectAccessHook(OAT_POST_CREATE, AuthIdRelationId, roleid, 0);
+	InvokeObjectAccessHook(OAT_POST_CREATE,
+						   AuthIdRelationId, roleid, 0, 0);
 
 	/*
 	 * Close pg_authid, but keep lock till commit.
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index fd7a9ed..b31eea4 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -43,6 +43,7 @@
 #include "access/xact.h"
 #include "catalog/heap.h"
 #include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
 #include "catalog/toasting.h"
 #include "commands/tablespace.h"
 #include "commands/trigger.h"
@@ -2393,6 +2394,7 @@ OpenIntoRel(QueryDesc *queryDesc)
 	Oid			namespaceId;
 	Oid			tablespaceId;
 	Datum		reloptions;
+	Datum		hook_private = 0;
 	Oid			intoRelationId;
 	DR_intorel *myState;
 	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
@@ -2467,6 +2469,12 @@ OpenIntoRel(QueryDesc *queryDesc)
 									 false);
 	(void) heap_reloptions(RELKIND_RELATION, reloptions, true);
 
+	/* Prep-creation hook for new table */
+	InvokeRelationPrepCreateHook(&hook_private, intoName,
+								 RELKIND_RELATION,
+								 namespaceId, tablespaceId,
+								 queryDesc->tupDesc);
+
 	/* Now we can actually create the new relation */
 	intoRelationId = heap_create_with_catalog(intoName,
 											  namespaceId,
@@ -2486,7 +2494,8 @@ OpenIntoRel(QueryDesc *queryDesc)
 											  into->onCommit,
 											  reloptions,
 											  true,
-											  allowSystemTableMods);
+											  allowSystemTableMods,
+											  hook_private);
 	Assert(intoRelationId != InvalidOid);
 
 	/*
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 17db70e..f0c0e31 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -178,7 +178,7 @@ InsertRule(char *rulname,
 
 	/* Post creation hook for new rule */
 	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   RewriteRelationId, rewriteObjectId, 0);
+						   RewriteRelationId, rewriteObjectId, 0, 0);
 
 	heap_close(pg_rewrite_desc, RowExclusiveLock);
 
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c
index fed6d2e..1cdfa5c 100644
--- a/src/backend/storage/large_object/inv_api.c
+++ b/src/backend/storage/large_object/inv_api.c
@@ -217,7 +217,7 @@ inv_create(Oid lobjId)
 
 	/* Post creation hook for new large object */
 	InvokeObjectAccessHook(OAT_POST_CREATE,
-						   LargeObjectRelationId, lobjId_new, 0);
+						   LargeObjectRelationId, lobjId_new, 0, 0);
 
 	/*
 	 * Advance command counter to make new tuple visible to later operations.
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index aee2d88..ca4d6a1 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -65,7 +65,8 @@ extern Oid heap_create_with_catalog(const char *relname,
 						 OnCommitAction oncommit,
 						 Datum reloptions,
 						 bool use_user_acl,
-						 bool allow_system_table_mods);
+						 bool allow_system_table_mods,
+						 Datum hook_private);
 
 extern void heap_drop_with_catalog(Oid relid);
 
diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h
index 2925475..30bdc75 100644
--- a/src/include/catalog/objectaccess.h
+++ b/src/include/catalog/objectaccess.h
@@ -10,11 +10,17 @@
 #ifndef OBJECTACCESS_H
 #define OBJECTACCESS_H
 
+#include "access/tupdesc.h"
+
 /*
  * Object access hooks are intended to be called just before or just after
  * performing certain actions on a SQL object.	This is intended as
  * infrastructure for security or logging pluggins.
  *
+ * OAT_PREP_CREATE should be invoked around the permission check of object
+ * creation; with arguments that informs properties of the object being
+ * constructed.
+ *
  * OAT_POST_CREATE should be invoked just after the the object is created.
  * Typically, this is done after inserting the primary catalog records and
  * associated dependencies.
@@ -23,24 +29,158 @@
  */
 typedef enum ObjectAccessType
 {
+	OAT_PREP_CREATE,
 	OAT_POST_CREATE,
 } ObjectAccessType;
 
 /*
+ * ObjectAccessPrepCreateInfo
+ *
+ * A set of contextual information on prep object events; reuired data
+ * is depending on object classes. So, module should check classId prior
+ * to reference this structure.
+ */
+typedef union
+{
+	struct {
+		Datum	   *private;
+		const char *objname;
+		Oid			namespaceId;	/* only objects under schema */
+	} common;
+
+	struct {
+		Datum	   *private;
+		const char *datname;
+		Oid			datsourceId;
+		Oid			tablespaceId;
+	} pg_database;
+
+	struct {
+		Datum	   *private;
+		const char *relname;
+		char		relkind;
+		Oid			namespaceId;
+		Oid			tablespaceId;
+		TupleDesc	tupdesc;
+	} pg_class;
+
+	struct {
+		Datum	   *private;
+		const char *proname;
+		oidvector  *proargs;
+		Oid			namespaceId;
+		Oid			languageId;
+		bool		leakproof;
+	} pg_proc;
+} ObjectAccessPrepCreateInfo;
+
+/*
  * Hook, and a macro to invoke it.
  */
 
 typedef void (*object_access_hook_type) (ObjectAccessType access,
-													 Oid classId,
-													 Oid objectId,
-													 int subId);
+										 Oid classId,
+										 Oid objectId,
+										 int subId,
+										 Datum argument);
 
 extern PGDLLIMPORT object_access_hook_type object_access_hook;
 
-#define InvokeObjectAccessHook(access,classId,objectId,subId)			\
+#define InvokeObjectPrepCreateHook(classId,_private,_objname)	\
+	do {														\
+		if (object_access_hook)									\
+		{														\
+			ObjectAccessPrepCreateInfo	__info;					\
+																\
+			__info.common.private = (_private);					\
+			__info.common.objname = (_objname);					\
+			__info.common.namespaceId = InvalidOid;				\
+																\
+			(*object_access_hook)(OAT_PREP_CREATE,				\
+								  (classId),InvalidOid,0,		\
+								  PointerGetDatum(&__info));	\
+		}														\
+	} while(0)
+
+#define InvokeSchemaObjectPrepCreateHook(classId,_private,		\
+										 _objname,_namespace)	\
+	do {														\
+		if (object_access_hook)									\
+		{														\
+			ObjectAccessPrepCreateInfo	__info;					\
+																\
+			__info.common.private = (_private);					\
+			__info.common.objname = (_objname);					\
+			__info.common.namespaceId = (_namespace);			\
+																\
+			(*object_access_hook)(OAT_PREP_CREATE,				\
+								  (classId),InvalidOid,0,		\
+								  PointerGetDatum(&__info));	\
+		}														\
+	} while(0)
+
+#define InvokeDatabasePrepCreateHook(_private,_datname,_source,_tablespace) \
+	do {																\
+		if (object_access_hook)											\
+		{																\
+			ObjectAccessPrepCreateInfo	__info;							\
+																		\
+			__info.pg_database.private = (_private);					\
+			__info.pg_database.datname = (_datname);					\
+			__info.pg_database.datsourceId = (_source);					\
+			__info.pg_database.tablespaceId = (_tablespace);			\
+																		\
+			(*object_access_hook)(OAT_PREP_CREATE,						\
+								  DatabaseRelationId,InvalidOid,0,		\
+								  PointerGetDatum(&__info));			\
+		}																\
+	} while (0)
+
+#define InvokeRelationPrepCreateHook(_private,_relname,_relkind,		\
+									 _namespace,_tablespace,_tupdesc)	\
 	do {																\
 		if (object_access_hook)											\
-			(*object_access_hook)((access),(classId),(objectId),(subId)); \
+		{																\
+			ObjectAccessPrepCreateInfo	__info;							\
+																		\
+			__info.pg_class.private = (_private);						\
+			__info.pg_class.relname = (_relname);						\
+			__info.pg_class.relkind = (_relkind);						\
+			__info.pg_class.namespaceId = (_namespace);					\
+			__info.pg_class.tablespaceId = (_tablespace);				\
+			__info.pg_class.tupdesc = (_tupdesc);						\
+																		\
+			(*object_access_hook)(OAT_PREP_CREATE,						\
+								  RelationRelationId,InvalidOid,0,		\
+								  PointerGetDatum(&__info));			\
+		}																\
+	} while(0)
+
+#define InvokeProcedurePrepCreateHook(_private,_proname,_proargs,		\
+									  _namespace,_language,_leakproof)	\
+	do {																\
+		if (object_access_hook)											\
+		{																\
+			ObjectAccessPrepCreateInfo	__info;							\
+																		\
+			__info.pg_proc.private = (_private);						\
+			__info.pg_proc.proname = (_proname);						\
+			__info.pg_proc.proargs = (_proargs);						\
+			__info.pg_proc.namespaceId = (_namespace);					\
+			__info.pg_proc.languageId = (_language);					\
+			__info.pg_proc.leakproof = (_leakproof);					\
+																		\
+			(*object_access_hook)(OAT_PREP_CREATE,						\
+								  ProcedureRelationId, InvalidOid, 0,	\
+								  PointerGetDatum(&__info));			\
+		}																\
+	} while(0)
+
+#define InvokeObjectAccessHook(access,classId,objectId,subId,argment) \
+	do {															  \
+		if (object_access_hook)										  \
+			(*object_access_hook)((access),(classId),				  \
+								  (objectId),(subId),(argment));	  \
 	} while(0)
 
 #endif   /* OBJECTACCESS_H */
diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h
index 26966d2..7b2d172 100644
--- a/src/include/catalog/pg_aggregate.h
+++ b/src/include/catalog/pg_aggregate.h
@@ -237,6 +237,7 @@ extern void AggregateCreate(const char *aggName,
 				List *aggfinalfnName,
 				List *aggsortopName,
 				Oid aggTransType,
-				const char *agginitval);
+				const char *agginitval,
+				Datum hook_private);
 
 #endif   /* PG_AGGREGATE_H */
diff --git a/src/include/catalog/pg_namespace.h b/src/include/catalog/pg_namespace.h
index 680802d7..eedcf1b 100644
--- a/src/include/catalog/pg_namespace.h
+++ b/src/include/catalog/pg_namespace.h
@@ -77,6 +77,7 @@ DESCR("standard public schema");
 /*
  * prototypes for functions in pg_namespace.c
  */
-extern Oid	NamespaceCreate(const char *nspName, Oid ownerId);
+extern Oid	NamespaceCreate(const char *nspName, Oid ownerId,
+							Datum hook_private);
 
 #endif   /* PG_NAMESPACE_H */
diff --git a/src/include/catalog/pg_proc_fn.h b/src/include/catalog/pg_proc_fn.h
index 09d779f..a28786f 100644
--- a/src/include/catalog/pg_proc_fn.h
+++ b/src/include/catalog/pg_proc_fn.h
@@ -37,7 +37,8 @@ extern Oid ProcedureCreate(const char *procedureName,
 				List *parameterDefaults,
 				Datum proconfig,
 				float4 procost,
-				float4 prorows);
+				float4 prorows,
+				Datum hook_private);
 
 extern bool function_parse_error_transpose(const char *prosrc);
 
diff --git a/src/include/catalog/pg_type_fn.h b/src/include/catalog/pg_type_fn.h
index 81e7d7f..f47c7b6 100644
--- a/src/include/catalog/pg_type_fn.h
+++ b/src/include/catalog/pg_type_fn.h
@@ -19,7 +19,8 @@
 
 extern Oid TypeShellMake(const char *typeName,
 			  Oid typeNamespace,
-			  Oid ownerId);
+			  Oid ownerId,
+			  Datum hook_private);
 
 extern Oid TypeCreate(Oid newTypeOid,
 		   const char *typeName,
@@ -51,7 +52,8 @@ extern Oid TypeCreate(Oid newTypeOid,
 		   int32 typeMod,
 		   int32 typNDims,
 		   bool typeNotNull,
-		   Oid typeCollation);
+		   Oid typeCollation,
+		   Datum hook_private);
 
 extern void GenerateTypeDependencies(Oid typeNamespace,
 						 Oid typeObjectId,
#19Robert Haas
robertmhaas@gmail.com
In reply to: Kohei KaiGai (#18)
Re: [v9.2] Object access hooks with arguments support (v1)

On Mon, Nov 7, 2011 at 12:20 PM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:

If sepgsql would apply permission checks db_procedure:{install} on the
OAT_POST_CREATE hook based on the funcion-oid within new entry of
system catalog, we can relocate OAT_PREP_CREATE hook more conceptually
right place, such as just after the pg_namespace_aclcheck() of DefineType().
On the other hand, we may need to answer why these information are NOT
delivered on the OAT_PREP_CREATE hook without knowledge of sepgsql
internal.

I'm having a hard time understanding this.

Can you strip this patch down so it just applies to a single object
type (tables, maybe, or functions, or whatever you like) and then
submit the corresponding sepgsql changes with it? Just as a demo
patch, so I can understand where you're trying to go with this.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#20Kohei KaiGai
kaigai@kaigai.gr.jp
In reply to: Robert Haas (#19)
4 attachment(s)
Re: [v9.2] Object access hooks with arguments support (v1)

2011/11/8 Robert Haas <robertmhaas@gmail.com>:

On Mon, Nov 7, 2011 at 12:20 PM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:

If sepgsql would apply permission checks db_procedure:{install} on the
OAT_POST_CREATE hook based on the funcion-oid within new entry of
system catalog, we can relocate OAT_PREP_CREATE hook more conceptually
right place, such as just after the pg_namespace_aclcheck() of DefineType().
On the other hand, we may need to answer why these information are NOT
delivered on the OAT_PREP_CREATE hook without knowledge of sepgsql
internal.

I'm having a hard time understanding this.

Can you strip this patch down so it just applies to a single object
type (tables, maybe, or functions, or whatever you like) and then
submit the corresponding sepgsql changes with it?  Just as a demo
patch, so I can understand where you're trying to go with this.

I tried to strip the previous patch into small portion per object types.
Part-1 for database, Part-2 for schema, Part-3 for relations, and
Part-4 for functions.

The basic idea is the prep-creation hook informs the sepgsql module
properties of the new object being constructed. According to the
previous discussion, all the arguments are commonly used to existing
DAC checks also. Then, this hook allows to write back its private
opaque to be delivered to the post-creation hook; likely used to deliver
security label of the new object to be assigned.

However, I become unsure whether it is a good idea to put prep-creation
hook on all the object, because it takes many boring interface changes
to deliver private datum, and we're probably able to implement similar
stuff with post-creation hook except for a few object types.

I guess the following (A) and (B) are the only case that needs prep-
creation hooks for permission checks. Elsewhere, sepgsql will be
able to make its decision based on the entry of system catalogs
on post-creation hook.

(A) In the case when we want to apply checks based on information
that is not contained within system catalogs.
E.g, Oid of source database on CREATE DATABASE. Existing DAC
checks ownership of the database, if not template.

(B) In the case when we want to distinguish code path between user's
query and system internal stuff.
E.g, heap_create_with_catalog() is also called by make_new_heap()
as a part of ALTER TABLE, but it is quite internal stuff, so not suitable
to apply permission checks here.

It seems to me, using post-creation hooks makes the patch mode simple
to implement permission checks; except for above two object types.
So, I'd like to adopt approach to put prep-creation hooks on limited number
of object types, not symmetric with post-creation hook.
How about your opinion about this?

Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>

Attachments:

pgsql-v9.2-prep-creation-hook-part-1.v1.patchapplication/octet-stream; name=pgsql-v9.2-prep-creation-hook-part-1.v1.patchDownload
 contrib/sepgsql/database.c          |   68 ++++++++++++++-----
 contrib/sepgsql/expected/create.out |   19 ++++++
 contrib/sepgsql/hooks.c             |  123 ++++++++++++++++++++++++++---------
 contrib/sepgsql/sepgsql.h           |   23 ++++++-
 contrib/sepgsql/sql/create.sql      |   15 ++++
 contrib/sepgsql/test_sepgsql        |    2 +-
 src/backend/commands/dbcommands.c   |    9 ++-
 src/include/catalog/objectaccess.h  |   71 ++++++++++++++++++--
 8 files changed, 272 insertions(+), 58 deletions(-)

diff --git a/contrib/sepgsql/database.c b/contrib/sepgsql/database.c
index 7f15d9c..ed3817b 100644
--- a/contrib/sepgsql/database.c
+++ b/contrib/sepgsql/database.c
@@ -15,40 +15,72 @@
 #include "commands/seclabel.h"
 #include "sepgsql.h"
 
-void
-sepgsql_database_post_create(Oid databaseId)
+/*
+ * sepgsql_database_prep_create
+ *
+ * This routine computes default security label and checks permissions to
+ * create a new database.
+ */
+const char *
+sepgsql_database_prep_create(const char *datname,
+							 Oid datsourceId,
+							 Oid tablespaceId)
 {
 	char   *scontext = sepgsql_get_client_label();
 	char   *tcontext;
 	char   *ncontext;
+	char	audit_name[NAMEDATALEN + 16];
 	ObjectAddress	object;
 
-	/*
-	 * Compute a default security label of the newly created database
-	 * based on a pair of security label of client and source database.
-	 *
-	 * XXX - Right now, this logic uses "template1" as its source, because
-	 * here is no way to know the Oid of source database.
-	 */
+	/* check db_database:{getattr} permission on the source database */
 	object.classId = DatabaseRelationId;
-	object.objectId = TemplateDbOid;
+	object.objectId = datsourceId;;
 	object.objectSubId = 0;
-	tcontext = GetSecurityLabel(&object, SEPGSQL_LABEL_TAG);
+	sepgsql_avc_check_perms(&object,
+							SEPG_CLASS_DB_DATABASE,
+							SEPG_DB_DATABASE__GETATTR,
+							getObjectDescription(&object),
+							true);
+	/*
+	 * Compute a default security label of the new database being
+	 * constructed; based on a pair of security label of client and
+	 * source database.
+	 */
+	tcontext = sepgsql_get_label(DatabaseRelationId, datsourceId, 0);
 
+	/*
+	 * XXX - upcoming libselinux supports case handling when
+	 * a new object that has a particular name.
+	 */
 	ncontext = sepgsql_compute_create(scontext, tcontext,
 									  SEPG_CLASS_DB_DATABASE);
 
-	/*
-	 * Assign the default security label on the new database
-	 */
+	/* check db_database:{create} permission */
+	snprintf(audit_name, sizeof(audit_name), "database: %s", datname);
+	sepgsql_avc_check_perms_label(ncontext,
+								  SEPG_CLASS_DB_DATABASE,
+								  SEPG_DB_DATABASE__CREATE,
+								  audit_name,
+								  true);
+
+	return ncontext;
+}
+
+/*
+ * sepgsql_database_post_create
+ *
+ * This routine assigns default security label of the database.
+ */
+void
+sepgsql_database_post_create(Oid databaseId, const char *seclabel)
+{
+	ObjectAddress	object;
+
 	object.classId = DatabaseRelationId;
 	object.objectId = databaseId;
 	object.objectSubId = 0;
 
-	SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext);
-
-	pfree(ncontext);
-	pfree(tcontext);
+	SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, seclabel);
 }
 
 /*
diff --git a/contrib/sepgsql/expected/create.out b/contrib/sepgsql/expected/create.out
new file mode 100644
index 0000000..95fc4f1
--- /dev/null
+++ b/contrib/sepgsql/expected/create.out
@@ -0,0 +1,19 @@
+--
+-- Regression Test for Creation of Object Permission Checks
+--
+-- confirm required permissions using audit messages
+SELECT sepgsql_getcon();	-- confirm client privilege
+              sepgsql_getcon               
+-------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0
+(1 row)
+
+SET sepgsql.debug_audit = true;
+SET client_min_messages = LOG;
+CREATE DATABASE regtest_sepgsql_test_database;
+LOG:  SELinux: allowed { getattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database template1"
+LOG:  SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database: regtest_sepgsql_test_database"
+--
+-- clean-up 
+--
+DROP DATABASE IF EXISTS regtest_sepgsql_test_database;
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 331bbd7..886f1fa 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -107,6 +107,91 @@ sepgsql_client_auth(Port *port, int status)
 }
 
 /*
+ * sepgsql_object_prep_create
+ *
+ * Entrypoint of OAT_PREP_CREATE event via object_access_hook.
+ */
+static void
+sepgsql_object_prep_create(Oid classId, Oid objectId, int subId,
+						   Datum argument)
+{
+	ObjectAccessCreateObjectArgs   *args
+		= (ObjectAccessCreateObjectArgs *)DatumGetPointer(argument);
+	sepgsql_creation_info		   *cinfo;
+
+	if (next_object_access_hook)
+		(*next_object_access_hook) (OAT_PREP_CREATE,
+									classId, objectId, subId,
+									argument);
+
+	/*
+	 * create sepgsql's private data to be delivered to
+	 * the post-creation hook.
+	 */
+	cinfo = palloc(sizeof(sepgsql_creation_info));
+	cinfo->classId = classId;
+	cinfo->private_next = *(args->common.private);
+
+	switch (classId)
+	{
+		case DatabaseRelationId:
+			cinfo->ncontext =
+				sepgsql_database_prep_create(args->pg_database.datname,
+											 args->pg_database.datsourceId,
+											 args->pg_database.tablespaceId);
+			break;
+
+		default:
+			/* Ignore unsupported object classes */
+			break;
+	}
+	*(args->common.private) = PointerGetDatum(cinfo);
+}
+
+/*
+ * sepgsql_object_post_create
+ *
+ * Entrypoint of OAT_POST_CREATE event via object_access_hook.
+ */
+static void
+sepgsql_object_post_create(Oid classId, Oid objectId, int subId,
+						   Datum argument)
+{
+	sepgsql_creation_info  *cinfo
+		= (sepgsql_creation_info *)DatumGetPointer(argument);
+
+	if (next_object_access_hook)
+		(*next_object_access_hook) (OAT_POST_CREATE,
+									classId, objectId, subId,
+									!cinfo ? 0 : cinfo->private_next);
+	switch (classId)
+	{
+		case DatabaseRelationId:
+			sepgsql_database_post_create(objectId, cinfo->ncontext);
+			break;
+
+		case NamespaceRelationId:
+			sepgsql_schema_post_create(objectId);
+			break;
+
+		case RelationRelationId:
+			if (subId == 0)
+				sepgsql_relation_post_create(objectId);
+			else
+				sepgsql_attribute_post_create(objectId, subId);
+			break;
+
+		case ProcedureRelationId:
+			sepgsql_proc_post_create(objectId);
+			break;
+
+		default:
+			/* Ignore unsupported object classes */
+			break;
+	}
+}
+
+/*
  * sepgsql_object_access
  *
  * Entrypoint of the object_access_hook. This routine performs as
@@ -114,41 +199,17 @@ sepgsql_client_auth(Port *port, int status)
  */
 static void
 sepgsql_object_access(ObjectAccessType access,
-					  Oid classId,
-					  Oid objectId,
-					  int subId)
+					  Oid classId, Oid objectId, int subId,
+					  Datum argument)
 {
-	if (next_object_access_hook)
-		(*next_object_access_hook) (access, classId, objectId, subId);
-
 	switch (access)
 	{
+		case OAT_PREP_CREATE:
+			sepgsql_object_prep_create(classId, objectId, subId, argument);
+			break;
+
 		case OAT_POST_CREATE:
-			switch (classId)
-			{
-				case DatabaseRelationId:
-					sepgsql_database_post_create(objectId);
-					break;
-
-				case NamespaceRelationId:
-					sepgsql_schema_post_create(objectId);
-					break;
-
-				case RelationRelationId:
-					if (subId == 0)
-						sepgsql_relation_post_create(objectId);
-					else
-						sepgsql_attribute_post_create(objectId, subId);
-					break;
-
-				case ProcedureRelationId:
-					sepgsql_proc_post_create(objectId);
-					break;
-
-				default:
-					/* Ignore unsupported object classes */
-					break;
-			}
+			sepgsql_object_post_create(classId, objectId, subId, argument);
 			break;
 
 		default:
diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h
index b4c1dfd..0d0eead 100644
--- a/contrib/sepgsql/sepgsql.h
+++ b/contrib/sepgsql/sepgsql.h
@@ -211,6 +211,24 @@
 #define SEPG_DB_VIEW__EXPAND				(1<<6)
 
 /*
+ * sepgsql_creation_info
+ *
+ * A data structure to store contextual information between prep-creation
+ * and post-creation hooks; mostly a security label to be assigned on the
+ * newly constructed object.
+ */
+typedef struct {
+	/* oid of the catalog that stores this new object */
+	Oid			classId;
+
+	/* private field for stacking module */
+	Datum		private_next;
+
+	/* a default security label to be assigned on */
+	const char *ncontext;
+} sepgsql_creation_info;
+
+/*
  * hooks.c
  */
 extern bool sepgsql_get_permissive(void);
@@ -286,7 +304,10 @@ extern bool sepgsql_dml_privileges(List *rangeTabls, bool abort);
 /*
  * database.c
  */
-extern void sepgsql_database_post_create(Oid databaseId);
+extern const char *sepgsql_database_prep_create(const char *datname,
+												Oid datsourceId,
+												Oid tablespaceId);
+extern void sepgsql_database_post_create(Oid databaseId, const char *seclabel);
 extern void sepgsql_database_relabel(Oid databaseId, const char *seclabel);
 
 /*
diff --git a/contrib/sepgsql/sql/create.sql b/contrib/sepgsql/sql/create.sql
new file mode 100644
index 0000000..e75d57b
--- /dev/null
+++ b/contrib/sepgsql/sql/create.sql
@@ -0,0 +1,15 @@
+--
+-- Regression Test for Creation of Object Permission Checks
+--
+
+-- confirm required permissions using audit messages
+-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0
+SET sepgsql.debug_audit = true;
+SET client_min_messages = LOG;
+
+CREATE DATABASE regtest_sepgsql_test_database;
+
+--
+-- clean-up 
+--
+DROP DATABASE IF EXISTS regtest_sepgsql_test_database;
diff --git a/contrib/sepgsql/test_sepgsql b/contrib/sepgsql/test_sepgsql
index 9b7262a..52237e6 100755
--- a/contrib/sepgsql/test_sepgsql
+++ b/contrib/sepgsql/test_sepgsql
@@ -259,6 +259,6 @@ echo "found ${NUM}"
 echo
 echo "============== running sepgsql regression tests       =============="
 
-make REGRESS="label dml misc" REGRESS_OPTS="--launcher ./launcher" installcheck
+make REGRESS="label dml create misc" REGRESS_OPTS="--launcher ./launcher" installcheck
 
 # exit with the exit code provided by "make"
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 4551db7..361a172 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -127,6 +127,7 @@ createdb(const CreatedbStmt *stmt)
 	int			dbconnlimit = -1;
 	int			notherbackends;
 	int			npreparedxacts;
+	Datum		hook_private = 0;
 	createdb_failure_params fparms;
 
 	/* Extract options from the statement node tree */
@@ -424,6 +425,10 @@ createdb(const CreatedbStmt *stmt)
 		/* Note there is no additional permission check in this path */
 	}
 
+	/* Prep-creation hook for new database */
+	InvokePrepCreateDatabaseHook(&hook_private, dbname, src_dboid,
+								 dst_deftablespace);
+
 	/*
 	 * Check for db name conflict.	This is just to give a more friendly error
 	 * message than "unique index violation".  There's a race condition but
@@ -515,7 +520,9 @@ createdb(const CreatedbStmt *stmt)
 	copyTemplateDependencies(src_dboid, dboid);
 
 	/* Post creation hook for new database */
-	InvokeObjectAccessHook(OAT_POST_CREATE, DatabaseRelationId, dboid, 0);
+	InvokeObjectAccessHookArg(OAT_POST_CREATE,
+							  DatabaseRelationId, dboid, 0,
+							  hook_private);
 
 	/*
 	 * Force a checkpoint before starting the copy. This will force dirty
diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h
index 2925475..0531327 100644
--- a/src/include/catalog/objectaccess.h
+++ b/src/include/catalog/objectaccess.h
@@ -15,32 +15,91 @@
  * performing certain actions on a SQL object.	This is intended as
  * infrastructure for security or logging pluggins.
  *
+ * OAT_PREP_CREATE should be invoked around permission check of the core
+ * with arguments that describes about new object being constructed.
+ * Its private field can be used to deliver contextual information to
+ * post-creation hook.
+ *
  * OAT_POST_CREATE should be invoked just after the the object is created.
  * Typically, this is done after inserting the primary catalog records and
- * associated dependencies.
+ * associated dependencies. If prep-creation hook was invoked in same
+ * context, its private valud shall be provided to the modules.
  *
  * Other types may be added in the future.
  */
 typedef enum ObjectAccessType
 {
+	OAT_PREP_CREATE,
 	OAT_POST_CREATE,
 } ObjectAccessType;
 
 /*
- * Hook, and a macro to invoke it.
+ * ObjectAccessCreateObjectInfo
+ *
+ * It shall be used as argument of OAT_PREP_CREATE hook to inform about
+ * new object being constructed; prior to updates of system catalogs.
+ */
+typedef union
+{
+	struct {
+		Datum	   *private;	/* private data to deliver contextual
+								 * information into post creation hook */
+	} common;
+	struct {
+		Datum	   *private;	/* common */
+		const char *datname;	/* name of new database */
+		Oid			datsourceId;	/* oid of source database */
+		Oid			tablespaceId;	/* oid of default tablespace */
+	} pg_database;
+} ObjectAccessCreateObjectArgs;
+
+/*
+ * Hook, and macros to invoke it.
  */
 
 typedef void (*object_access_hook_type) (ObjectAccessType access,
-													 Oid classId,
-													 Oid objectId,
-													 int subId);
+										 Oid classId,
+										 Oid objectId,
+										 int subId,
+										 Datum argument);
 
 extern PGDLLIMPORT object_access_hook_type object_access_hook;
 
 #define InvokeObjectAccessHook(access,classId,objectId,subId)			\
 	do {																\
 		if (object_access_hook)											\
-			(*object_access_hook)((access),(classId),(objectId),(subId)); \
+			(*object_access_hook)((access),								\
+								  (classId),(objectId),(subId),			\
+								  (Datum) 0);							\
+	} while(0)
+
+#define InvokeObjectAccessHookArg(access,classId,objectId,subId,argument) \
+	do {																\
+		if (object_access_hook)											\
+			(*object_access_hook)((access),						\
+								  (classId),(objectId),(subId),			\
+								  (argument));							\
+	} while(0)
+
+/*
+ * OAT_PREP_CREATE specific macros, because it takes various arguments
+ * depending on object classes.
+ */
+#define InvokePrepCreateDatabaseHook(_private,_datname,_source,_tablespace) \
+	do {																\
+		if (object_access_hook)											\
+		{																\
+			ObjectAccessCreateObjectArgs	__args;						\
+																		\
+			__args.pg_database.private = (_private);					\
+			__args.pg_database.datname = (_datname);					\
+			__args.pg_database.datsourceId = (_source);					\
+			__args.pg_database.tablespaceId = (_tablespace);			\
+																		\
+			(*object_access_hook)(OAT_PREP_CREATE,						\
+								  DatabaseRelationId, InvalidOid, 0,	\
+								  PointerGetDatum(&__args));			\
+		}																\
 	} while(0)
 
 #endif   /* OBJECTACCESS_H */
pgsql-v9.2-prep-creation-hook-part-2.v1.patchapplication/octet-stream; name=pgsql-v9.2-prep-creation-hook-part-2.v1.patchDownload
 contrib/sepgsql/expected/create.out |    4 +++
 contrib/sepgsql/hooks.c             |    7 ++++-
 contrib/sepgsql/schema.c            |   49 +++++++++++++++++++++++++---------
 contrib/sepgsql/sepgsql.h           |    3 +-
 contrib/sepgsql/sql/create.sql      |    6 ++++
 src/backend/catalog/namespace.c     |   17 +++++++++---
 src/backend/catalog/pg_namespace.c  |    7 +++--
 src/backend/commands/schemacmds.c   |    7 ++++-
 src/include/catalog/objectaccess.h  |   19 +++++++++++++
 src/include/catalog/pg_namespace.h  |    3 +-
 10 files changed, 98 insertions(+), 24 deletions(-)

diff --git a/contrib/sepgsql/expected/create.out b/contrib/sepgsql/expected/create.out
index 95fc4f1..3293a73 100644
--- a/contrib/sepgsql/expected/create.out
+++ b/contrib/sepgsql/expected/create.out
@@ -13,7 +13,11 @@ SET client_min_messages = LOG;
 CREATE DATABASE regtest_sepgsql_test_database;
 LOG:  SELinux: allowed { getattr } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=system_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database template1"
 LOG:  SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_db_t:s0 tclass=db_database name="database: regtest_sepgsql_test_database"
+CREATE SCHEMA regtest_schema;
+LOG:  SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
+SET search_path = regtest_schema, public;
 --
 -- clean-up 
 --
 DROP DATABASE IF EXISTS regtest_sepgsql_test_database;
+DROP SCHEMA IF EXISTS regtest_schema CASCADE;
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 886f1fa..2b62a75 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -141,6 +141,11 @@ sepgsql_object_prep_create(Oid classId, Oid objectId, int subId,
 											 args->pg_database.tablespaceId);
 			break;
 
+		case NamespaceRelationId:
+			cinfo->ncontext =
+				sepgsql_schema_prep_create(args->pg_namespace.nspname);
+			break;
+
 		default:
 			/* Ignore unsupported object classes */
 			break;
@@ -171,7 +176,7 @@ sepgsql_object_post_create(Oid classId, Oid objectId, int subId,
 			break;
 
 		case NamespaceRelationId:
-			sepgsql_schema_post_create(objectId);
+			sepgsql_schema_post_create(objectId, cinfo->ncontext);
 			break;
 
 		case RelationRelationId:
diff --git a/contrib/sepgsql/schema.c b/contrib/sepgsql/schema.c
index a167be1..07dbf80 100644
--- a/contrib/sepgsql/schema.c
+++ b/contrib/sepgsql/schema.c
@@ -20,38 +20,61 @@
 #include "sepgsql.h"
 
 /*
- * sepgsql_schema_post_create
+ * sepgsql_schema_prep_create
  *
- * This routine assigns a default security label on a newly defined
- * schema.
+ * It computes a default security label of schema object, and checks
+ * permission to create a new schema object.
  */
-void
-sepgsql_schema_post_create(Oid namespaceId)
+const char *
+sepgsql_schema_prep_create(const char *nspname)
 {
-	char	   *scontext;
+	char	   *scontext = sepgsql_get_client_label();
 	char	   *tcontext;
 	char	   *ncontext;
-	ObjectAddress object;
+	char		audit_name[NAMEDATALEN + 16];
 
 	/*
 	 * Compute a default security label when we create a new schema object
 	 * under the working database.
 	 */
-	scontext = sepgsql_get_client_label();
 	tcontext = sepgsql_get_label(DatabaseRelationId, MyDatabaseId, 0);
 	ncontext = sepgsql_compute_create(scontext, tcontext,
 									  SEPG_CLASS_DB_SCHEMA);
 
 	/*
-	 * Assign the default security label on a new procedure
+	 * Check db_schema:{create} permission
 	 */
+	if (strncmp(nspname, "pg_temp_", 8) == 0)
+		snprintf(audit_name, sizeof(audit_name), "schema pg_temp");
+	else
+		snprintf(audit_name, sizeof(audit_name), "schema %s", nspname);
+
+	sepgsql_avc_check_perms_label(ncontext,
+								  SEPG_CLASS_DB_SCHEMA,
+								  SEPG_DB_SCHEMA__CREATE,
+								  audit_name,
+								  true);
+
+	pfree(tcontext);
+
+	return ncontext;
+}
+
+
+/*
+ * sepgsql_schema_post_create
+ *
+ * This routine assigns a default security label on a newly defined schema.
+ */
+void
+sepgsql_schema_post_create(Oid namespaceId, const char *seclabel)
+{
+	ObjectAddress	object;
+
 	object.classId = NamespaceRelationId;
 	object.objectId = namespaceId;
 	object.objectSubId = 0;
-	SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext);
-
-	pfree(ncontext);
-	pfree(tcontext);
+	SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, seclabel);
 }
 
 /*
diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h
index 0d0eead..b4a3e4d 100644
--- a/contrib/sepgsql/sepgsql.h
+++ b/contrib/sepgsql/sepgsql.h
@@ -313,7 +313,8 @@ extern void sepgsql_database_relabel(Oid databaseId, const char *seclabel);
 /*
  * schema.c
  */
-extern void sepgsql_schema_post_create(Oid namespaceId);
+extern const char *sepgsql_schema_prep_create(const char *nspname);
+extern void sepgsql_schema_post_create(Oid namespaceId, const char *seclabel);
 extern void sepgsql_schema_relabel(Oid namespaceId, const char *seclabel);
 
 /*
diff --git a/contrib/sepgsql/sql/create.sql b/contrib/sepgsql/sql/create.sql
index e75d57b..9ce1db9 100644
--- a/contrib/sepgsql/sql/create.sql
+++ b/contrib/sepgsql/sql/create.sql
@@ -9,7 +9,13 @@ SET client_min_messages = LOG;
 
 CREATE DATABASE regtest_sepgsql_test_database;
 
+CREATE SCHEMA regtest_schema;
+
+SET search_path = regtest_schema, public;
+
 --
 -- clean-up 
 --
 DROP DATABASE IF EXISTS regtest_sepgsql_test_database;
+
+DROP SCHEMA IF EXISTS regtest_schema CASCADE;
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index fcc90fe..3a0238f 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -21,6 +21,7 @@
 
 #include "access/xact.h"
 #include "catalog/dependency.h"
+#include "catalog/objectaccess.h"
 #include "catalog/pg_authid.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_conversion.h"
@@ -3384,9 +3385,12 @@ InitTempTableNamespace(void)
 	char		namespaceName[NAMEDATALEN];
 	Oid			namespaceId;
 	Oid			toastspaceId;
+	Datum		hook_private = 0;
 
 	Assert(!OidIsValid(myTempNamespace));
 
+	snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId);
+
 	/*
 	 * First, do permission check to see if we are authorized to make temp
 	 * tables.	We use a nonstandard error message here since "databasename:
@@ -3404,6 +3408,9 @@ InitTempTableNamespace(void)
 				 errmsg("permission denied to create temporary tables in database \"%s\"",
 						get_database_name(MyDatabaseId))));
 
+	/* Prep-creation hook for new temp-schema */
+	InvokePrepCreateSchemaHook(&hook_private, namespaceName);
+
 	/*
 	 * Do not allow a Hot Standby slave session to make temp tables.  Aside
 	 * from problems with modifying the system catalogs, there is a naming
@@ -3419,8 +3426,6 @@ InitTempTableNamespace(void)
 				(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
 				 errmsg("cannot create temporary tables during recovery")));
 
-	snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId);
-
 	namespaceId = get_namespace_oid(namespaceName, true);
 	if (!OidIsValid(namespaceId))
 	{
@@ -3432,7 +3437,9 @@ InitTempTableNamespace(void)
 		 * temp tables.  This works because the places that access the temp
 		 * namespace for my own backend skip permissions checks on it.
 		 */
-		namespaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID);
+		namespaceId = NamespaceCreate(namespaceName,
+									  BOOTSTRAP_SUPERUSERID,
+									  hook_private);
 		/* Advance command counter to make namespace visible */
 		CommandCounterIncrement();
 	}
@@ -3456,7 +3463,9 @@ InitTempTableNamespace(void)
 	toastspaceId = get_namespace_oid(namespaceName, true);
 	if (!OidIsValid(toastspaceId))
 	{
-		toastspaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID);
+		toastspaceId = NamespaceCreate(namespaceName,
+									   BOOTSTRAP_SUPERUSERID,
+									   hook_private);
 		/* Advance command counter to make namespace visible */
 		CommandCounterIncrement();
 	}
diff --git a/src/backend/catalog/pg_namespace.c b/src/backend/catalog/pg_namespace.c
index ceebac2..48b9fc9 100644
--- a/src/backend/catalog/pg_namespace.c
+++ b/src/backend/catalog/pg_namespace.c
@@ -29,7 +29,7 @@
  * ---------------
  */
 Oid
-NamespaceCreate(const char *nspName, Oid ownerId)
+NamespaceCreate(const char *nspName, Oid ownerId, Datum hook_private)
 {
 	Relation	nspdesc;
 	HeapTuple	tup;
@@ -86,7 +86,8 @@ NamespaceCreate(const char *nspName, Oid ownerId)
 	recordDependencyOnCurrentExtension(&myself, false);
 
 	/* Post creation hook for new schema */
-	InvokeObjectAccessHook(OAT_POST_CREATE, NamespaceRelationId, nspoid, 0);
-
+	InvokeObjectAccessHookArg(OAT_POST_CREATE,
+							  NamespaceRelationId, nspoid, 0,
+							  hook_private);
 	return nspoid;
 }
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
index 8daa9d0..d6f1927 100644
--- a/src/backend/commands/schemacmds.c
+++ b/src/backend/commands/schemacmds.c
@@ -19,6 +19,7 @@
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
 #include "catalog/pg_namespace.h"
 #include "commands/dbcommands.h"
 #include "commands/schemacmds.h"
@@ -48,6 +49,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
 	Oid			owner_uid;
 	Oid			saved_uid;
 	int			save_sec_context;
+	Datum		hook_private = 0;
 	AclResult	aclresult;
 
 	GetUserIdAndSecContext(&saved_uid, &save_sec_context);
@@ -74,6 +76,9 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
 
 	check_is_member_of_role(saved_uid, owner_uid);
 
+	/* Prep-creation hook for new schema */
+	InvokePrepCreateSchemaHook(&hook_private, schemaName);
+
 	/* Additional check to protect reserved schema names */
 	if (!allowSystemTableMods && IsReservedName(schemaName))
 		ereport(ERROR,
@@ -94,7 +99,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
 							save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
 
 	/* Create the schema's namespace */
-	namespaceId = NamespaceCreate(schemaName, owner_uid);
+	namespaceId = NamespaceCreate(schemaName, owner_uid, hook_private);
 
 	/* Advance cmd counter to make the namespace visible */
 	CommandCounterIncrement();
diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h
index 0531327..cb5c396 100644
--- a/src/include/catalog/objectaccess.h
+++ b/src/include/catalog/objectaccess.h
@@ -51,6 +51,10 @@ typedef union
 		Oid			datsourceId;	/* oid of source database */
 		Oid			tablespaceId;	/* oid of default tablespace */
 	} pg_database;
+	struct {
+		Datum	   *private;	/* common */
+		const char *nspname;	/* name of new schema */
+	} pg_namespace;
 } ObjectAccessCreateObjectArgs;
 
 /*
@@ -102,4 +106,19 @@ extern PGDLLIMPORT object_access_hook_type object_access_hook;
 		}																\
 	} while(0)
 
+#define InvokePrepCreateSchemaHook(_private,_nspname)					\
+	do {																\
+		if (object_access_hook)											\
+		{																\
+			ObjectAccessCreateObjectArgs	__args;						\
+																		\
+			__args.pg_namespace.private = (_private);					\
+			__args.pg_namespace.nspname = (_nspname);					\
+																		\
+			(*object_access_hook)(OAT_PREP_CREATE,						\
+								  NamespaceRelationId, InvalidOid, 0,	\
+								  PointerGetDatum(&__args));			\
+		}																\
+	} while(0)
+
 #endif   /* OBJECTACCESS_H */
diff --git a/src/include/catalog/pg_namespace.h b/src/include/catalog/pg_namespace.h
index 680802d7..eedcf1b 100644
--- a/src/include/catalog/pg_namespace.h
+++ b/src/include/catalog/pg_namespace.h
@@ -77,6 +77,7 @@ DESCR("standard public schema");
 /*
  * prototypes for functions in pg_namespace.c
  */
-extern Oid	NamespaceCreate(const char *nspName, Oid ownerId);
+extern Oid	NamespaceCreate(const char *nspName, Oid ownerId,
+							Datum hook_private);
 
 #endif   /* PG_NAMESPACE_H */
pgsql-v9.2-prep-creation-hook-part-3.v1.patchapplication/octet-stream; name=pgsql-v9.2-prep-creation-hook-part-3.v1.patchDownload
 contrib/sepgsql/expected/create.out |   28 +++++
 contrib/sepgsql/hooks.c             |   11 ++-
 contrib/sepgsql/relation.c          |  213 +++++++++++++++++++++++------------
 contrib/sepgsql/sepgsql.h           |   11 ++-
 contrib/sepgsql/sql/create.sql      |    8 ++
 contrib/sepgsql/test_sepgsql        |   28 +++++
 src/backend/bootstrap/bootparse.y   |    3 +-
 src/backend/catalog/heap.c          |    7 +-
 src/backend/catalog/toasting.c      |    3 +-
 src/backend/commands/cluster.c      |    3 +-
 src/backend/commands/tablecmds.c    |    8 +-
 src/backend/executor/execMain.c     |   11 ++-
 src/include/catalog/heap.h          |    3 +-
 src/include/catalog/objectaccess.h  |   30 +++++
 14 files changed, 287 insertions(+), 80 deletions(-)

diff --git a/contrib/sepgsql/expected/create.out b/contrib/sepgsql/expected/create.out
index 3293a73..d450dc5 100644
--- a/contrib/sepgsql/expected/create.out
+++ b/contrib/sepgsql/expected/create.out
@@ -16,8 +16,36 @@ LOG:  SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_
 CREATE SCHEMA regtest_schema;
 LOG:  SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
 SET search_path = regtest_schema, public;
+CREATE TABLE regtest_table (x serial primary key, y text);
+NOTICE:  CREATE TABLE will create implicit sequence "regtest_table_x_seq" for serial column "regtest_table.x"
+LOG:  SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
+LOG:  SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_table_x_seq"
+LOG:  SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
+LOG:  SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="table regtest_table"
+LOG:  SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column tableoid"
+LOG:  SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column cmax"
+LOG:  SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column xmax"
+LOG:  SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column cmin"
+LOG:  SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column xmin"
+LOG:  SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column ctid"
+LOG:  SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column x"
+LOG:  SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column y"
+NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "regtest_table_pkey" for table "regtest_table"
+CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 10;
+LOG:  SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
+LOG:  SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_view_t:s0 tclass=db_view name="view regtest_view"
+CREATE SEQUENCE regtest_seq;
+LOG:  SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
+LOG:  SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_seq"
+CREATE TYPE regtest_type AS (a int, b text);
+LOG:  SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
 --
 -- clean-up 
 --
 DROP DATABASE IF EXISTS regtest_sepgsql_test_database;
 DROP SCHEMA IF EXISTS regtest_schema CASCADE;
+NOTICE:  drop cascades to 4 other objects
+DETAIL:  drop cascades to table regtest_table
+drop cascades to view regtest_view
+drop cascades to sequence regtest_seq
+drop cascades to type regtest_type
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 2b62a75..aa56d9c 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -146,6 +146,15 @@ sepgsql_object_prep_create(Oid classId, Oid objectId, int subId,
 				sepgsql_schema_prep_create(args->pg_namespace.nspname);
 			break;
 
+		case RelationRelationId:
+			sepgsql_relation_prep_create(cinfo,
+										 args->pg_class.relname,
+										 args->pg_class.relkind,
+										 args->pg_class.namespaceId,
+										 args->pg_class.tablespaceId,
+										 args->pg_class.tupdesc);
+			break;
+
 		default:
 			/* Ignore unsupported object classes */
 			break;
@@ -181,7 +190,7 @@ sepgsql_object_post_create(Oid classId, Oid objectId, int subId,
 
 		case RelationRelationId:
 			if (subId == 0)
-				sepgsql_relation_post_create(objectId);
+				sepgsql_relation_post_create(cinfo, objectId);
 			else
 				sepgsql_attribute_post_create(objectId, subId);
 			break;
diff --git a/contrib/sepgsql/relation.c b/contrib/sepgsql/relation.c
index 0767382..09f80d7 100644
--- a/contrib/sepgsql/relation.c
+++ b/contrib/sepgsql/relation.c
@@ -13,6 +13,7 @@
 #include "access/genam.h"
 #include "access/heapam.h"
 #include "access/sysattr.h"
+#include "catalog/heap.h"
 #include "catalog/indexing.h"
 #include "catalog/dependency.h"
 #include "catalog/pg_attribute.h"
@@ -113,102 +114,174 @@ sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
 }
 
 /*
- * sepgsql_relation_post_create
+ * sepgsql_relation_prep_create
  *
- * The post creation hook of relation/attribute
+ * This routine computes default security label of relation and columns
+ * if relation is a table, then checks permissions to create them.
  */
 void
-sepgsql_relation_post_create(Oid relOid)
+sepgsql_relation_prep_create(sepgsql_creation_info *info,
+							 const char *relname, char relkind,
+							 Oid namespaceId, Oid tablespaceId,
+							 TupleDesc tupdesc)
 {
-	Relation	rel;
-	ScanKeyData skey;
-	SysScanDesc sscan;
-	HeapTuple	tuple;
-	Form_pg_class classForm;
-	ObjectAddress object;
+	char	   *scontext;
+	char	   *tcontext;
+	char	   *ncontext;
+	char	   *acontext;
+	char		audit_name[2 * NAMEDATALEN + 16];
 	uint16		tclass;
-	char	   *scontext;		/* subject */
-	char	   *tcontext;		/* schema */
-	char	   *rcontext;		/* relation */
-	char	   *ccontext;		/* column */
+	ObjectAddress	object;
 
 	/*
-	 * Fetch catalog record of the new relation. Because pg_class entry is not
-	 * visible right now, we need to scan the catalog using SnapshotSelf.
+	 * so check db_schema:{add_name} permission
 	 */
-	rel = heap_open(RelationRelationId, AccessShareLock);
-
-	ScanKeyInit(&skey,
-				ObjectIdAttributeNumber,
-				BTEqualStrategyNumber, F_OIDEQ,
-				ObjectIdGetDatum(relOid));
-
-	sscan = systable_beginscan(rel, ClassOidIndexId, true,
-							   SnapshotSelf, 1, &skey);
+	object.classId = NamespaceRelationId;
+	object.objectId = namespaceId;
+	object.objectSubId = 0;
+	sepgsql_avc_check_perms(&object,
+							SEPG_CLASS_DB_SCHEMA,
+							SEPG_DB_SCHEMA__ADD_NAME,
+							getObjectDescription(&object),
+							true);
+	/*
+	 * compute default security label of new relation
+	 */
+	switch (relkind)
+	{
+		case RELKIND_RELATION:
+			tclass = SEPG_CLASS_DB_TABLE;
+			snprintf(audit_name, sizeof(audit_name), "table %s", relname);
+			break;
 
-	tuple = systable_getnext(sscan);
-	if (!HeapTupleIsValid(tuple))
-		elog(ERROR, "catalog lookup failed for relation %u", relOid);
+		case RELKIND_SEQUENCE:
+			tclass = SEPG_CLASS_DB_SEQUENCE;
+			snprintf(audit_name, sizeof(audit_name), "sequence %s", relname);
+			break;
 
-	classForm = (Form_pg_class) GETSTRUCT(tuple);
+		case RELKIND_VIEW:
+			tclass = SEPG_CLASS_DB_VIEW;
+			snprintf(audit_name, sizeof(audit_name), "view %s", relname);
+			break;
 
-	if (classForm->relkind == RELKIND_RELATION)
-		tclass = SEPG_CLASS_DB_TABLE;
-	else if (classForm->relkind == RELKIND_SEQUENCE)
-		tclass = SEPG_CLASS_DB_SEQUENCE;
-	else if (classForm->relkind == RELKIND_VIEW)
-		tclass = SEPG_CLASS_DB_VIEW;
-	else
-		goto out;				/* No need to assign individual labels */
-
-	/*
-	 * Compute a default security label when we create a new relation object
-	 * under the specified namespace.
-	 */
+		default:
+			/*
+			 * No need to check permission any more, if relation is not
+			 * a table, sequence or view.
+			 */
+			info->ncontext = NULL;
+			info->nattrs = 0;
+			info->acontexts = NULL;
+			return;
+	}
 	scontext = sepgsql_get_client_label();
-	tcontext = sepgsql_get_label(NamespaceRelationId,
-								 classForm->relnamespace, 0);
-	rcontext = sepgsql_compute_create(scontext, tcontext, tclass);
+	tcontext = sepgsql_get_label(NamespaceRelationId, namespaceId, 0);
+	ncontext = sepgsql_compute_create(scontext, tcontext, tclass);
+	info->ncontext = ncontext;
 
 	/*
-	 * Assign the default security label on the new relation
+	 * check db_xxx:{create} permission
 	 */
-	object.classId = RelationRelationId;
-	object.objectId = relOid;
-	object.objectSubId = 0;
-	SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, rcontext);
-
+	sepgsql_avc_check_perms_label(ncontext,
+								  tclass,
+								  SEPG_DB_TABLE__CREATE,
+								  audit_name,
+								  true);
 	/*
-	 * We also assigns a default security label on columns of the new regular
-	 * tables.
+	 * compute default security label of columns, and check
+	 * db_column:{create} permission, if relkind == RELKIND_RELATION
 	 */
-	if (classForm->relkind == RELKIND_RELATION)
+	if (relkind == RELKIND_RELATION)
 	{
-		AttrNumber	index;
+		AttrNumber	attnum;
+		int			shift = - FirstLowInvalidHeapAttributeNumber - 1;
 
-		ccontext = sepgsql_compute_create(scontext, rcontext,
+		/*
+		 * XXX - upcoming libselinux allows case handling when a new
+		 * object that has a particular name, but it is not supported
+		 * right now. So, we omit to compute default security label
+		 * for each columns, because it shall be uniform when same
+		 * client create columns under the same table.
+		 */
+		acontext = sepgsql_compute_create(scontext, ncontext,
 										  SEPG_CLASS_DB_COLUMN);
-		for (index = FirstLowInvalidHeapAttributeNumber + 1;
-			 index <= classForm->relnatts;
-			 index++)
+
+		info->nattrs = tupdesc->natts;
+		info->acontexts = palloc0(sizeof(const char *) *
+								  (tupdesc->natts + shift + 1));
+
+		for (attnum = FirstLowInvalidHeapAttributeNumber + 1;
+			 attnum <= tupdesc->natts;
+			 attnum++)
 		{
-			if (index == InvalidAttrNumber)
-				continue;
+			Form_pg_attribute	attr;
 
-			if (index == ObjectIdAttributeNumber && !classForm->relhasoids)
+			if (attnum == InvalidAttrNumber ||
+				(attnum == ObjectIdAttributeNumber && !tupdesc->tdhasoid))
+			{
+				info->acontexts[attnum + shift] = NULL;
 				continue;
+			}
+			/* check db_column:{create} permission */
+			if (attnum < 0)
+				attr = SystemAttributeDefinition(attnum, true);
+			else
+				attr = tupdesc->attrs[attnum - 1];
+			snprintf(audit_name, sizeof(audit_name), "table %s column %s",
+					 relname, NameStr(attr->attname));
 
-			object.classId = RelationRelationId;
-			object.objectId = relOid;
-			object.objectSubId = index;
-			SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ccontext);
+			sepgsql_avc_check_perms_label(acontext,
+										  SEPG_CLASS_DB_COLUMN,
+										  SEPG_DB_COLUMN__CREATE,
+										  audit_name,
+										  true);
+			info->acontexts[attnum + shift] = acontext;
 		}
-		pfree(ccontext);
 	}
-	pfree(rcontext);
-out:
-	systable_endscan(sscan);
-	heap_close(rel, AccessShareLock);
+	else
+	{
+		info->nattrs = 0;
+		info->acontexts = NULL;
+	}
+}
+
+/*
+ * sepgsql_relation_post_create
+ *
+ * The post creation hook of relation/attribute
+ */
+void
+sepgsql_relation_post_create(sepgsql_creation_info *info,
+							 Oid relationOid)
+{
+	ObjectAddress	object;
+	AttrNumber	attnum;
+	int			shift = - FirstLowInvalidHeapAttributeNumber - 1;
+
+	if (!info || !info->ncontext)
+		return;
+
+	object.classId = RelationRelationId;
+	object.objectId = relationOid;
+	object.objectSubId = 0;
+	SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, info->ncontext);
+
+	if (!info->acontexts)
+		return;
+
+	for (attnum = FirstLowInvalidHeapAttributeNumber + 1;
+		 attnum <= info->nattrs;
+		 attnum++)
+	{
+		if (!info->acontexts[attnum + shift])
+			continue;
+
+		object.classId = RelationRelationId;
+		object.objectId = relationOid;
+		object.objectSubId = attnum;
+		SetSecurityLabel(&object, SEPGSQL_LABEL_TAG,
+						 info->acontexts[attnum + shift]);
+	}
 }
 
 /*
diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h
index b4a3e4d..cd9c4c6 100644
--- a/contrib/sepgsql/sepgsql.h
+++ b/contrib/sepgsql/sepgsql.h
@@ -226,6 +226,10 @@ typedef struct {
 
 	/* a default security label to be assigned on */
 	const char *ncontext;
+
+	/* a default security label of columns (only for tables) */
+	AttrNumber	nattrs;
+	const char  **acontexts;
 } sepgsql_creation_info;
 
 /*
@@ -323,7 +327,12 @@ extern void sepgsql_schema_relabel(Oid namespaceId, const char *seclabel);
 extern void sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum);
 extern void sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
 						  const char *seclabel);
-extern void sepgsql_relation_post_create(Oid relOid);
+extern void sepgsql_relation_prep_create(sepgsql_creation_info *info,
+										 const char *relname, char relkind,
+										 Oid namespaceId, Oid tablespaceId,
+										 TupleDesc tupdesc);
+extern void sepgsql_relation_post_create(sepgsql_creation_info *info,
+										 Oid relationOid);
 extern void sepgsql_relation_relabel(Oid relOid, const char *seclabel);
 
 /*
diff --git a/contrib/sepgsql/sql/create.sql b/contrib/sepgsql/sql/create.sql
index 9ce1db9..4e12411 100644
--- a/contrib/sepgsql/sql/create.sql
+++ b/contrib/sepgsql/sql/create.sql
@@ -13,6 +13,14 @@ CREATE SCHEMA regtest_schema;
 
 SET search_path = regtest_schema, public;
 
+CREATE TABLE regtest_table (x serial primary key, y text);
+
+CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 10;
+
+CREATE SEQUENCE regtest_seq;
+
+CREATE TYPE regtest_type AS (a int, b text);
+
 --
 -- clean-up 
 --
diff --git a/contrib/sepgsql/test_sepgsql b/contrib/sepgsql/test_sepgsql
index 52237e6..4e43e97 100755
--- a/contrib/sepgsql/test_sepgsql
+++ b/contrib/sepgsql/test_sepgsql
@@ -163,6 +163,34 @@ if [ "${POLICY_STATUS}" != on ]; then
     exit 1
 fi
 
+# Verify that sepgsql_enable_users_ddl is active.
+echo -n "checking whether DDL is allowed     ... "
+POLICY_STATUS=`getsebool sepgsql_enable_users_ddl | awk '{print $3}'`
+echo ${POLICY_STATUS:-failed}
+if [ "${POLICY_STATUS}" != on ]; then
+    echo ""
+    echo "The SELinux boolean 'sepgsql_enable_users_ddl' must be"
+    echo "turned on in order to enable the rules necessary to run the"
+    echo "regression tests."
+    echo ""
+    if [ "${POLICY_STATUS}" = "" ]; then
+        echo "We attempted to determine the state of this Boolean using"
+        echo "'getsebool', but that command did not produce the expected"
+        echo "output.  Please verify that getsebool is available and in"
+        echo "your PATH."
+    else
+        echo "You can turn on this variable using the following commands:"
+        echo ""
+        echo "  \$ sudo setsebool sepgsql_enable_users_ddl on"
+        echo ""
+        echo "For security reasons, it is suggested that you turn off this"
+        echo "variable unless you want unprivileged users to execute"
+	echo "DDL commands."
+    fi
+    echo ""
+    exit 1
+fi
+
 # 'psql' command must be executable from test domain
 echo -n "checking whether we can run psql    ... "
 CMD_PSQL="${PG_BINDIR}/psql"
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index d0a0e92..4ca33ab 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -248,7 +248,8 @@ Boot_CreateStmt:
 													  ONCOMMIT_NOOP,
 													  (Datum) 0,
 													  false,
-													  true);
+													  true,
+													  (Datum) 0);
 						elog(DEBUG4, "relation created with OID %u", id);
 					}
 					do_end();
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index e11d896..84613f7 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -993,7 +993,8 @@ heap_create_with_catalog(const char *relname,
 						 OnCommitAction oncommit,
 						 Datum reloptions,
 						 bool use_user_acl,
-						 bool allow_system_table_mods)
+						 bool allow_system_table_mods,
+						 Datum hook_private)
 {
 	Relation	pg_class_desc;
 	Relation	new_rel_desc;
@@ -1279,7 +1280,9 @@ heap_create_with_catalog(const char *relname,
 	}
 
 	/* Post creation hook for new relation */
-	InvokeObjectAccessHook(OAT_POST_CREATE, RelationRelationId, relid, 0);
+	InvokeObjectAccessHookArg(OAT_POST_CREATE,
+							  RelationRelationId, relid, 0,
+							  hook_private);
 
 	/*
 	 * Store any supplied constraints and defaults.
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 3a40e8b..a28c423 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -228,7 +228,8 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
 										   ONCOMMIT_NOOP,
 										   reloptions,
 										   false,
-										   true);
+										   true,
+										   (Datum) 0);
 	Assert(toast_relid != InvalidOid);
 
 	/* make the toast relation visible, else heap_open will fail */
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index edec44d..08ad2bc 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -645,7 +645,8 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace)
 										  ONCOMMIT_NOOP,
 										  reloptions,
 										  false,
-										  true);
+										  true,
+										  (Datum) 0);
 	Assert(OIDNewHeap != InvalidOid);
 
 	ReleaseSysCache(tuple);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index c4622c0..a8b5a56 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -410,6 +410,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
 	List	   *rawDefaults;
 	List	   *cookedDefaults;
 	Datum		reloptions;
+	Datum		hook_private = 0;
 	ListCell   *listptr;
 	AttrNumber	attnum;
 	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
@@ -575,6 +576,10 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
 		}
 	}
 
+	/* Prep-creation hook for new relation */
+	InvokePrepCreateRelationHook(&hook_private, relname, relkind,
+								 namespaceId, tablespaceId, descriptor);
+
 	/*
 	 * Create the relation.  Inherited defaults and constraints are passed in
 	 * for immediate handling --- since they don't need parsing, they can be
@@ -599,7 +604,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
 										  stmt->oncommit,
 										  reloptions,
 										  true,
-										  allowSystemTableMods);
+										  allowSystemTableMods,
+										  hook_private);
 
 	/* Store inheritance information for new rel. */
 	StoreCatalogInheritance(relationId, inheritOids);
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index fd7a9ed..8d97d1e 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -43,6 +43,7 @@
 #include "access/xact.h"
 #include "catalog/heap.h"
 #include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
 #include "catalog/toasting.h"
 #include "commands/tablespace.h"
 #include "commands/trigger.h"
@@ -2393,6 +2394,7 @@ OpenIntoRel(QueryDesc *queryDesc)
 	Oid			namespaceId;
 	Oid			tablespaceId;
 	Datum		reloptions;
+	Datum		hook_private = 0;
 	Oid			intoRelationId;
 	DR_intorel *myState;
 	static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
@@ -2458,6 +2460,12 @@ OpenIntoRel(QueryDesc *queryDesc)
 						   get_tablespace_name(tablespaceId));
 	}
 
+	/* Prep-creation hook for new relation */
+	InvokePrepCreateRelationHook(&hook_private, intoName,
+								 RELKIND_RELATION,
+								 namespaceId, tablespaceId,
+								 queryDesc->tupDesc);
+
 	/* Parse and validate any reloptions */
 	reloptions = transformRelOptions((Datum) 0,
 									 into->options,
@@ -2486,7 +2494,8 @@ OpenIntoRel(QueryDesc *queryDesc)
 											  into->onCommit,
 											  reloptions,
 											  true,
-											  allowSystemTableMods);
+											  allowSystemTableMods,
+											  hook_private);
 	Assert(intoRelationId != InvalidOid);
 
 	/*
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index aee2d88..ca4d6a1 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -65,7 +65,8 @@ extern Oid heap_create_with_catalog(const char *relname,
 						 OnCommitAction oncommit,
 						 Datum reloptions,
 						 bool use_user_acl,
-						 bool allow_system_table_mods);
+						 bool allow_system_table_mods,
+						 Datum hook_private);
 
 extern void heap_drop_with_catalog(Oid relid);
 
diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h
index cb5c396..d151ee5 100644
--- a/src/include/catalog/objectaccess.h
+++ b/src/include/catalog/objectaccess.h
@@ -10,6 +10,8 @@
 #ifndef OBJECTACCESS_H
 #define OBJECTACCESS_H
 
+#include "access/tupdesc.h"
+
 /*
  * Object access hooks are intended to be called just before or just after
  * performing certain actions on a SQL object.	This is intended as
@@ -55,6 +57,14 @@ typedef union
 		Datum	   *private;	/* common */
 		const char *nspname;	/* name of new schema */
 	} pg_namespace;
+	struct {
+		Datum	   *private;	/* common */
+		const char *relname;	/* name of new relation */
+		char		relkind;
+		Oid			namespaceId;
+		Oid			tablespaceId;
+		TupleDesc	tupdesc;
+	} pg_class;
 } ObjectAccessCreateObjectArgs;
 
 /*
@@ -121,4 +131,24 @@ extern PGDLLIMPORT object_access_hook_type object_access_hook;
 		}																\
 	} while(0)
 
+#define InvokePrepCreateRelationHook(_private,_relname,_relkind,		\
+									 _namespace,_tablespace,_tupdesc)	\
+	do {																\
+		if (object_access_hook)											\
+		{																\
+			ObjectAccessCreateObjectArgs	__args;						\
+																		\
+			__args.pg_class.private = (_private);						\
+			__args.pg_class.relname = (_relname);						\
+			__args.pg_class.relkind = (_relkind);						\
+			__args.pg_class.namespaceId = (_namespace);					\
+			__args.pg_class.tablespaceId = (_tablespace);				\
+			__args.pg_class.tupdesc = (_tupdesc);						\
+																		\
+			(*object_access_hook)(OAT_PREP_CREATE,						\
+								  RelationRelationId, InvalidOid, 0,	\
+								  PointerGetDatum(&__args));			\
+		}																\
+	} while(0)
+
 #endif   /* OBJECTACCESS_H */
pgsql-v9.2-prep-creation-hook-part-4.v1.patchapplication/octet-stream; name=pgsql-v9.2-prep-creation-hook-part-4.v1.patchDownload
 contrib/sepgsql/expected/create.out  |   14 +++-
 contrib/sepgsql/hooks.c              |   12 +++-
 contrib/sepgsql/proc.c               |  144 +++++++++++++++++++++++++--------
 contrib/sepgsql/sepgsql.h            |    7 ++-
 contrib/sepgsql/sql/create.sql       |    8 ++
 src/backend/catalog/pg_aggregate.c   |    6 +-
 src/backend/catalog/pg_proc.c        |    7 +-
 src/backend/commands/aggregatecmds.c |   11 +++-
 src/backend/commands/functioncmds.c  |    8 ++-
 src/backend/commands/proclang.c      |    9 ++-
 src/backend/commands/typecmds.c      |    3 +-
 src/include/catalog/objectaccess.h   |   28 +++++++
 src/include/catalog/pg_aggregate.h   |    3 +-
 src/include/catalog/pg_proc_fn.h     |    3 +-
 14 files changed, 213 insertions(+), 50 deletions(-)

diff --git a/contrib/sepgsql/expected/create.out b/contrib/sepgsql/expected/create.out
index d450dc5..68d7e61 100644
--- a/contrib/sepgsql/expected/create.out
+++ b/contrib/sepgsql/expected/create.out
@@ -39,13 +39,25 @@ LOG:  SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfine
 LOG:  SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="sequence regtest_seq"
 CREATE TYPE regtest_type AS (a int, b text);
 LOG:  SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
+CREATE FUNCTION regtest_func (text) RETURNS bool LANGUAGE plpgsql
+    AS 'BEGIN RAISE NOTICE ''hello => %'', $1; RETURN true; END';
+LOG:  SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
+LOG:  SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function regtest_func(text)"
+CREATE AGGREGATE regtest_agg (
+    sfunc1 = int4pl, basetype = int4, stype1 = int4,
+    initcond1 = '0'
+);
+LOG:  SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="schema regtest_schema"
+LOG:  SELinux: allowed { create } scontext=unconfined_u:unconfined_r:unconfined_t:s0 tcontext=unconfined_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="function regtest_agg(integer)"
 --
 -- clean-up 
 --
 DROP DATABASE IF EXISTS regtest_sepgsql_test_database;
 DROP SCHEMA IF EXISTS regtest_schema CASCADE;
-NOTICE:  drop cascades to 4 other objects
+NOTICE:  drop cascades to 6 other objects
 DETAIL:  drop cascades to table regtest_table
 drop cascades to view regtest_view
 drop cascades to sequence regtest_seq
 drop cascades to type regtest_type
+drop cascades to function regtest_func(text)
+drop cascades to function regtest_agg(integer)
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index aa56d9c..1eeec1e 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -155,6 +155,15 @@ sepgsql_object_prep_create(Oid classId, Oid objectId, int subId,
 										 args->pg_class.tupdesc);
 			break;
 
+		case ProcedureRelationId:
+			cinfo->ncontext =
+				sepgsql_proc_prep_create(args->pg_proc.proname,
+										 args->pg_proc.proargs,
+										 args->pg_proc.namespaceId,
+										 args->pg_proc.languageId,
+										 args->pg_proc.leakproof);
+			break;
+
 		default:
 			/* Ignore unsupported object classes */
 			break;
@@ -196,7 +205,8 @@ sepgsql_object_post_create(Oid classId, Oid objectId, int subId,
 			break;
 
 		case ProcedureRelationId:
-			sepgsql_proc_post_create(objectId);
+			sepgsql_proc_post_create(objectId,
+									 !cinfo ? NULL : cinfo->ncontext);
 			break;
 
 		default:
diff --git a/contrib/sepgsql/proc.c b/contrib/sepgsql/proc.c
index 9630d45..c0e2c0e 100644
--- a/contrib/sepgsql/proc.c
+++ b/contrib/sepgsql/proc.c
@@ -18,6 +18,7 @@
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_proc.h"
 #include "commands/seclabel.h"
+#include "utils/builtins.h"
 #include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
 #include "utils/tqual.h"
@@ -25,55 +26,131 @@
 #include "sepgsql.h"
 
 /*
- * sepgsql_proc_post_create
+ * sepgsql_proc_prep_create
+ *
+ *
  *
- * This routine assigns a default security label on a newly defined
- * procedure.
  */
-void
-sepgsql_proc_post_create(Oid functionId)
+const char *
+sepgsql_proc_prep_create(const char *proname,
+						 oidvector *proargs,
+						 Oid namespaceId,
+						 Oid languageId,
+						 bool leakproof)
 {
-	Relation	rel;
-	ScanKeyData skey;
-	SysScanDesc sscan;
-	HeapTuple	tuple;
-	Oid			namespaceId;
-	ObjectAddress object;
 	char	   *scontext;
 	char	   *tcontext;
 	char	   *ncontext;
+	uint32		required;
+	StringInfoData	audit_name;
+	ObjectAddress	object;
+	int			i;
 
 	/*
-	 * Fetch namespace of the new procedure. Because pg_proc entry is not
-	 * visible right now, we need to scan the catalog using SnapshotSelf.
+	 * check db_schema:{add_name} permission
 	 */
-	rel = heap_open(ProcedureRelationId, AccessShareLock);
+	object.classId = NamespaceRelationId;
+	object.objectId = namespaceId;
+	object.objectSubId = 0;
+	sepgsql_avc_check_perms(&object,
+							SEPG_CLASS_DB_SCHEMA,
+							SEPG_DB_SCHEMA__ADD_NAME,
+							getObjectDescription(&object),
+							true);
 
-	ScanKeyInit(&skey,
-				ObjectIdAttributeNumber,
-				BTEqualStrategyNumber, F_OIDEQ,
-				ObjectIdGetDatum(functionId));
+	/*
+	 * compute default security label of new function
+	 */
+	scontext = sepgsql_get_client_label();
+	tcontext = sepgsql_get_label(NamespaceRelationId, namespaceId, 0);
+	ncontext = sepgsql_compute_create(scontext, tcontext,
+									  SEPG_CLASS_DB_PROCEDURE);
 
-	sscan = systable_beginscan(rel, ProcedureOidIndexId, true,
-							   SnapshotSelf, 1, &skey);
+	/*
+	 * check db_procedure:{create (install)} permission
+	 */
+	required = SEPG_DB_PROCEDURE__CREATE;
+	if (leakproof)
+		required |= SEPG_DB_PROCEDURE__INSTALL;
 
-	tuple = systable_getnext(sscan);
-	if (!HeapTupleIsValid(tuple))
-		elog(ERROR, "catalog lookup failed for proc %u", functionId);
+	/*
+	 * XXX - format_procedure() is not available at this point,
+	 * so we need to construct human readable function names by hand.
+	 */
+	initStringInfo(&audit_name);
+	appendStringInfo(&audit_name, _("function %s("),
+					 quote_identifier(proname));
+	for (i = 0; i < proargs->dim1; i++)
+	{
+		if (i > 0)
+			appendStringInfoChar(&audit_name, ',');
+		appendStringInfoString(&audit_name,
+							   format_type_be(proargs->values[i]));
+	}
+	appendStringInfoChar(&audit_name, ')');
+
+	sepgsql_avc_check_perms_label(ncontext,
+								  SEPG_CLASS_DB_PROCEDURE,
+								  required,
+								  audit_name.data,
+								  true);
 
-	namespaceId = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace;
+	/*
+	 * XXX - db_language:{implement} also shoule be checked here.
+	 */
+	return ncontext;
+}
 
-	systable_endscan(sscan);
-	heap_close(rel, AccessShareLock);
+/*
+ * sepgsql_proc_post_create
+ *
+ * This routine assigns a default security label on a newly defined
+ * procedure.
+ */
+void
+sepgsql_proc_post_create(Oid functionId, const char *ncontext)
+{
+	ObjectAddress	object;
 
 	/*
-	 * Compute a default security label when we create a new procedure object
-	 * under the specified namespace.
+	 * In the case when system internal functions are constructed,
+	 * prep-creation hook is not invoked, security label to be
+	 * assigned on is not also computed yet. Thus, we try to obtain 
+	 * the default security label based on the properties of system
+	 * catalogs.
 	 */
-	scontext = sepgsql_get_client_label();
-	tcontext = sepgsql_get_label(NamespaceRelationId, namespaceId, 0);
-	ncontext = sepgsql_compute_create(scontext, tcontext,
-									  SEPG_CLASS_DB_PROCEDURE);
+	if (!ncontext)
+	{
+		Relation	rel;
+		ScanKeyData skey;
+		SysScanDesc sscan;
+		HeapTuple	tuple;
+		Form_pg_proc	procForm;
+
+		rel = heap_open(ProcedureRelationId, AccessShareLock);
+
+		ScanKeyInit(&skey,
+					ObjectIdAttributeNumber,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(functionId));
+
+		sscan = systable_beginscan(rel, ProcedureOidIndexId, true,
+								   SnapshotSelf, 1, &skey);
+
+		tuple = systable_getnext(sscan);
+		if (!HeapTupleIsValid(tuple))
+			elog(ERROR, "catalog lookup failed for proc %u", functionId);
+
+		procForm = (Form_pg_proc) GETSTRUCT(tuple);
+
+		ncontext = sepgsql_proc_prep_create(NameStr(procForm->proname),
+											&procForm->proargtypes,
+											procForm->pronamespace,
+											procForm->prolang,
+											false);
+		systable_endscan(sscan);
+		heap_close(rel, AccessShareLock);
+	}
 
 	/*
 	 * Assign the default security label on a new procedure
@@ -82,9 +159,6 @@ sepgsql_proc_post_create(Oid functionId)
 	object.objectId = functionId;
 	object.objectSubId = 0;
 	SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext);
-
-	pfree(tcontext);
-	pfree(ncontext);
 }
 
 /*
diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h
index cd9c4c6..56d2056 100644
--- a/contrib/sepgsql/sepgsql.h
+++ b/contrib/sepgsql/sepgsql.h
@@ -338,7 +338,12 @@ extern void sepgsql_relation_relabel(Oid relOid, const char *seclabel);
 /*
  * proc.c
  */
-extern void sepgsql_proc_post_create(Oid functionId);
+extern const char *sepgsql_proc_prep_create(const char *proname,
+											oidvector *proargs,
+											Oid namespaceId,
+											Oid languageId,
+											bool leakproof);
+extern void sepgsql_proc_post_create(Oid functionId, const char *ncontext);
 extern void sepgsql_proc_relabel(Oid functionId, const char *seclabel);
 
 #endif   /* SEPGSQL_H */
diff --git a/contrib/sepgsql/sql/create.sql b/contrib/sepgsql/sql/create.sql
index 4e12411..bc4a664 100644
--- a/contrib/sepgsql/sql/create.sql
+++ b/contrib/sepgsql/sql/create.sql
@@ -21,6 +21,14 @@ CREATE SEQUENCE regtest_seq;
 
 CREATE TYPE regtest_type AS (a int, b text);
 
+CREATE FUNCTION regtest_func (text) RETURNS bool LANGUAGE plpgsql
+    AS 'BEGIN RAISE NOTICE ''hello => %'', $1; RETURN true; END';
+
+CREATE AGGREGATE regtest_agg (
+    sfunc1 = int4pl, basetype = int4, stype1 = int4,
+    initcond1 = '0'
+);
+
 --
 -- clean-up 
 --
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index 86e8c6b..0cd0ca3 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -50,7 +50,8 @@ AggregateCreate(const char *aggName,
 				List *aggfinalfnName,
 				List *aggsortopName,
 				Oid aggTransType,
-				const char *agginitval)
+				const char *agginitval,
+				Datum hook_private)
 {
 	Relation	aggdesc;
 	HeapTuple	tup;
@@ -229,7 +230,8 @@ AggregateCreate(const char *aggName,
 							  NIL,		/* parameterDefaults */
 							  PointerGetDatum(NULL),	/* proconfig */
 							  1,	/* procost */
-							  0);		/* prorows */
+							  0,	/* prorows */
+							  hook_private);	/* hook_private */
 
 	/*
 	 * Okay to create the pg_aggregate entry.
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 8378c36..7fb5ef2 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -85,7 +85,8 @@ ProcedureCreate(const char *procedureName,
 				List *parameterDefaults,
 				Datum proconfig,
 				float4 procost,
-				float4 prorows)
+				float4 prorows,
+				Datum hook_private)
 {
 	Oid			retval;
 	int			parameterCount;
@@ -646,7 +647,9 @@ ProcedureCreate(const char *procedureName,
 	heap_freetuple(tup);
 
 	/* Post creation hook for new function */
-	InvokeObjectAccessHook(OAT_POST_CREATE, ProcedureRelationId, retval, 0);
+	InvokeObjectAccessHookArg(OAT_POST_CREATE,
+							  ProcedureRelationId, retval, 0,
+							  hook_private);
 
 	heap_close(rel, RowExclusiveLock);
 
diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c
index a2122c1..f02a467 100644
--- a/src/backend/commands/aggregatecmds.c
+++ b/src/backend/commands/aggregatecmds.c
@@ -25,7 +25,9 @@
 #include "access/heapam.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
 #include "catalog/pg_aggregate.h"
+#include "catalog/pg_language.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
@@ -60,6 +62,7 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
 	Oid		   *aggArgTypes;
 	int			numArgs;
 	Oid			transTypeId;
+	Datum		hook_private = 0;
 	ListCell   *pl;
 
 	/* Convert list of names to a name and namespace */
@@ -192,6 +195,11 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
 							format_type_be(transTypeId))));
 	}
 
+	/* Prep-creation hook for new aggregate functions */
+	InvokePrepCreateProcedureHook(&hook_private, aggName,
+								  buildoidvector(aggArgTypes, numArgs),
+								  aggNamespace, INTERNALlanguageId, false);
+
 	/*
 	 * Most of the argument-checking is done inside of AggregateCreate
 	 */
@@ -203,7 +211,8 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
 					finalfuncName,		/* final function name */
 					sortoperatorName,	/* sort operator name */
 					transTypeId,	/* transition data type */
-					initval);	/* initial condition */
+					initval,	/* initial condition */
+					hook_private);
 }
 
 
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 92abd44..33c65be 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -801,6 +801,7 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
 	HeapTuple	languageTuple;
 	Form_pg_language languageStruct;
 	List	   *as_clause;
+	Datum		hook_private = 0;
 
 	/* Convert list of names to a name and namespace */
 	namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname,
@@ -935,6 +936,10 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("ROWS is not applicable when function does not return a set")));
 
+	/* Prep-creation hook for new function */
+	InvokePrepCreateProcedureHook(&hook_private, funcname, parameterTypes,
+								  namespaceId, languageOid, false);
+
 	/*
 	 * And now that we have all the parameters, and know we're permitted to do
 	 * so, go ahead and create the function.
@@ -960,7 +965,8 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
 					parameterDefaults,
 					PointerGetDatum(proconfig),
 					procost,
-					prorows);
+					prorows,
+					hook_private);
 }
 
 
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
index 98770c5..1392dca 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -146,7 +146,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 										 NIL,
 										 PointerGetDatum(NULL),
 										 1,
-										 0);
+										 0,
+										 (Datum) 0);
 		}
 
 		/*
@@ -181,7 +182,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 											NIL,
 											PointerGetDatum(NULL),
 											1,
-											0);
+											0,
+											(Datum) 0);
 			}
 		}
 		else
@@ -219,7 +221,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 										 NIL,
 										 PointerGetDatum(NULL),
 										 1,
-										 0);
+										 0,
+										 (Datum) 0);
 			}
 		}
 		else
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 91488bb..307dcbf 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1461,7 +1461,8 @@ makeRangeConstructor(char *name, Oid namespace, Oid rangeOid, Oid subtype)
 			NIL, /* parameterDefaults */
 			PointerGetDatum(NULL), /* proconfig */
 			1.0, /* procost */
-			0.0); /* prorows */
+			0.0, /* prorows */
+			(Datum) 0);	/* hook_private */
 
 		/*
 		 * Make the constructor internally-dependent on the range type so that
diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h
index d151ee5..db57e03 100644
--- a/src/include/catalog/objectaccess.h
+++ b/src/include/catalog/objectaccess.h
@@ -65,6 +65,14 @@ typedef union
 		Oid			tablespaceId;
 		TupleDesc	tupdesc;
 	} pg_class;
+	struct {
+		Datum	   *private;
+		const char *proname;
+		oidvector  *proargs;
+		Oid			namespaceId;
+		Oid			languageId;
+		bool		leakproof;
+	} pg_proc;
 } ObjectAccessCreateObjectArgs;
 
 /*
@@ -151,4 +159,24 @@ extern PGDLLIMPORT object_access_hook_type object_access_hook;
 		}																\
 	} while(0)
 
+#define InvokePrepCreateProcedureHook(_private,_proname,_proargs,		\
+									  _namespace,_language,_leakproof)	\
+	do {																\
+		if (object_access_hook)											\
+		{																\
+			ObjectAccessCreateObjectArgs	__args;						\
+																		\
+			__args.pg_proc.private = (_private);						\
+			__args.pg_proc.proname = (_proname);						\
+			__args.pg_proc.proargs = (_proargs);						\
+			__args.pg_proc.namespaceId = (_namespace);					\
+			__args.pg_proc.languageId = (_language);					\
+			__args.pg_proc.leakproof = (_leakproof);					\
+																		\
+			(*object_access_hook)(OAT_PREP_CREATE,						\
+								  ProcedureRelationId, InvalidOid, 0,	\
+								  PointerGetDatum(&__args));			\
+		}																\
+	} while(0)
+
 #endif   /* OBJECTACCESS_H */
diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h
index 26966d2..7b2d172 100644
--- a/src/include/catalog/pg_aggregate.h
+++ b/src/include/catalog/pg_aggregate.h
@@ -237,6 +237,7 @@ extern void AggregateCreate(const char *aggName,
 				List *aggfinalfnName,
 				List *aggsortopName,
 				Oid aggTransType,
-				const char *agginitval);
+				const char *agginitval,
+				Datum hook_private);
 
 #endif   /* PG_AGGREGATE_H */
diff --git a/src/include/catalog/pg_proc_fn.h b/src/include/catalog/pg_proc_fn.h
index 09d779f..a28786f 100644
--- a/src/include/catalog/pg_proc_fn.h
+++ b/src/include/catalog/pg_proc_fn.h
@@ -37,7 +37,8 @@ extern Oid ProcedureCreate(const char *procedureName,
 				List *parameterDefaults,
 				Datum proconfig,
 				float4 procost,
-				float4 prorows);
+				float4 prorows,
+				Datum hook_private);
 
 extern bool function_parse_error_transpose(const char *prosrc);