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<[email protected]>:
>>> 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 <[email protected]>
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 */
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers