Dimitri Fontaine escribió:

> The good news is that the patch to do that has already been sent on this
> list, and got reviewed in details by Álvaro who did offer incremental
> changes. Version 3 of that patch is to be found in:
> 
>   http://www.postgresql.org/message-id/m2fw19n1hr....@2ndquadrant.fr

Here's a v4 of that patch.  I added support for DROP OWNED, and added
object name and schema name available to the pg_dropped_objects
function.

Since we're now in agreement that this is the way to go, I think this
only needs a few more tweaks to get to a committable state, as well as
some useful tests and doc changes.  (v3 has docs which I didn't include
here but are probably useful almost verbatim.)

Do we want some more stuff provided by pg_dropped_objects?  We now have
classId, objectId, objectSubId, object name, schema name.  One further
thing I think we need is the object's type, i.e. a simple untranslated
string "table", "view", "operator" and so on.  AFAICT this requires a
nearly-duplicate of getObjectDescription.

-- 
Álvaro Herrera                http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index d203725..2233158 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -267,6 +267,12 @@ performDeletion(const ObjectAddress *object,
 	{
 		ObjectAddress *thisobj = targetObjects->refs + i;
 
+		if ((!(flags & PERFORM_DELETION_INTERNAL)) &&
+			EventTriggerSupportsObjectType(getObjectClass(thisobj)))
+		{
+			evtrig_sqldrop_add_object(thisobj);
+		}
+
 		deleteOneObject(thisobj, &depRel, flags);
 	}
 
@@ -349,6 +355,12 @@ performMultipleDeletions(const ObjectAddresses *objects,
 	{
 		ObjectAddress *thisobj = targetObjects->refs + i;
 
+		if ((!(flags & PERFORM_DELETION_INTERNAL)) &&
+			EventTriggerSupportsObjectType(getObjectClass(thisobj)))
+		{
+			evtrig_sqldrop_add_object(thisobj);
+		}
+
 		deleteOneObject(thisobj, &depRel, flags);
 	}
 
@@ -366,6 +378,10 @@ performMultipleDeletions(const ObjectAddresses *objects,
  * This is currently used only to clean out the contents of a schema
  * (namespace): the passed object is a namespace.  We normally want this
  * to be done silently, so there's an option to suppress NOTICE messages.
+ *
+ * Note we don't fire object drop event triggers here; it would be wrong to do
+ * so for the current only use of this function, but if more callers are added
+ * this might need to be reconsidered.
  */
 void
 deleteWhatDependsOn(const ObjectAddress *object,
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 269d19c..347c405 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -746,58 +746,6 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
 }
 
 /*
- * Return a copy of the tuple for the object with the given object OID, from
- * the given catalog (which must have been opened by the caller and suitably
- * locked).  NULL is returned if the OID is not found.
- *
- * We try a syscache first, if available.
- *
- * XXX this function seems general in possible usage.  Given sufficient callers
- * elsewhere, we should consider moving it to a more appropriate place.
- */
-static HeapTuple
-get_catalog_object_by_oid(Relation catalog, Oid objectId)
-{
-	HeapTuple	tuple;
-	Oid			classId = RelationGetRelid(catalog);
-	int			oidCacheId = get_object_catcache_oid(classId);
-
-	if (oidCacheId > 0)
-	{
-		tuple = SearchSysCacheCopy1(oidCacheId, ObjectIdGetDatum(objectId));
-		if (!HeapTupleIsValid(tuple))  /* should not happen */
-			return NULL;
-	}
-	else
-	{
-		Oid			oidIndexId = get_object_oid_index(classId);
-		SysScanDesc	scan;
-		ScanKeyData	skey;
-
-		Assert(OidIsValid(oidIndexId));
-
-		ScanKeyInit(&skey,
-					ObjectIdAttributeNumber,
-					BTEqualStrategyNumber, F_OIDEQ,
-					ObjectIdGetDatum(objectId));
-
-		scan = systable_beginscan(catalog, oidIndexId, true,
-								  SnapshotNow, 1, &skey);
-		tuple = systable_getnext(scan);
-		if (!HeapTupleIsValid(tuple))
-		{
-			systable_endscan(scan);
-			return NULL;
-		}
-		tuple = heap_copytuple(tuple);
-
-		systable_endscan(scan);
-	}
-
-	return tuple;
-}
-
-/*
  * Generic function to change the ownership of a given object, for simple
  * cases (won't work for tables, nor other cases where we need to do more than
  * change the ownership column of a single catalog entry).
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 18b3753..66f5005 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -25,6 +25,7 @@
 #include "commands/dbcommands.h"
 #include "commands/event_trigger.h"
 #include "commands/trigger.h"
+#include "funcapi.h"
 #include "parser/parse_func.h"
 #include "pgstat.h"
 #include "miscadmin.h"
@@ -39,6 +40,10 @@
 #include "utils/syscache.h"
 #include "tcop/utility.h"
 
+/* Globally visible state variables */
+bool evtrig_sqldrop_inprogress = false;
+slist_head	SQLDropList = SLIST_STATIC_INIT(SQLDropList);
+
 typedef struct
 {
 	const char	   *obtypename;
@@ -88,6 +93,15 @@ static event_trigger_support_data event_trigger_support[] = {
 	{ NULL, false }
 };
 
+/* Support for dropped objects */
+typedef struct SQLDropObject
+{
+	ObjectAddress	address;
+	char		   *objname;
+	char		   *schemaname;
+	slist_node		next;
+} SQLDropObject;
+
 static void AlterEventTriggerOwner_internal(Relation rel,
 											HeapTuple tup,
 											Oid newOwnerId);
@@ -150,8 +164,12 @@ CreateEventTrigger(CreateEventTrigStmt *stmt)
 	}
 
 	/* Validate tag list, if any. */
-	if (strcmp(stmt->eventname, "ddl_command_start") == 0 && tags != NULL)
+	if ((strcmp(stmt->eventname, "ddl_command_start") == 0 ||
+		 strcmp(stmt->eventname, "ddl_command_end") == 0)
+		&& tags != NULL)
+	{
 		validate_ddl_tags("tag", tags);
+	}
 
 	/*
 	 * Give user a nice error message if an event trigger of the same name
@@ -218,7 +236,8 @@ check_ddl_tag(const char *tag)
 	if (pg_strcasecmp(tag, "CREATE TABLE AS") == 0 ||
 		pg_strcasecmp(tag, "SELECT INTO") == 0 ||
 		pg_strcasecmp(tag, "ALTER DEFAULT PRIVILEGES") == 0 ||
-		pg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0)
+		pg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0 ||
+		pg_strcasecmp(tag, "DROP OWNED") == 0)
 		return EVENT_TRIGGER_COMMAND_TAG_OK;
 
 	/*
@@ -825,3 +844,202 @@ EventTriggerSupportsObjectType(ObjectType obtype)
 	}
 	return true;
 }
+
+/*
+ * Support for dropped objects information on event trigger functions.
+ *
+ * We keep the list of objects dropped by the current command in a list of
+ * these structs.  Each command that might drop objects saves the current
+ * list in a local variable, initialize a new empty list and do the dependency.c
+ * dance to drop objects, which populates the list; when the event triggers are
+ * invoked they can consume the list via pg_event_trigger_dropped_objects().
+ * When the command finishes, the list is cleared and the original list is
+ * restored.  This is to support the case that an event trigger function drops
+ * objects "reentrantly".
+ *
+ * For each object dropped, we save the below info, which can be obtained as a
+ * set via the pg_event_trigger_dropped_objects() SQL-callable function.
+ */
+
+/*
+ * Initialize state of objects dropped
+ */
+void
+EventTriggerInitializeDrop(bool *save_inprogress, slist_head *save_objlist)
+{
+	/* save previous state in local vars of caller, for later restore */
+	*save_inprogress = evtrig_sqldrop_inprogress;
+	*save_objlist = SQLDropList;
+
+	evtrig_sqldrop_inprogress = true;
+	slist_init(&SQLDropList);
+}
+
+/*
+ * Restore state after running a command that drops objects; free memory from a
+ * list we may have created.
+ */
+void
+EventTriggerFinalizeDrop(bool save_inprogress, slist_head save_objlist)
+{
+	slist_mutable_iter	iter;
+
+	slist_foreach_modify(iter, &SQLDropList)
+	{
+		SQLDropObject  *obj = slist_container(SQLDropObject, next, iter.cur);
+
+		if (obj->objname)
+			pfree(obj->objname);
+		if (obj->schemaname)
+			pfree(obj->schemaname);
+		pfree(obj);
+	}
+
+	evtrig_sqldrop_inprogress = save_inprogress;
+	SQLDropList = save_objlist;
+}
+
+/*
+ * Register one object as being dropped by the current command.
+ *
+ * XXX do we need to think about memory context these things are stored in?
+ */
+void
+evtrig_sqldrop_add_object(ObjectAddress *object)
+{
+	SQLDropObject  *obj;
+	HeapTuple	tuple;
+	Relation	catalog;
+
+	Assert(EventTriggerSupportsObjectType(getObjectClass(object)));
+
+	obj = palloc0(sizeof(SQLDropObject));
+	obj->address = *object;
+
+	/*
+	 * Obtain object and schema names from the object's catalog tuple, if one
+	 * exists.
+	 */
+	catalog = heap_open(obj->address.classId, AccessShareLock);
+	tuple = get_catalog_object_by_oid(catalog, obj->address.objectId);
+	if (tuple)
+	{
+		AttrNumber	attnum;
+		Datum		datum;
+		bool		isnull;
+
+		attnum = get_object_attnum_name(obj->address.classId);
+		if (attnum != InvalidAttrNumber)
+		{
+			datum = heap_getattr(tuple, attnum,
+								 RelationGetDescr(catalog), &isnull);
+			if (!isnull)
+				obj->objname = pstrdup(NameStr(*DatumGetName(datum)));
+		}
+
+		attnum = get_object_attnum_namespace(obj->address.classId);
+		if (attnum != InvalidAttrNumber)
+		{
+			datum = heap_getattr(tuple, attnum,
+								 RelationGetDescr(catalog), &isnull);
+			if (!isnull)
+				obj->schemaname = get_namespace_name(DatumGetObjectId(datum));
+		}
+	}
+
+	heap_close(catalog, AccessShareLock);
+
+	slist_push_head(&SQLDropList, &obj->next);
+}
+
+/*
+ * pg_event_trigger_dropped_objects
+ *
+ * Make the list of dropped objects available to the user function run by the
+ * Event Trigger.
+ */
+Datum
+pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
+{
+	ReturnSetInfo	   *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	TupleDesc			tupdesc;
+	Tuplestorestate	   *tupstore;
+	MemoryContext		per_query_ctx;
+	MemoryContext		oldcontext;
+	slist_iter			iter;
+
+	/*
+	 * This function is meant to be called from within an event trigger in
+	 * order to get the list of objects dropped, if any.
+	 */
+	if (!evtrig_sqldrop_inprogress)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("%s can only be called from an event trigger function",
+						"pg_event_trigger_dropped_objects()")));
+
+	/* check to see if caller supports us returning a tuplestore */
+	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
+	if (!(rsinfo->allowedModes & SFRM_Materialize))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("materialize mode required, but it is not allowed in this context")));
+
+	/* Build a tuple descriptor for our result type */
+	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+		elog(ERROR, "return type must be a row type");
+
+	/* Build tuplestore to hold the result rows */
+	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+	oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+	tupstore = tuplestore_begin_heap(true, false, work_mem);
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = tupstore;
+	rsinfo->setDesc = tupdesc;
+
+	MemoryContextSwitchTo(oldcontext);
+
+	slist_foreach(iter, &SQLDropList)
+	{
+		SQLDropObject *obj;
+		Datum		values[5];
+		bool		nulls[5];
+
+		obj = slist_container(SQLDropObject, next, iter.cur);
+
+		MemSet(values, 0, sizeof(values));
+		MemSet(nulls, 0, sizeof(nulls));
+
+		/* classid */
+		values[0] = ObjectIdGetDatum(obj->address.classId);
+
+		/* objid */
+		values[1] = ObjectIdGetDatum(obj->address.objectId);
+
+		/* objsubid */
+		values[2] = Int32GetDatum(obj->address.objectSubId);
+
+		/* objname */
+		if (obj->objname)
+			values[3] = CStringGetTextDatum(obj->objname);
+		else
+			nulls[3] = true;
+
+		/* schemaname */
+		if (obj->schemaname)
+			values[4] = CStringGetTextDatum(obj->objname);
+		else
+			nulls[4] = true;
+
+		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+	}
+
+	/* clean up and return the tuplestore */
+	tuplestore_donestoring(tupstore);
+
+	return (Datum) 0;
+}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 8904c6f..e30fe42 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -697,33 +697,60 @@ standard_ProcessUtility(Node *parsetree,
 		case T_DropStmt:
 			{
 				DropStmt   *stmt = (DropStmt *) parsetree;
+				bool		save_inprogress;
+				slist_head	save_objlist;
 
-				if (isCompleteQuery
-					&& EventTriggerSupportsObjectType(stmt->removeType))
+				/*
+				 * don't run any event trigger when we require not to have open
+				 * a transaction
+				 */
+				if (stmt->removeType == OBJECT_INDEX && stmt->concurrent)
+					PreventTransactionChain(isTopLevel,
+											"DROP INDEX CONCURRENTLY");
+
+				if (isCompleteQuery &&
+					EventTriggerSupportsObjectType(stmt->removeType))
+				{
 					EventTriggerDDLCommandStart(parsetree);
 
-				switch (stmt->removeType)
+					EventTriggerInitializeDrop(&save_inprogress, &save_objlist);
+				}
+
+				PG_TRY();
 				{
-					case OBJECT_INDEX:
-						if (stmt->concurrent)
-							PreventTransactionChain(isTopLevel,
-													"DROP INDEX CONCURRENTLY");
-						/* fall through */
+					switch (stmt->removeType)
+					{
+						case OBJECT_INDEX:
+						case OBJECT_TABLE:
+						case OBJECT_SEQUENCE:
+						case OBJECT_VIEW:
+						case OBJECT_FOREIGN_TABLE:
+							RemoveRelations((DropStmt *) parsetree);
+							break;
+						default:
+							RemoveObjects((DropStmt *) parsetree);
+							break;
+					}
 
-					case OBJECT_TABLE:
-					case OBJECT_SEQUENCE:
-					case OBJECT_VIEW:
-					case OBJECT_FOREIGN_TABLE:
-						RemoveRelations((DropStmt *) parsetree);
-						break;
-					default:
-						RemoveObjects((DropStmt *) parsetree);
-						break;
+					if (isCompleteQuery
+						&& EventTriggerSupportsObjectType(stmt->removeType))
+					{
+						EventTriggerDDLCommandEnd(parsetree);
+					}
+				}
+				PG_CATCH();
+				{
+					if (isCompleteQuery
+						&& EventTriggerSupportsObjectType(stmt->removeType))
+						EventTriggerFinalizeDrop(save_inprogress, save_objlist);
+
+					PG_RE_THROW();
 				}
+				PG_END_TRY();
 
 				if (isCompleteQuery
 					&& EventTriggerSupportsObjectType(stmt->removeType))
-					EventTriggerDDLCommandEnd(parsetree);
+					EventTriggerFinalizeDrop(save_inprogress, save_objlist);
 
 				break;
 			}
@@ -1238,9 +1265,37 @@ standard_ProcessUtility(Node *parsetree,
 			break;
 
 		case T_DropOwnedStmt:
-			/* no event triggers for global objects */
-			DropOwnedObjects((DropOwnedStmt *) parsetree);
-			break;
+			{
+				bool	save_inprogress;
+				slist_head save_objlist;
+
+				if (isCompleteQuery)
+				{
+					EventTriggerDDLCommandStart(parsetree);
+
+					EventTriggerInitializeDrop(&save_inprogress, &save_objlist);
+				}
+
+				PG_TRY();
+				{
+					DropOwnedObjects((DropOwnedStmt *) parsetree);
+
+					if (isCompleteQuery)
+						EventTriggerDDLCommandEnd(parsetree);
+				}
+				PG_CATCH();
+				{
+					if (isCompleteQuery)
+						EventTriggerFinalizeDrop(save_inprogress, save_objlist);
+					PG_RE_THROW();
+				}
+				PG_END_TRY();
+
+				if (isCompleteQuery)
+					EventTriggerFinalizeDrop(save_inprogress, save_objlist);
+
+				break;
+			}
 
 		case T_ReassignOwnedStmt:
 			/* no event triggers for global objects */
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 5865962..7f25de4 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -18,6 +18,7 @@
 #include "access/hash.h"
 #include "access/htup_details.h"
 #include "access/nbtree.h"
+#include "access/sysattr.h"
 #include "bootstrap/bootstrap.h"
 #include "catalog/pg_amop.h"
 #include "catalog/pg_amproc.h"
@@ -40,6 +41,7 @@
 #include "utils/lsyscache.h"
 #include "utils/rel.h"
 #include "utils/syscache.h"
+#include "utils/tqual.h"
 #include "utils/typcache.h"
 
 /* Hook for plugins to get control in get_attavgwidth() */
@@ -2926,3 +2928,54 @@ get_range_subtype(Oid rangeOid)
 	else
 		return InvalidOid;
 }
+
+/*				------------- GENERIC --------------				 */
+
+/*
+ * Return a copy of the tuple for the object with the given object OID, from
+ * the given catalog (which must have been opened by the caller and suitably
+ * locked).  NULL is returned if the OID is not found.
+ *
+ * We try a syscache first, if available.
+ */
+HeapTuple
+get_catalog_object_by_oid(Relation catalog, Oid objectId)
+{
+	HeapTuple	tuple;
+	Oid			classId = RelationGetRelid(catalog);
+	int			oidCacheId = get_object_catcache_oid(classId);
+
+	if (oidCacheId > 0)
+	{
+		tuple = SearchSysCacheCopy1(oidCacheId, ObjectIdGetDatum(objectId));
+		if (!HeapTupleIsValid(tuple))  /* should not happen */
+			return NULL;
+	}
+	else
+	{
+		Oid			oidIndexId = get_object_oid_index(classId);
+		SysScanDesc	scan;
+		ScanKeyData	skey;
+
+		Assert(OidIsValid(oidIndexId));
+
+		ScanKeyInit(&skey,
+					ObjectIdAttributeNumber,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(objectId));
+
+		scan = systable_beginscan(catalog, oidIndexId, true,
+								  SnapshotNow, 1, &skey);
+		tuple = systable_getnext(scan);
+		if (!HeapTupleIsValid(tuple))
+		{
+			systable_endscan(scan);
+			return NULL;
+		}
+		tuple = heap_copytuple(tuple);
+
+		systable_endscan(scan);
+	}
+
+	return tuple;
+}
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index d9f50d2..6b7d4a6 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4679,7 +4679,9 @@ DESCR("SP-GiST support for quad tree over range");
 DATA(insert OID = 3473 (  spg_range_quad_leaf_consistent	PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "2281 2281" _null_ _null_ _null_ _null_  spg_range_quad_leaf_consistent _null_ _null_ _null_ ));
 DESCR("SP-GiST support for quad tree over range");
 
-
+/* event triggers */
+DATA(insert OID = 3566 (  pg_event_trigger_dropped_objects		PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{26,26,26,25,25}" "{o,o,o,o,0}" "{classid, objid, objsubid, objname, schemaname}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
+DESCR("list an extension's version update paths");
 /*
  * Symbolic values for provolatile column: these indicate whether the result
  * of a function is dependent *only* on the values of its explicit arguments,
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index 74c150b..2e7d815 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -13,7 +13,10 @@
 #ifndef EVENT_TRIGGER_H
 #define EVENT_TRIGGER_H
 
+#include "catalog/dependency.h"
+#include "catalog/objectaddress.h"
 #include "catalog/pg_event_trigger.h"
+#include "lib/ilist.h"
 #include "nodes/parsenodes.h"
 
 typedef struct EventTriggerData
@@ -43,4 +46,10 @@ extern bool EventTriggerSupportsObjectType(ObjectType obtype);
 extern void EventTriggerDDLCommandStart(Node *parsetree);
 extern void EventTriggerDDLCommandEnd(Node *parsetree);
 
+extern void EventTriggerInitializeDrop(bool *save_inprogress,
+						   slist_head *save_objlist);
+extern void EventTriggerFinalizeDrop(bool save_inprogress,
+						 slist_head save_objlist);
+extern void evtrig_sqldrop_add_object(ObjectAddress *object);
+
 #endif   /* EVENT_TRIGGER_H */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 533539c..d51b829 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -1146,6 +1146,9 @@ extern Datum pg_describe_object(PG_FUNCTION_ARGS);
 /* commands/constraint.c */
 extern Datum unique_key_recheck(PG_FUNCTION_ARGS);
 
+/* commands/event_trigger.c */
+extern Datum pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS);
+
 /* commands/extension.c */
 extern Datum pg_available_extensions(PG_FUNCTION_ARGS);
 extern Datum pg_available_extension_versions(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 49f459a..dfa6eb7 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -16,6 +16,7 @@
 #include "access/attnum.h"
 #include "access/htup.h"
 #include "nodes/pg_list.h"
+#include "utils/relcache.h"
 
 /* Result list element for get_op_btree_interpretation */
 typedef struct OpBtreeInterpretation
@@ -152,6 +153,7 @@ extern void free_attstatsslot(Oid atttype,
 				  float4 *numbers, int nnumbers);
 extern char *get_namespace_name(Oid nspid);
 extern Oid	get_range_subtype(Oid rangeOid);
+extern HeapTuple get_catalog_object_by_oid(Relation catalog, Oid objectId);
 
 #define type_is_array(typid)  (get_element_type(typid) != InvalidOid)
 /* type_is_array_domain accepts both plain arrays and domains over arrays */
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to