security hooks on object creation
The attached patch provides plugin modules a hook just after object
creation time. In typical use cases, it enables to assign default
security labels on object creation by the external security providers.
As Robert suggested before, it provides a generic purpose main hook.
It takes an enum of ObjectAccessType which informs plugins what kind
of accesses are required, and identifier of the object to be referenced.
But, in this version, no additional information, such as new name in
ALTER xxx RENAME TO, are not supported.
The ObjectAccessType is defined as follows:
typedef enum ObjectAccessType {
OAT_POST_CREATE, /* Post creation fixups; such as security labeling */
} ObjectAccessType;
We will support more complete kind of access types in the future version,
however, we focus on default labeling rather than DDL permissions right
now, so only OAT_POST_CREATE is defined here.
Perhaps, we will add OAT_ALTER, OAT_DROP, OAT_COMMENT and so on.
In this patch, I put hooks on the place just after creation of database
objects that we can assign security labels. (schema, relation, attribute,
procedure, language, type, large object)
However, I didn't touch or move CommandCounterIncrement() yet, although
we had a long discussion MVCC visibility of new object.
Because I'm not clear whether it is really preferable to inject CCIs
onto random points such as TypeCreate() or ProcedureCreate() under
development of the version killed by myself.
(In other words, it was simply ugly...)
At least, we can see the new entries with SnapshotSelf, although we will
pay performance penalty. If so, it is an idea not to touch anything
related to CCIs.
The purpose of post creation hooks are assignment of default security
labels, not DDL permissions. So, it is not a bad idea not to touch
routines related to CCIs in the earlier version of external security
provider.
In this patch, we put InvokeObjectAccessHook0 on the following functions.
- heap_create_with_catalog() for relations/attributes
- ATExecAddColumn() for attributes
- NamespaceCreate() for schemas
- ProcedureCreate() for aggregates/functions
- TypeCreate() and TypeShellMake() for types
- create_proc_lang() for procedural languages
- inv_create() for large objects
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
Attachments:
pgsql-object-creation.1.patchtext/x-patch; name=pgsql-object-creation.1.patchDownload
*** a/src/backend/catalog/heap.c
--- b/src/backend/catalog/heap.c
***************
*** 61,66 ****
--- 61,67 ----
#include "storage/freespace.h"
#include "storage/smgr.h"
#include "utils/acl.h"
+ #include "utils/esp.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
***************
*** 1189,1194 **** heap_create_with_catalog(const char *relname,
--- 1190,1202 ----
}
/*
+ * If installed, ESP can assign initial properties (such as security
+ * labels) of the relation.
+ */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ RelationRelationId, relid, 0);
+
+ /*
* Store any supplied constraints and defaults.
*
* NB: this may do a CommandCounterIncrement and rebuild the relcache
*** a/src/backend/catalog/pg_namespace.c
--- b/src/backend/catalog/pg_namespace.c
***************
*** 19,24 ****
--- 19,25 ----
#include "catalog/indexing.h"
#include "catalog/pg_namespace.h"
#include "utils/builtins.h"
+ #include "utils/esp.h"
#include "utils/rel.h"
#include "utils/syscache.h"
***************
*** 75,79 **** NamespaceCreate(const char *nspName, Oid ownerId)
--- 76,87 ----
/* Record dependency on owner */
recordDependencyOnOwner(NamespaceRelationId, nspoid, ownerId);
+ /*
+ * If installed, ESP can assign initial properties (such as security
+ * labels) of the new namespace.
+ */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ NamespaceRelationId, nspoid, 0);
+
return nspoid;
}
*** a/src/backend/catalog/pg_proc.c
--- b/src/backend/catalog/pg_proc.c
***************
*** 32,37 ****
--- 32,38 ----
#include "tcop/pquery.h"
#include "tcop/tcopprot.h"
#include "utils/acl.h"
+ #include "utils/esp.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
***************
*** 614,619 **** ProcedureCreate(const char *procedureName,
--- 615,627 ----
nnewmembers, newmembers);
}
+ /*
+ * If installed, ESP can assign initial properties (such as security
+ * labels) of the new function.
+ */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ ProcedureRelationId, retval, 0);
+
heap_freetuple(tup);
heap_close(rel, RowExclusiveLock);
*** a/src/backend/catalog/pg_type.c
--- b/src/backend/catalog/pg_type.c
***************
*** 26,31 ****
--- 26,32 ----
#include "miscadmin.h"
#include "parser/scansup.h"
#include "utils/acl.h"
+ #include "utils/esp.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
***************
*** 156,161 **** TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
--- 157,169 ----
false);
/*
+ * If installed, ESP can assign initial properties (such as security
+ * labels) of the new shell type.
+ */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ TypeRelationId, typoid, 0);
+
+ /*
* clean up and return the type-oid
*/
heap_freetuple(tup);
***************
*** 456,461 **** TypeCreate(Oid newTypeOid,
--- 464,476 ----
rebuildDeps);
/*
+ * If installed, ESP can assign initial properties (such as security
+ * labels) of the new type.
+ */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ TypeRelationId, typeObjectId, 0);
+
+ /*
* finish up
*/
heap_close(pg_type_desc, RowExclusiveLock);
*** a/src/backend/commands/proclang.c
--- b/src/backend/commands/proclang.c
***************
*** 31,36 ****
--- 31,37 ----
#include "parser/parser.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+ #include "utils/esp.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
***************
*** 426,431 **** create_proc_lang(const char *languageName, bool replace,
--- 427,440 ----
}
heap_close(rel, RowExclusiveLock);
+
+ /*
+ * If installed, ESP can assign initial properties (such as security
+ * labels) of the new procedural language.
+ */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ LanguageRelationId,
+ myself.objectId, 0);
}
/*
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 68,73 ****
--- 68,74 ----
#include "storage/smgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+ #include "utils/esp.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
***************
*** 4024,4029 **** ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
--- 4025,4037 ----
heap_close(pgclass, RowExclusiveLock);
+ /*
+ * If installed, ESP can assign initial properties (such as security
+ * labels) of the column.
+ */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ RelationRelationId, myrelid, newattnum);
+
/* Make the attribute's catalog entry visible */
CommandCounterIncrement();
*** a/src/backend/storage/large_object/inv_api.c
--- b/src/backend/storage/large_object/inv_api.c
***************
*** 44,49 ****
--- 44,50 ----
#include "libpq/libpq-fs.h"
#include "miscadmin.h"
#include "storage/large_object.h"
+ #include "utils/esp.h"
#include "utils/fmgroids.h"
#include "utils/rel.h"
#include "utils/resowner.h"
***************
*** 219,224 **** inv_create(Oid lobjId)
--- 220,232 ----
lobjId_new, GetUserId());
/*
+ * If installed, ESP can assign initial properties (such as security
+ * labels) of the new procedural language.
+ */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ LargeObjectRelationId, lobjId_new, 0);
+
+ /*
* Advance command counter to make new tuple visible to later operations.
*/
CommandCounterIncrement();
*** a/src/backend/utils/adt/Makefile
--- b/src/backend/utils/adt/Makefile
***************
*** 17,23 **** endif
OBJS = acl.o arrayfuncs.o array_userfuncs.o arrayutils.o bool.o \
cash.o char.o date.o datetime.o datum.o domains.o \
! enum.o float.o format_type.o \
geo_ops.o geo_selfuncs.o int.o int8.o like.o lockfuncs.o \
misc.o nabstime.o name.o numeric.o numutils.o \
oid.o oracle_compat.o pseudotypes.o rowtypes.o \
--- 17,23 ----
OBJS = acl.o arrayfuncs.o array_userfuncs.o arrayutils.o bool.o \
cash.o char.o date.o datetime.o datum.o domains.o \
! enum.o esp.o float.o format_type.o \
geo_ops.o geo_selfuncs.o int.o int8.o like.o lockfuncs.o \
misc.o nabstime.o name.o numeric.o numutils.o \
oid.o oracle_compat.o pseudotypes.o rowtypes.o \
*** /dev/null
--- b/src/backend/utils/adt/esp.c
***************
*** 0 ****
--- 1,70 ----
+ /*-------------------------------------------------------------------------
+ *
+ * esp.c
+ * External security provider hooks and related routines.
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/esp.c
+ *
+ *-------------------------------------------------------------------------
+ */
+ #include "postgres.h"
+
+ #include "nodes/pg_list.h"
+ #include "utils/esp.h"
+ #include "utils/memutils.h"
+
+ typedef struct
+ {
+ const char *provider_name;
+ check_object_access_type security_hook;
+ } SecurityProvider;
+
+ static List *security_provider_list = NIL;
+
+ /*
+ * invoke_object_access_hook
+ *
+ * It invokes the main security hook with the supplied argument.
+ * If and when no external security providers are installed,
+ * it shall do nothing.
+ */
+ void
+ invoke_object_access_hook(ObjectAccessType access,
+ Oid classId,
+ Oid objectId,
+ int subId)
+ {
+ ListCell *cell;
+
+ foreach (cell, security_provider_list)
+ {
+ SecurityProvider *provider = lfirst(cell);
+
+ if (provider->security_hook)
+ (*provider->security_hook)(access, classId, objectId, subId);
+ }
+ }
+
+ /*
+ * register_security_provider
+ *
+ * It allows to install plugin modules of external security providers
+ */
+ void
+ register_security_provider(const char *provider_name,
+ check_object_access_type security_hook)
+ {
+ SecurityProvider *provider;
+ MemoryContext oldcxt;
+
+ oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+ provider = palloc(sizeof(SecurityProvider));
+ provider->provider_name = pstrdup(provider_name);
+ provider->security_hook = security_hook;
+ security_provider_list = lappend(security_provider_list, provider);
+ MemoryContextSwitchTo(oldcxt);
+ }
*** /dev/null
--- b/src/include/utils/esp.h
***************
*** 0 ****
--- 1,42 ----
+ /*
+ * esp.h
+ *
+ * Definitions for external security providers
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ */
+ #ifndef ESP_H
+ #define ESP_H
+
+ typedef enum ObjectAccessType {
+ OAT_POST_CREATE, /* Post creation fixups; such as security labeling */
+ } ObjectAccessType;
+
+ /*
+ * Definition of the main security hook type
+ */
+ typedef void (*check_object_access_type)(ObjectAccessType access,
+ Oid classId,
+ Oid objectId,
+ int subId);
+ /*
+ * The main security hook to handle most of security events
+ */
+ extern void invoke_object_access_hook(ObjectAccessType access,
+ Oid classId,
+ Oid objectId,
+ int subId);
+ /*
+ * Utility macros
+ */
+ #define InvokeObjectAccessHook0(access,classId,objectId,subId) \
+ invoke_object_access_hook((access),(classId),(objectId),(subId))
+
+ /*
+ * An API to register security hooks
+ */
+ extern void register_security_provider(const char *provider,
+ check_object_access_type security_hook);
+
+ #endif /* ESP_H */
2010/11/9 KaiGai Kohei <kaigai@ak.jp.nec.com>:
The attached patch provides plugin modules a hook just after object
creation time. In typical use cases, it enables to assign default
security labels on object creation by the external security providers.
It looks like "DDL Trigger" on other database products.
Do we need to consider both security hooks and DDL triggers now?
Or, is it enough to design DLL triggers after the hooks are merged?
Low-level hooks might be better for security providers because
SQL-level triggers could be uninstall by superusers.
--
Itagaki Takahiro
(2010/11/09 20:34), Itagaki Takahiro wrote:
2010/11/9 KaiGai Kohei<kaigai@ak.jp.nec.com>:
The attached patch provides plugin modules a hook just after object
creation time. In typical use cases, it enables to assign default
security labels on object creation by the external security providers.It looks like "DDL Trigger" on other database products.
Do we need to consider both security hooks and DDL triggers now?
Or, is it enough to design DLL triggers after the hooks are merged?
Low-level hooks might be better for security providers because
SQL-level triggers could be uninstall by superusers.
An interesting viewpoint. Does the DDL trigger allow us to do something
on CREATE/ALTER/DROP command?
One thing we need to pay attention is that CREATE command is an exception
from any other DDL commands, because the database object to be modified
does not exist before the actual works. So, I'm saying we need both of
prep/post creation hooks in the world of complete features.
Meanwhile, I don't think we need security hooks post ALTER/DROP commands.
Thus, we will put security hooks next to the existing permission checks,
not after the actual works of these commands.
Is it reasonable for DDL triggers (if it has something like BEFORE/AFTER)?
Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
2010/11/9 KaiGai Kohei <kaigai@ak.jp.nec.com>:
The attached patch provides plugin modules a hook just after object
creation time. In typical use cases, it enables to assign default
security labels on object creation by the external security providers.As Robert suggested before, it provides a generic purpose main hook.
It takes an enum of ObjectAccessType which informs plugins what kind
of accesses are required, and identifier of the object to be referenced.
But, in this version, no additional information, such as new name in
ALTER xxx RENAME TO, are not supported.The ObjectAccessType is defined as follows:
typedef enum ObjectAccessType {
OAT_POST_CREATE, /* Post creation fixups; such as security labeling */
} ObjectAccessType;We will support more complete kind of access types in the future version,
however, we focus on default labeling rather than DDL permissions right
now, so only OAT_POST_CREATE is defined here.
Perhaps, we will add OAT_ALTER, OAT_DROP, OAT_COMMENT and so on.In this patch, I put hooks on the place just after creation of database
objects that we can assign security labels. (schema, relation, attribute,
procedure, language, type, large object)However, I didn't touch or move CommandCounterIncrement() yet, although
we had a long discussion MVCC visibility of new object.
Because I'm not clear whether it is really preferable to inject CCIs
onto random points such as TypeCreate() or ProcedureCreate() under
development of the version killed by myself.
(In other words, it was simply ugly...)At least, we can see the new entries with SnapshotSelf, although we will
pay performance penalty. If so, it is an idea not to touch anything
related to CCIs.
The purpose of post creation hooks are assignment of default security
labels, not DDL permissions. So, it is not a bad idea not to touch
routines related to CCIs in the earlier version of external security
provider.In this patch, we put InvokeObjectAccessHook0 on the following functions.
- heap_create_with_catalog() for relations/attributes
- ATExecAddColumn() for attributes
- NamespaceCreate() for schemas
- ProcedureCreate() for aggregates/functions
- TypeCreate() and TypeShellMake() for types
- create_proc_lang() for procedural languages
- inv_create() for large objects
I think you ought to try to arrange to avoid the overhead of a
function call in the common case where nobody's using the hook.
That's why I originally suggested making InvokeObjectAccessHook() a
macro around the actual function call.
I don't want to refer to this as a framework for enhanced security
providers. Let's stick with the term "object access hook". Calling
it an enhanced security provider overspecifies; it could equally well
be used for, say, logging.
Is there any compelling reason not to apply this to every object type
in the system (e.g. all the ones COMMENT can apply to)? I don't see
any reason to restrict it to the set of objects to which it's sensible
to apply security labels.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
(2010/11/10 13:06), Robert Haas wrote:
In this patch, we put InvokeObjectAccessHook0 on the following functions.
- heap_create_with_catalog() for relations/attributes
- ATExecAddColumn() for attributes
- NamespaceCreate() for schemas
- ProcedureCreate() for aggregates/functions
- TypeCreate() and TypeShellMake() for types
- create_proc_lang() for procedural languages
- inv_create() for large objectsI think you ought to try to arrange to avoid the overhead of a
function call in the common case where nobody's using the hook.
That's why I originally suggested making InvokeObjectAccessHook() a
macro around the actual function call.
Hmm. Although I have little preference here, the penalty to call
an empty function (when no plugins are installed) is not visible,
because frequency of DDL commands are not high.
Even so, is it necessary to replace them by macros?
I don't want to refer to this as a framework for enhanced security
providers. Let's stick with the term "object access hook". Calling
it an enhanced security provider overspecifies; it could equally well
be used for, say, logging.
OK. As Itagaki-san also pointed out, we may be able to utilize the hooks
in other purposes. Although I designed it in similar manner with security
label provider, I'll revise it like as other hooks doing.
Is there any compelling reason not to apply this to every object type
in the system (e.g. all the ones COMMENT can apply to)? I don't see
any reason to restrict it to the set of objects to which it's sensible
to apply security labels.
Because I thought too many hooks within one patch gives burden to reviewers,
so I restricted it on a part of object classes in this version.
However,it is not a compelling reason.
OK, I'll try to revise the patch soon.
Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
On Wed, Nov 10, 2010 at 8:33 AM, KaiGai Kohei <kaigai@kaigai.gr.jp> wrote:
(2010/11/10 13:06), Robert Haas wrote:
In this patch, we put InvokeObjectAccessHook0 on the following functions.
- heap_create_with_catalog() for relations/attributes
- ATExecAddColumn() for attributes
- NamespaceCreate() for schemas
- ProcedureCreate() for aggregates/functions
- TypeCreate() and TypeShellMake() for types
- create_proc_lang() for procedural languages
- inv_create() for large objectsI think you ought to try to arrange to avoid the overhead of a
function call in the common case where nobody's using the hook.
That's why I originally suggested making InvokeObjectAccessHook() a
macro around the actual function call.Hmm. Although I have little preference here, the penalty to call
an empty function (when no plugins are installed) is not visible,
because frequency of DDL commands are not high.
Even so, is it necessary to replace them by macros?
It's a fair point. I'm open to other opinions but my vote is to shove
a macro in there. A pointer test is cheaper than a function call, and
doesn't really complicate things much.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
(2010/11/11 3:00), Robert Haas wrote:
On Wed, Nov 10, 2010 at 8:33 AM, KaiGai Kohei<kaigai@kaigai.gr.jp> wrote:
(2010/11/10 13:06), Robert Haas wrote:
In this patch, we put InvokeObjectAccessHook0 on the following functions.
- heap_create_with_catalog() for relations/attributes
- ATExecAddColumn() for attributes
- NamespaceCreate() for schemas
- ProcedureCreate() for aggregates/functions
- TypeCreate() and TypeShellMake() for types
- create_proc_lang() for procedural languages
- inv_create() for large objectsI think you ought to try to arrange to avoid the overhead of a
function call in the common case where nobody's using the hook.
That's why I originally suggested making InvokeObjectAccessHook() a
macro around the actual function call.Hmm. Although I have little preference here, the penalty to call
an empty function (when no plugins are installed) is not visible,
because frequency of DDL commands are not high.
Even so, is it necessary to replace them by macros?It's a fair point. I'm open to other opinions but my vote is to shove
a macro in there. A pointer test is cheaper than a function call, and
doesn't really complicate things much.
Since I have no strong preference function call here, so, I'll revise my
patch according to your vote.
Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
I revised my patch according to the prior suggestions.
Invocation of the hooks is encapsulated within macro, not function:
+ #define InvokeObjectAccessHook0(access,classId,objectId,subId) \
+ do { \
+ if (object_access_hook) \
+ (*object_access_hook)((access),(classId),(objectId),(subId)); \
+ } while(0)
The "0" of tail means that it does not takes any arguments except for
object-ids, like syscache code.
It will reduce impact when we want to add arguments of the hooks.
In the previous version, it just support seven object classes that is
allowed to assign security labels. But, Robert pointed out the purpose
of post-creation hook is limited to security labeling. So, I expand its
coverage into all the commentable object classes.
- relations: heap_create_with_catalog()
- constraint: CreateConstraintEntry()
- conversion: ConversionCreate()
- schema: NamespaceCreate()
- operator: OperatorCreate() and OperatorShellMake()
- procedure: ProcedureCreate()
- type: TypeCreate() and TypeShellMake()
- database: createdb()
- cast: CreateCast()
- opfamily: CreateOpFamily()
- opclass: DefineOpClass()
- language: create_proc_lang()
- attribute: ATExecAddColumn()
- tablespace: CreateTableSpace()
- trigger: CreateTrigger()
- ts_parser: DefineTSParser()
- ts_dict: DefineTSDictionary()
- ts_template: DefineTSTemplate()
- ts_config: DefineTSConfiguration()
- role: CreateRole()
- rule: InsertRule()
- largeobject: inv_create()
The post-creation hooks are put on the place just after adding dependency
of the new object, if the object class uses dependency mechanism.
I believe it will be a clear guidance for the future maintenance works.
Thanks,
(2010/11/11 7:41), KaiGai Kohei wrote:
(2010/11/11 3:00), Robert Haas wrote:
On Wed, Nov 10, 2010 at 8:33 AM, KaiGai Kohei<kaigai@kaigai.gr.jp> wrote:
(2010/11/10 13:06), Robert Haas wrote:
In this patch, we put InvokeObjectAccessHook0 on the following functions.
- heap_create_with_catalog() for relations/attributes
- ATExecAddColumn() for attributes
- NamespaceCreate() for schemas
- ProcedureCreate() for aggregates/functions
- TypeCreate() and TypeShellMake() for types
- create_proc_lang() for procedural languages
- inv_create() for large objectsI think you ought to try to arrange to avoid the overhead of a
function call in the common case where nobody's using the hook.
That's why I originally suggested making InvokeObjectAccessHook() a
macro around the actual function call.Hmm. Although I have little preference here, the penalty to call
an empty function (when no plugins are installed) is not visible,
because frequency of DDL commands are not high.
Even so, is it necessary to replace them by macros?It's a fair point. I'm open to other opinions but my vote is to shove
a macro in there. A pointer test is cheaper than a function call, and
doesn't really complicate things much.Since I have no strong preference function call here, so, I'll revise my
patch according to your vote.Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
(2010/11/12 19:34), KaiGai Kohei wrote:
I revised my patch according to the prior suggestions.
I'm sorry. I revised my patch, but not attached.
Please see this attached one.
Thanks,
Invocation of the hooks is encapsulated within macro, not function:
+ #define InvokeObjectAccessHook0(access,classId,objectId,subId) \ + do { \ + if (object_access_hook) \ + (*object_access_hook)((access),(classId),(objectId),(subId)); \ + } while(0)The "0" of tail means that it does not takes any arguments except for
object-ids, like syscache code.
It will reduce impact when we want to add arguments of the hooks.In the previous version, it just support seven object classes that is
allowed to assign security labels. But, Robert pointed out the purpose
of post-creation hook is limited to security labeling. So, I expand its
coverage into all the commentable object classes.- relations: heap_create_with_catalog()
- constraint: CreateConstraintEntry()
- conversion: ConversionCreate()
- schema: NamespaceCreate()
- operator: OperatorCreate() and OperatorShellMake()
- procedure: ProcedureCreate()
- type: TypeCreate() and TypeShellMake()
- database: createdb()
- cast: CreateCast()
- opfamily: CreateOpFamily()
- opclass: DefineOpClass()
- language: create_proc_lang()
- attribute: ATExecAddColumn()
- tablespace: CreateTableSpace()
- trigger: CreateTrigger()
- ts_parser: DefineTSParser()
- ts_dict: DefineTSDictionary()
- ts_template: DefineTSTemplate()
- ts_config: DefineTSConfiguration()
- role: CreateRole()
- rule: InsertRule()
- largeobject: inv_create()The post-creation hooks are put on the place just after adding dependency
of the new object, if the object class uses dependency mechanism.
I believe it will be a clear guidance for the future maintenance works.Thanks,
(2010/11/11 7:41), KaiGai Kohei wrote:
(2010/11/11 3:00), Robert Haas wrote:
On Wed, Nov 10, 2010 at 8:33 AM, KaiGai Kohei<kaigai@kaigai.gr.jp> wrote:
(2010/11/10 13:06), Robert Haas wrote:
In this patch, we put InvokeObjectAccessHook0 on the following functions.
- heap_create_with_catalog() for relations/attributes
- ATExecAddColumn() for attributes
- NamespaceCreate() for schemas
- ProcedureCreate() for aggregates/functions
- TypeCreate() and TypeShellMake() for types
- create_proc_lang() for procedural languages
- inv_create() for large objectsI think you ought to try to arrange to avoid the overhead of a
function call in the common case where nobody's using the hook.
That's why I originally suggested making InvokeObjectAccessHook() a
macro around the actual function call.Hmm. Although I have little preference here, the penalty to call
an empty function (when no plugins are installed) is not visible,
because frequency of DDL commands are not high.
Even so, is it necessary to replace them by macros?It's a fair point. I'm open to other opinions but my vote is to shove
a macro in there. A pointer test is cheaper than a function call, and
doesn't really complicate things much.Since I have no strong preference function call here, so, I'll revise my
patch according to your vote.Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
Attachments:
pgsql-object-creation.2.patchtext/x-patch; name=pgsql-object-creation.2.patchDownload
*** a/src/backend/catalog/heap.c
--- b/src/backend/catalog/heap.c
***************
*** 63,68 ****
--- 63,69 ----
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
+ #include "utils/hooks.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/relcache.h"
***************
*** 1188,1193 **** heap_create_with_catalog(const char *relname,
--- 1189,1198 ----
}
}
+ /* Post creation of new relation */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ RelationRelationId, relid, 0);
+
/*
* Store any supplied constraints and defaults.
*
*** a/src/backend/catalog/pg_constraint.c
--- b/src/backend/catalog/pg_constraint.c
***************
*** 25,30 ****
--- 25,31 ----
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
+ #include "utils/hooks.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
***************
*** 360,365 **** CreateConstraintEntry(const char *constraintName,
--- 361,370 ----
DEPENDENCY_NORMAL);
}
+ /* Post creation of a new constraint */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ ConstraintRelationId, conOid, 0);
+
return conOid;
}
*** a/src/backend/catalog/pg_conversion.c
--- b/src/backend/catalog/pg_conversion.c
***************
*** 27,32 ****
--- 27,33 ----
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
+ #include "utils/hooks.h"
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
***************
*** 131,136 **** ConversionCreate(const char *conname, Oid connamespace,
--- 132,141 ----
recordDependencyOnOwner(ConversionRelationId, HeapTupleGetOid(tup),
conowner);
+ /* Post creation of a new conversion */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ ConversionRelationId, HeapTupleGetOid(tup), 0);
+
heap_freetuple(tup);
heap_close(rel, RowExclusiveLock);
*** a/src/backend/catalog/pg_namespace.c
--- b/src/backend/catalog/pg_namespace.c
***************
*** 19,24 ****
--- 19,25 ----
#include "catalog/indexing.h"
#include "catalog/pg_namespace.h"
#include "utils/builtins.h"
+ #include "utils/hooks.h"
#include "utils/rel.h"
#include "utils/syscache.h"
***************
*** 75,79 **** NamespaceCreate(const char *nspName, Oid ownerId)
--- 76,84 ----
/* Record dependency on owner */
recordDependencyOnOwner(NamespaceRelationId, nspoid, ownerId);
+ /* Post creation of new schema */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ NamespaceRelationId, nspoid, 0);
+
return nspoid;
}
*** a/src/backend/catalog/pg_operator.c
--- b/src/backend/catalog/pg_operator.c
***************
*** 30,35 ****
--- 30,36 ----
#include "parser/parse_oper.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+ #include "utils/hooks.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
***************
*** 271,276 **** OperatorShellMake(const char *operatorName,
--- 272,281 ----
/* Add dependencies for the entry */
makeOperatorDependencies(tup);
+ /* Post creation of a new shell operator */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ OperatorRelationId, operatorObjectId, 0);
+
heap_freetuple(tup);
/*
***************
*** 539,544 **** OperatorCreate(const char *operatorName,
--- 544,553 ----
/* Add dependencies for the entry */
makeOperatorDependencies(tup);
+ /* Post creation of a new operator */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ OperatorRelationId, operatorObjectId, 0);
+
heap_close(pg_operator_desc, RowExclusiveLock);
/*
*** a/src/backend/catalog/pg_proc.c
--- b/src/backend/catalog/pg_proc.c
***************
*** 33,38 ****
--- 33,39 ----
#include "tcop/tcopprot.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+ #include "utils/hooks.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
***************
*** 614,619 **** ProcedureCreate(const char *procedureName,
--- 615,624 ----
nnewmembers, newmembers);
}
+ /* Post creation of new procedure */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ ProcedureRelationId, retval, 0);
+
heap_freetuple(tup);
heap_close(rel, RowExclusiveLock);
*** a/src/backend/catalog/pg_type.c
--- b/src/backend/catalog/pg_type.c
***************
*** 28,33 ****
--- 28,34 ----
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
+ #include "utils/hooks.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
***************
*** 155,160 **** TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
--- 156,165 ----
NULL,
false);
+ /* Post creation of new shell type */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ TypeRelationId, typoid, 0);
+
/*
* clean up and return the type-oid
*/
***************
*** 455,460 **** TypeCreate(Oid newTypeOid,
--- 460,469 ----
NULL),
rebuildDeps);
+ /* Post creation of new type */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ TypeRelationId, typeObjectId, 0);
+
/*
* finish up
*/
*** a/src/backend/commands/dbcommands.c
--- b/src/backend/commands/dbcommands.c
***************
*** 53,58 ****
--- 53,59 ----
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
+ #include "utils/hooks.h"
#include "utils/lsyscache.h"
#include "utils/pg_locale.h"
#include "utils/snapmgr.h"
***************
*** 571,576 **** createdb(const CreatedbStmt *stmt)
--- 572,581 ----
/* Create pg_shdepend entries for objects within database */
copyTemplateDependencies(src_dboid, dboid);
+ /* Post creation of new database */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ DatabaseRelationId, dboid, 0);
+
/*
* Force a checkpoint before starting the copy. This will force dirty
* buffers out to disk, to ensure source database is up-to-date on disk
*** a/src/backend/commands/functioncmds.c
--- b/src/backend/commands/functioncmds.c
***************
*** 57,62 ****
--- 57,63 ----
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
+ #include "utils/hooks.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
***************
*** 1761,1766 **** CreateCast(CreateCastStmt *stmt)
--- 1762,1771 ----
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* Post creation of a new cast */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ CastRelationId, myself.objectId, 0);
+
heap_freetuple(tuple);
heap_close(relation, RowExclusiveLock);
*** a/src/backend/commands/opclasscmds.c
--- b/src/backend/commands/opclasscmds.c
***************
*** 38,43 ****
--- 38,44 ----
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
+ #include "utils/hooks.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
***************
*** 306,311 **** CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
--- 307,316 ----
/* dependency on owner */
recordDependencyOnOwner(OperatorFamilyRelationId, opfamilyoid, GetUserId());
+ /* Post creation of a new operator family */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ OperatorFamilyRelationId, opfamilyoid, 0);
+
heap_close(rel, RowExclusiveLock);
return opfamilyoid;
***************
*** 693,698 **** DefineOpClass(CreateOpClassStmt *stmt)
--- 698,707 ----
/* dependency on owner */
recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId());
+ /* Post creation of a new operator class */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ OperatorClassRelationId, opclassoid, 0);
+
heap_close(rel, RowExclusiveLock);
}
*** a/src/backend/commands/proclang.c
--- b/src/backend/commands/proclang.c
***************
*** 32,37 ****
--- 32,38 ----
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
+ #include "utils/hooks.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
***************
*** 425,430 **** create_proc_lang(const char *languageName, bool replace,
--- 426,435 ----
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* Post creation of a new procedural language */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ LanguageRelationId, myself.objectId, 0);
+
heap_close(rel, RowExclusiveLock);
}
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 69,74 ****
--- 69,75 ----
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
+ #include "utils/hooks.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
***************
*** 4024,4029 **** ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
--- 4025,4034 ----
heap_close(pgclass, RowExclusiveLock);
+ /* Post creation of new attribute */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ AttributeRelationId, myrelid, newattnum);
+
/* Make the attribute's catalog entry visible */
CommandCounterIncrement();
*** a/src/backend/commands/tablespace.c
--- b/src/backend/commands/tablespace.c
***************
*** 72,77 ****
--- 72,78 ----
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
+ #include "utils/hooks.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
***************
*** 333,338 **** CreateTableSpace(CreateTableSpaceStmt *stmt)
--- 334,343 ----
/* Record dependency on owner */
recordDependencyOnOwner(TableSpaceRelationId, tablespaceoid, ownerId);
+ /* Post creation of a new tablespace */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ TableSpaceRelationId, tablespaceoid, 0);
+
create_tablespace_directories(location, tablespaceoid);
/* Record the filesystem change in XLOG */
*** a/src/backend/commands/trigger.c
--- b/src/backend/commands/trigger.c
***************
*** 46,51 ****
--- 46,52 ----
#include "utils/builtins.h"
#include "utils/bytea.h"
#include "utils/fmgroids.h"
+ #include "utils/hooks.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
***************
*** 735,740 **** CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
--- 736,745 ----
recordDependencyOnExpr(&myself, whenClause, whenRtable,
DEPENDENCY_NORMAL);
+ /* Post creation of a new trigger */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ TriggerRelationId, trigoid, 0);
+
/* Keep lock on target rel until end of xact */
heap_close(rel, NoLock);
*** a/src/backend/commands/tsearchcmds.c
--- b/src/backend/commands/tsearchcmds.c
***************
*** 42,47 ****
--- 42,48 ----
#include "utils/builtins.h"
#include "utils/catcache.h"
#include "utils/fmgroids.h"
+ #include "utils/hooks.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
***************
*** 263,268 **** DefineTSParser(List *names, List *parameters)
--- 264,272 ----
makeParserDependencies(tup);
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ TSParserRelationId, prsOid, 0);
+
heap_freetuple(tup);
heap_close(prsRel, RowExclusiveLock);
***************
*** 563,568 **** DefineTSDictionary(List *names, List *parameters)
--- 567,575 ----
makeDictionaryDependencies(tup);
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ TSDictionaryRelationId, dictOid, 0);
+
heap_freetuple(tup);
heap_close(dictRel, RowExclusiveLock);
***************
*** 1050,1055 **** DefineTSTemplate(List *names, List *parameters)
--- 1057,1065 ----
makeTSTemplateDependencies(tup);
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ TSTemplateRelationId, dictOid, 0);
+
heap_freetuple(tup);
heap_close(tmplRel, RowExclusiveLock);
***************
*** 1440,1445 **** DefineTSConfiguration(List *names, List *parameters)
--- 1450,1458 ----
makeConfigurationDependencies(tup, false, mapRel);
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ TSConfigRelationId, cfgOid, 0);
+
heap_freetuple(tup);
if (mapRel)
*** a/src/backend/commands/user.c
--- b/src/backend/commands/user.c
***************
*** 30,35 ****
--- 30,36 ----
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
+ #include "utils/hooks.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
***************
*** 402,407 **** CreateRole(CreateRoleStmt *stmt)
--- 403,412 ----
rolemembers, roleNamesToIds(rolemembers),
GetUserId(), false);
+ /* Post creation of a new role */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ AuthIdRelationId, roleid, 0);
+
/*
* Close pg_authid, but keep lock till commit.
*/
*** a/src/backend/rewrite/rewriteDefine.c
--- b/src/backend/rewrite/rewriteDefine.c
***************
*** 29,34 ****
--- 29,35 ----
#include "rewrite/rewriteSupport.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+ #include "utils/hooks.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
***************
*** 177,182 **** InsertRule(char *rulname,
--- 178,187 ----
DEPENDENCY_NORMAL);
}
+ /* Post creation of a new rule */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ RewriteRelationId, rewriteObjectId, 0);
+
heap_close(pg_rewrite_desc, RowExclusiveLock);
return rewriteObjectId;
*** a/src/backend/storage/large_object/inv_api.c
--- b/src/backend/storage/large_object/inv_api.c
***************
*** 45,50 ****
--- 45,51 ----
#include "miscadmin.h"
#include "storage/large_object.h"
#include "utils/fmgroids.h"
+ #include "utils/hooks.h"
#include "utils/rel.h"
#include "utils/resowner.h"
#include "utils/snapmgr.h"
***************
*** 218,223 **** inv_create(Oid lobjId)
--- 219,228 ----
recordDependencyOnOwner(LargeObjectRelationId,
lobjId_new, GetUserId());
+ /* Post creation of a new large object */
+ InvokeObjectAccessHook0(OAT_POST_CREATE,
+ LargeObjectRelationId, lobjId_new, 0);
+
/*
* Advance command counter to make new tuple visible to later operations.
*/
*** a/src/backend/utils/init/globals.c
--- b/src/backend/utils/init/globals.c
***************
*** 21,26 ****
--- 21,27 ----
#include "libpq/pqcomm.h"
#include "miscadmin.h"
#include "storage/backendid.h"
+ #include "utils/hooks.h"
ProtocolVersion FrontendProtocol;
***************
*** 117,119 **** int VacuumCostBalance = 0; /* working state for vacuum */
--- 118,126 ----
bool VacuumCostActive = false;
int GinFuzzySearchLimit = 0;
+
+ /*
+ * Hooks on object accesses what can be applied for external security
+ * providers and so on.
+ */
+ PGDLLIMPORT object_access_hook_type object_access_hook = NULL;
*** /dev/null
--- b/src/include/utils/hooks.h
***************
*** 0 ****
--- 1,48 ----
+ /*
+ * hooks.h
+ *
+ * Definitions and introductions for object access hooks.
+ * This hooks allows plugins to handle events on accesses
+ * to database objects with user's query.
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ */
+ #ifndef HOOKS_H
+ #define HOOKS_H
+
+ typedef enum ObjectAccessType
+ {
+ OAT_POST_CREATE, /* Post object creation */
+ } ObjectAccessType;
+
+ /*
+ * Introduction of every access types
+ * -----------------------------------
+ *
+ * OAT_POST_CREATE
+ *
+ * This access type shall be invoked just after object creation.
+ * Right now, it does not take any arguments except for object identifiers.
+ */
+
+ /*
+ * Definition of the object access hook
+ */
+ typedef void (*object_access_hook_type)(ObjectAccessType access,
+ Oid classId,
+ Oid objectId,
+ int subId);
+
+ extern PGDLLIMPORT object_access_hook_type object_access_hook;
+
+ /*
+ * Utility macros
+ */
+ #define InvokeObjectAccessHook0(access,classId,objectId,subId) \
+ do { \
+ if (object_access_hook) \
+ (*object_access_hook)((access),(classId),(objectId),(subId)); \
+ } while(0)
+
+ #endif /* HOOKS_H */
2010/11/12 KaiGai Kohei <kaigai@ak.jp.nec.com>:
(2010/11/12 19:34), KaiGai Kohei wrote:
I revised my patch according to the prior suggestions.
I'm sorry. I revised my patch, but not attached.
Please see this attached one.
I'm satisfied with this approach, although I intend to change
InvokeObjectAccessHook0 to simply InvokeObjectAccessHook before
committing it; and correct your use of AttributeRelationId to
RelationRelationId for consistency with the rest of the code. What
I'm not quite sure about is where to put the definitions you've added
to a new file utils/hooks.h; I don't feel that's a very appropriate
location. It's tempting to put them in utils/acl.h just because this
is vaguely access-control related and that header is already included
in most of the right places, but maybe that's too much of a stretch;
or perhaps catalog/catalog.h, although that doesn't feel quite right
either. If we are going to add a new header file, I still don't like
utils/hooks.h much - it's considerably more generic than can be
justified by its contents.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Thanks for your reviewing, and sorry for the late reply.
I've not been available for a few days.
(2010/11/22 12:11), Robert Haas wrote:
2010/11/12 KaiGai Kohei<kaigai@ak.jp.nec.com>:
(2010/11/12 19:34), KaiGai Kohei wrote:
I revised my patch according to the prior suggestions.
I'm sorry. I revised my patch, but not attached.
Please see this attached one.
I'm satisfied with this approach, although I intend to change
InvokeObjectAccessHook0 to simply InvokeObjectAccessHook before
committing it;
OK. We have no other object-access-type which takes any arguments
right now. It is quite cosmetic things, so we may be able to add
the number of arguments later, such as SysCache.
and correct your use of AttributeRelationId to
RelationRelationId for consistency with the rest of the code.
Oops, it was my bug. I'll fix it.
What
I'm not quite sure about is where to put the definitions you've added
to a new file utils/hooks.h; I don't feel that's a very appropriate
location. It's tempting to put them in utils/acl.h just because this
is vaguely access-control related and that header is already included
in most of the right places, but maybe that's too much of a stretch;
or perhaps catalog/catalog.h, although that doesn't feel quite right
either. If we are going to add a new header file, I still don't like
utils/hooks.h much - it's considerably more generic than can be
justified by its contents.
I don't think utils/acl.h is long-standing right place, because we
intended not to restrict the purpose of this hooks to access controls
as you mentioned.
I think somewhere under the catalog/ directory is a good idea because
it hooks events that user wants (eventually) to modify system catalogs.
How about catalog/hooks.h, instead of utils/hooks.h?
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
2010/11/23 KaiGai Kohei <kaigai@ak.jp.nec.com>:
What
I'm not quite sure about is where to put the definitions you've added
to a new file utils/hooks.h; I don't feel that's a very appropriate
location. It's tempting to put them in utils/acl.h just because this
is vaguely access-control related and that header is already included
in most of the right places, but maybe that's too much of a stretch;
or perhaps catalog/catalog.h, although that doesn't feel quite right
either. If we are going to add a new header file, I still don't like
utils/hooks.h much - it's considerably more generic than can be
justified by its contents.I don't think utils/acl.h is long-standing right place, because we
intended not to restrict the purpose of this hooks to access controls
as you mentioned.I think somewhere under the catalog/ directory is a good idea because
it hooks events that user wants (eventually) to modify system catalogs.
How about catalog/hooks.h, instead of utils/hooks.h?
Well, if we're going to create a new header file for this, I think it
should be called something like catalog/objectaccess.h, rather than
just hooks.h. But I'd rather reuse something that's already there,
all things being equal.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
The attached patch is a revised patch.
- The utils/hooks.h was renamed to catalog/objectaccess.h
- Numeric in the tail of InvokeObjectAccessHook0() has gone.
- Fixed bug in ATExecAddColumn; it gave AttributeRelationId
to the hook instead of RelationRelationId.
In addition, I found that we didn't put post-creation hook
on foreign data wrapper, foreign server and user mapping
exceptionally. So, I put this hook around their command
handler like any other object classes.
Thanks,
(2010/11/24 12:07), Robert Haas wrote:
2010/11/23 KaiGai Kohei<kaigai@ak.jp.nec.com>:
What
I'm not quite sure about is where to put the definitions you've added
to a new file utils/hooks.h; I don't feel that's a very appropriate
location. It's tempting to put them in utils/acl.h just because this
is vaguely access-control related and that header is already included
in most of the right places, but maybe that's too much of a stretch;
or perhaps catalog/catalog.h, although that doesn't feel quite right
either. If we are going to add a new header file, I still don't like
utils/hooks.h much - it's considerably more generic than can be
justified by its contents.I don't think utils/acl.h is long-standing right place, because we
intended not to restrict the purpose of this hooks to access controls
as you mentioned.I think somewhere under the catalog/ directory is a good idea because
it hooks events that user wants (eventually) to modify system catalogs.
How about catalog/hooks.h, instead of utils/hooks.h?Well, if we're going to create a new header file for this, I think it
should be called something like catalog/objectaccess.h, rather than
just hooks.h. But I'd rather reuse something that's already there,
all things being equal.
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
Attachments:
pgsql-object-creation.3.patchtext/x-patch; name=pgsql-object-creation.3.patchDownload
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index dcc53e1..9a38207 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -40,6 +40,7 @@
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_inherits.h"
@@ -1188,6 +1189,10 @@ heap_create_with_catalog(const char *relname,
}
}
+ /* Post creation of new relation */
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ RelationRelationId, relid, 0);
+
/*
* Store any supplied constraints and defaults.
*
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 8b4f8c6..1ed108f 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -18,6 +18,7 @@
#include "access/heapam.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_type.h"
@@ -360,6 +361,10 @@ CreateConstraintEntry(const char *constraintName,
DEPENDENCY_NORMAL);
}
+ /* Post creation of a new constraint */
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ ConstraintRelationId, conOid, 0);
+
return conOid;
}
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 9578184..853ed0e 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -18,6 +18,7 @@
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_conversion_fn.h"
#include "catalog/pg_namespace.h"
@@ -131,6 +132,10 @@ ConversionCreate(const char *conname, Oid connamespace,
recordDependencyOnOwner(ConversionRelationId, HeapTupleGetOid(tup),
conowner);
+ /* Post creation of a new conversion */
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ ConversionRelationId, HeapTupleGetOid(tup), 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 71ebd7a..978be51 100644
--- a/src/backend/catalog/pg_namespace.c
+++ b/src/backend/catalog/pg_namespace.c
@@ -17,6 +17,7 @@
#include "access/heapam.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_namespace.h"
#include "utils/builtins.h"
#include "utils/rel.h"
@@ -75,5 +76,9 @@ NamespaceCreate(const char *nspName, Oid ownerId)
/* Record dependency on owner */
recordDependencyOnOwner(NamespaceRelationId, nspoid, ownerId);
+ /* Post creation of new schema */
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ NamespaceRelationId, nspoid, 0);
+
return nspoid;
}
diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c
index 73de672..cd169a6 100644
--- a/src/backend/catalog/pg_operator.c
+++ b/src/backend/catalog/pg_operator.c
@@ -22,6 +22,7 @@
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
@@ -271,6 +272,10 @@ OperatorShellMake(const char *operatorName,
/* Add dependencies for the entry */
makeOperatorDependencies(tup);
+ /* Post creation of a new shell operator */
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ OperatorRelationId, operatorObjectId, 0);
+
heap_freetuple(tup);
/*
@@ -539,6 +544,10 @@ OperatorCreate(const char *operatorName,
/* Add dependencies for the entry */
makeOperatorDependencies(tup);
+ /* Post creation of a new operator */
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ OperatorRelationId, operatorObjectId, 0);
+
heap_close(pg_operator_desc, RowExclusiveLock);
/*
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 34cd862..6ef8cc7 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -18,6 +18,7 @@
#include "access/xact.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
@@ -614,6 +615,10 @@ ProcedureCreate(const char *procedureName,
nnewmembers, newmembers);
}
+ /* Post creation of new procedure */
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ ProcedureRelationId, retval, 0);
+
heap_freetuple(tup);
heap_close(rel, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index d7fccdf..61ba83e 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -18,6 +18,7 @@
#include "access/xact.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
@@ -155,6 +156,10 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
NULL,
false);
+ /* Post creation of new shell type */
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ TypeRelationId, typoid, 0);
+
/*
* clean up and return the type-oid
*/
@@ -455,6 +460,10 @@ TypeCreate(Oid newTypeOid,
NULL),
rebuildDeps);
+ /* Post creation of new type */
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ TypeRelationId, typeObjectId, 0);
+
/*
* finish up
*/
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 8cbd754..8dece96 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -32,6 +32,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_database.h"
#include "catalog/pg_db_role_setting.h"
@@ -572,6 +573,10 @@ createdb(const CreatedbStmt *stmt)
/* Create pg_shdepend entries for objects within database */
copyTemplateDependencies(src_dboid, dboid);
+ /* Post creation of new database */
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ DatabaseRelationId, dboid, 0);
+
/*
* Force a checkpoint before starting the copy. This will force dirty
* buffers out to disk, to ensure source database is up-to-date on disk
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 4e6367c..6d9da5d 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -18,6 +18,7 @@
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_proc.h"
@@ -415,6 +416,10 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
+ /* Post creation of new foreign data wrapper */
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ ForeignDataWrapperRelationId, fdwId, 0);
+
heap_close(rel, NoLock);
}
@@ -696,6 +701,10 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
+ /* Post creation of new foreign server */
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ ForeignServerRelationId, srvId, 0);
+
heap_close(rel, NoLock);
}
@@ -967,6 +976,10 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
/* Record the mapped user dependency */
recordDependencyOnOwner(UserMappingRelationId, umId, useId);
+ /* Post creation of new shell type */
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ UserMappingRelationId, umId, 0);
+
heap_close(rel, NoLock);
}
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 62a2110..571649e 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -37,6 +37,7 @@
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_language.h"
@@ -1761,6 +1762,10 @@ CreateCast(CreateCastStmt *stmt)
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* Post creation of a new cast */
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ CastRelationId, myself.objectId, 0);
+
heap_freetuple(tuple);
heap_close(relation, RowExclusiveLock);
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 132c4ee..26cb0a4 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -22,6 +22,7 @@
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_namespace.h"
@@ -307,6 +308,10 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
/* dependency on owner */
recordDependencyOnOwner(OperatorFamilyRelationId, opfamilyoid, GetUserId());
+ /* Post creation of a new operator family */
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ OperatorFamilyRelationId, opfamilyoid, 0);
+
heap_close(rel, RowExclusiveLock);
return opfamilyoid;
@@ -703,6 +708,10 @@ DefineOpClass(CreateOpClassStmt *stmt)
/* dependency on owner */
recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId());
+ /* Post creation of a new operator class */
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ OperatorClassRelationId, opclassoid, 0);
+
heap_close(rel, RowExclusiveLock);
}
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
index a2e653a..163b10f 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -17,6 +17,7 @@
#include "access/heapam.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_pltemplate.h"
@@ -425,6 +426,10 @@ create_proc_lang(const char *languageName, bool replace,
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* Post creation of a new procedural language */
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ LanguageRelationId, myself.objectId, 0);
+
heap_close(rel, RowExclusiveLock);
}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index e8808e2..5575607 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -26,6 +26,7 @@
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_inherits.h"
@@ -4082,6 +4083,10 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
heap_close(pgclass, RowExclusiveLock);
+ /* Post creation of new attribute */
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ RelationRelationId, myrelid, newattnum);
+
/* Make the attribute's catalog entry visible */
CommandCounterIncrement();
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 305ac46..6637cec 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -59,6 +59,7 @@
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_tablespace.h"
#include "commands/comment.h"
#include "commands/defrem.h"
@@ -333,6 +334,10 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
/* Record dependency on owner */
recordDependencyOnOwner(TableSpaceRelationId, tablespaceoid, ownerId);
+ /* Post creation of a new tablespace */
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ TableSpaceRelationId, tablespaceoid, 0);
+
create_tablespace_directories(location, tablespaceoid);
/* Record the filesystem change in XLOG */
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index d69fdcf..ef841b2 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -20,6 +20,7 @@
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_trigger.h"
@@ -735,6 +736,10 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
recordDependencyOnExpr(&myself, whenClause, whenRtable,
DEPENDENCY_NORMAL);
+ /* Post creation of a new trigger */
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ TriggerRelationId, trigoid, 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 73b8c92..2ea6dc1 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -23,6 +23,7 @@
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_ts_config.h"
@@ -263,6 +264,9 @@ DefineTSParser(List *names, List *parameters)
makeParserDependencies(tup);
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ TSParserRelationId, prsOid, 0);
+
heap_freetuple(tup);
heap_close(prsRel, RowExclusiveLock);
@@ -563,6 +567,9 @@ DefineTSDictionary(List *names, List *parameters)
makeDictionaryDependencies(tup);
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ TSDictionaryRelationId, dictOid, 0);
+
heap_freetuple(tup);
heap_close(dictRel, RowExclusiveLock);
@@ -1050,6 +1057,9 @@ DefineTSTemplate(List *names, List *parameters)
makeTSTemplateDependencies(tup);
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ TSTemplateRelationId, dictOid, 0);
+
heap_freetuple(tup);
heap_close(tmplRel, RowExclusiveLock);
@@ -1440,6 +1450,9 @@ DefineTSConfiguration(List *names, List *parameters)
makeConfigurationDependencies(tup, false, mapRel);
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ TSConfigRelationId, cfgOid, 0);
+
heap_freetuple(tup);
if (mapRel)
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index f1ff839..a9f97ce 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -17,6 +17,7 @@
#include "access/xact.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_auth_members.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_database.h"
@@ -402,6 +403,10 @@ CreateRole(CreateRoleStmt *stmt)
rolemembers, roleNamesToIds(rolemembers),
GetUserId(), false);
+ /* Post creation of a new role */
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ AuthIdRelationId, roleid, 0);
+
/*
* Close pg_authid, but keep lock till commit.
*/
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 029a288..c2e4f89 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -19,6 +19,7 @@
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_rewrite.h"
#include "catalog/storage.h"
#include "miscadmin.h"
@@ -177,6 +178,10 @@ InsertRule(char *rulname,
DEPENDENCY_NORMAL);
}
+ /* Post creation of a new rule */
+ InvokeObjectAccessHook(OAT_POST_CREATE,
+ RewriteRelationId, rewriteObjectId, 0);
+
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 e2faf95..a028988 100644
--- a/src/backend/storage/large_object/inv_api.c
+++ b/src/backend/storage/large_object/inv_api.c
@@ -38,6 +38,7 @@
#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"
@@ -218,6 +219,10 @@ inv_create(Oid lobjId)
recordDependencyOnOwner(LargeObjectRelationId,
lobjId_new, GetUserId());
+ /* Post creation of a 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/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 9aa2c0a..a132edb 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -18,6 +18,7 @@
*/
#include "postgres.h"
+#include "catalog/objectaccess.h"
#include "libpq/pqcomm.h"
#include "miscadmin.h"
#include "storage/backendid.h"
@@ -117,3 +118,9 @@ int VacuumCostBalance = 0; /* working state for vacuum */
bool VacuumCostActive = false;
int GinFuzzySearchLimit = 0;
+
+/*
+ * Hooks on object accesses what can be applied for external security
+ * providers and so on.
+ */
+PGDLLIMPORT object_access_hook_type object_access_hook = NULL;
diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h
new file mode 100644
index 0000000..6b7e82e
--- /dev/null
+++ b/src/include/catalog/objectaccess.h
@@ -0,0 +1,50 @@
+/*
+ * objectaccess.h
+ *
+ * Definitions and introductions for object access hooks.
+ * This hooks allows plugins to handle events on accesses
+ * to database objects with user's query.
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ */
+#ifndef OBJECTACCESS_H
+#define OBJECTACCESS_H
+
+typedef enum ObjectAccessType
+{
+ OAT_POST_CREATE, /* Post object creation */
+} ObjectAccessType;
+
+/*
+ * Introduction of every access types
+ * -----------------------------------
+ *
+ * OAT_POST_CREATE
+ *
+ * This access type shall be invoked just after object creations.
+ * Typically, the hook is put just after setting up dependency of
+ * the new object. Right now, it does not take any arguments except
+ * for object identifiers.
+ */
+
+/*
+ * Definition of the object access hook.
+ */
+typedef void (*object_access_hook_type)(ObjectAccessType access,
+ Oid classId,
+ Oid objectId,
+ int subId);
+
+extern PGDLLIMPORT object_access_hook_type object_access_hook;
+
+/*
+ * Utility macros
+ */
+#define InvokeObjectAccessHook(access,classId,objectId,subId) \
+ do { \
+ if (object_access_hook) \
+ (*object_access_hook)((access),(classId),(objectId),(subId)); \
+ } while(0)
+
+#endif /* OBJECTACCESS_H */
2010/11/25 KaiGai Kohei <kaigai@ak.jp.nec.com>:
The attached patch is a revised patch.
- The utils/hooks.h was renamed to catalog/objectaccess.h
- Numeric in the tail of InvokeObjectAccessHook0() has gone.
- Fixed bug in ATExecAddColumn; it gave AttributeRelationId
to the hook instead of RelationRelationId.In addition, I found that we didn't put post-creation hook
on foreign data wrapper, foreign server and user mapping
exceptionally. So, I put this hook around their command
handler like any other object classes.
Committed with minor, mostly cosmetic adjustments.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company