Dimitri Fontaine escribió:
> Alvaro Herrera <alvhe...@2ndquadrant.com> writes:

> > 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.
> 
> About what missing information to add, please review:
> 
>   
> http://wiki.postgresql.org/wiki/Event_Triggers#How_to_expose_Information_to_Event_Triggers_Functions

That list contains the following items:

TG_OBJECTID
TG_OBJECTNAME
TG_SCHEMANAME
TG_OPERATION
TG_OBTYPENAME
TG_CONTEXT

Of the above, TG_OPERATION is redundant with the fact that we're
building pg_dropped_objects (i.e. the user code knows it's dealing with
a drop) and TG_CONTEXT is not relevant; with the attached patch, we
provide all the other bits.

I think this is mostly ready to go in.  I'll look at your docs, and
unless there are more objections will commit later or early tomorrow.

-- 
Álvaro Herrera                http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 198,203 **** static bool stack_address_present_add_flags(const ObjectAddress *object,
--- 198,204 ----
  								ObjectAddressStack *stack);
  static void getRelationDescription(StringInfo buffer, Oid relid);
  static void getOpFamilyDescription(StringInfo buffer, Oid opfid);
+ static void getRelationTypeDescription(StringInfo buffer, Oid relid);
  
  
  /*
***************
*** 267,272 **** performDeletion(const ObjectAddress *object,
--- 268,279 ----
  	{
  		ObjectAddress *thisobj = targetObjects->refs + i;
  
+ 		if ((!(flags & PERFORM_DELETION_INTERNAL)) &&
+ 			EventTriggerSupportsObjectType(getObjectClass(thisobj)))
+ 		{
+ 			evtrig_sqldrop_add_object(thisobj);
+ 		}
+ 
  		deleteOneObject(thisobj, &depRel, flags);
  	}
  
***************
*** 349,354 **** performMultipleDeletions(const ObjectAddresses *objects,
--- 356,367 ----
  	{
  		ObjectAddress *thisobj = targetObjects->refs + i;
  
+ 		if ((!(flags & PERFORM_DELETION_INTERNAL)) &&
+ 			EventTriggerSupportsObjectType(getObjectClass(thisobj)))
+ 		{
+ 			evtrig_sqldrop_add_object(thisobj);
+ 		}
+ 
  		deleteOneObject(thisobj, &depRel, flags);
  	}
  
***************
*** 366,371 **** performMultipleDeletions(const ObjectAddresses *objects,
--- 379,388 ----
   * 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,
***************
*** 3107,3109 **** pg_describe_object(PG_FUNCTION_ARGS)
--- 3124,3318 ----
  	description = getObjectDescription(&address);
  	PG_RETURN_TEXT_P(cstring_to_text(description));
  }
+ 
+ char *
+ getObjectTypeDescription(const ObjectAddress *object)
+ {
+ 	StringInfoData buffer;
+ 
+ 	initStringInfo(&buffer);
+ 
+ 	switch (getObjectClass(object))
+ 	{
+ 		case OCLASS_CLASS:
+ 			getRelationTypeDescription(&buffer, object->objectId);
+ 			break;
+ 
+ 		case OCLASS_PROC:
+ 			appendStringInfo(&buffer, "function");
+ 			break;
+ 
+ 		case OCLASS_TYPE:
+ 			appendStringInfo(&buffer, "type");
+ 			break;
+ 
+ 		case OCLASS_CAST:
+ 			appendStringInfo(&buffer, "cast");
+ 			break;
+ 
+ 		case OCLASS_COLLATION:
+ 			appendStringInfo(&buffer, "collation");
+ 			break;
+ 
+ 		case OCLASS_CONSTRAINT:
+ 			appendStringInfo(&buffer, "constraint");
+ 			break;
+ 
+ 		case OCLASS_CONVERSION:
+ 			appendStringInfo(&buffer, "conversion");
+ 			break;
+ 
+ 		case OCLASS_DEFAULT:
+ 			appendStringInfo(&buffer, "default value");
+ 			break;
+ 
+ 		case OCLASS_LANGUAGE:
+ 			appendStringInfo(&buffer, "language");
+ 			break;
+ 
+ 		case OCLASS_LARGEOBJECT:
+ 			appendStringInfo(&buffer, "large object");
+ 			break;
+ 
+ 		case OCLASS_OPERATOR:
+ 			appendStringInfo(&buffer, "operator");
+ 			break;
+ 
+ 		case OCLASS_OPCLASS:
+ 			appendStringInfo(&buffer, "operator class");
+ 			break;
+ 
+ 		case OCLASS_OPFAMILY:
+ 			appendStringInfo(&buffer, "operator family");
+ 			break;
+ 
+ 		case OCLASS_AMOP:
+ 			appendStringInfo(&buffer, "operator of access method");
+ 			break;
+ 
+ 		case OCLASS_AMPROC:
+ 			appendStringInfo(&buffer, "function of access method");
+ 			break;
+ 
+ 		case OCLASS_REWRITE:
+ 			appendStringInfo(&buffer, "rule");
+ 			break;
+ 
+ 		case OCLASS_TRIGGER:
+ 			appendStringInfo(&buffer, "trigger");
+ 			break;
+ 
+ 		case OCLASS_SCHEMA:
+ 			appendStringInfo(&buffer, "schema");
+ 			break;
+ 
+ 		case OCLASS_TSPARSER:
+ 			appendStringInfo(&buffer, "text search parser");
+ 			break;
+ 
+ 		case OCLASS_TSDICT:
+ 			appendStringInfo(&buffer, "text search dictionary");
+ 			break;
+ 
+ 		case OCLASS_TSTEMPLATE:
+ 			appendStringInfo(&buffer, "text search template");
+ 			break;
+ 
+ 		case OCLASS_TSCONFIG:
+ 			appendStringInfo(&buffer, "text search configuration");
+ 			break;
+ 
+ 		case OCLASS_ROLE:
+ 			appendStringInfo(&buffer, "role");
+ 			break;
+ 
+ 		case OCLASS_DATABASE:
+ 			appendStringInfo(&buffer, "database");
+ 			break;
+ 
+ 		case OCLASS_TBLSPACE:
+ 			appendStringInfo(&buffer, "tablespace");
+ 			break;
+ 
+ 		case OCLASS_FDW:
+ 			appendStringInfo(&buffer, "foreign-data wrapper");
+ 			break;
+ 
+ 		case OCLASS_FOREIGN_SERVER:
+ 			appendStringInfo(&buffer, "server");
+ 			break;
+ 
+ 		case OCLASS_USER_MAPPING:
+ 			appendStringInfo(&buffer, "user mapping");
+ 			break;
+ 
+ 		case OCLASS_DEFACL:
+ 			/* XXX do we need more detail here? */
+ 			appendStringInfo(&buffer, "default ACL");
+ 			break;
+ 
+ 		case OCLASS_EXTENSION:
+ 			appendStringInfo(&buffer, "extension");
+ 			break;
+ 
+ 		case OCLASS_EVENT_TRIGGER:
+ 			appendStringInfo(&buffer, "event trigger");
+ 			break;
+ 
+ 		default:
+ 			appendStringInfo(&buffer, "unrecognized object type");
+ 			break;
+ 	}
+ 
+ 	return buffer.data;
+ }
+ 
+ /*
+  * subroutine for getObjectTypeDescription: describe a relation type
+  */
+ static void
+ getRelationTypeDescription(StringInfo buffer, Oid relid)
+ {
+ 	HeapTuple	relTup;
+ 	Form_pg_class relForm;
+ 
+ 	relTup = SearchSysCache1(RELOID,
+ 							 ObjectIdGetDatum(relid));
+ 	if (!HeapTupleIsValid(relTup))
+ 		elog(ERROR, "cache lookup failed for relation %u", relid);
+ 	relForm = (Form_pg_class) GETSTRUCT(relTup);
+ 
+ 	switch (relForm->relkind)
+ 	{
+ 		case RELKIND_RELATION:
+ 			appendStringInfo(buffer, "table");
+ 			break;
+ 		case RELKIND_INDEX:
+ 			appendStringInfo(buffer, "index");
+ 			break;
+ 		case RELKIND_SEQUENCE:
+ 			appendStringInfo(buffer, "sequence");
+ 			break;
+ 		case RELKIND_TOASTVALUE:
+ 			appendStringInfo(buffer, "toast table");
+ 			break;
+ 		case RELKIND_VIEW:
+ 			appendStringInfo(buffer, "view");
+ 			break;
+ 		case RELKIND_MATVIEW:
+ 			appendStringInfo(buffer, "materialized view");
+ 			break;
+ 		case RELKIND_COMPOSITE_TYPE:
+ 			appendStringInfo(buffer, "composite type");
+ 			break;
+ 		case RELKIND_FOREIGN_TABLE:
+ 			appendStringInfo(buffer, "foreign table");
+ 			break;
+ 		default:
+ 			/* shouldn't get here */
+ 			appendStringInfo(buffer, "relation");
+ 			break;
+ 	}
+ 
+ 	ReleaseSysCache(relTup);
+ }
*** a/src/backend/commands/alter.c
--- b/src/backend/commands/alter.c
***************
*** 748,805 **** 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).
--- 748,753 ----
*** a/src/backend/commands/event_trigger.c
--- b/src/backend/commands/event_trigger.c
***************
*** 25,30 ****
--- 25,31 ----
  #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,44 ****
--- 40,49 ----
  #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;
***************
*** 89,94 **** static event_trigger_support_data event_trigger_support[] = {
--- 94,109 ----
  	{ NULL, false }
  };
  
+ /* Support for dropped objects */
+ typedef struct SQLDropObject
+ {
+ 	ObjectAddress	address;
+ 	char		   *objname;
+ 	char		   *schemaname;
+ 	char		   *objecttype;
+ 	slist_node		next;
+ } SQLDropObject;
+ 
  static void AlterEventTriggerOwner_internal(Relation rel,
  											HeapTuple tup,
  											Oid newOwnerId);
***************
*** 151,158 **** CreateEventTrigger(CreateEventTrigStmt *stmt)
  	}
  
  	/* Validate tag list, if any. */
! 	if (strcmp(stmt->eventname, "ddl_command_start") == 0 && tags != NULL)
  		validate_ddl_tags("tag", tags);
  
  	/*
  	 * Give user a nice error message if an event trigger of the same name
--- 166,177 ----
  	}
  
  	/* Validate tag list, if any. */
! 	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
***************
*** 220,226 **** check_ddl_tag(const char *tag)
  		pg_strcasecmp(tag, "SELECT INTO") == 0 ||
  		pg_strcasecmp(tag, "REFRESH MATERIALIZED VIEW") == 0 ||
  		pg_strcasecmp(tag, "ALTER DEFAULT PRIVILEGES") == 0 ||
! 		pg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0)
  		return EVENT_TRIGGER_COMMAND_TAG_OK;
  
  	/*
--- 239,246 ----
  		pg_strcasecmp(tag, "SELECT INTO") == 0 ||
  		pg_strcasecmp(tag, "REFRESH MATERIALIZED VIEW") == 0 ||
  		pg_strcasecmp(tag, "ALTER DEFAULT PRIVILEGES") == 0 ||
! 		pg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0 ||
! 		pg_strcasecmp(tag, "DROP OWNED") == 0)
  		return EVENT_TRIGGER_COMMAND_TAG_OK;
  
  	/*
***************
*** 827,829 **** EventTriggerSupportsObjectType(ObjectType obtype)
--- 847,1054 ----
  	}
  	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));
+ 		}
+ 	}
+ 
+ 	/* and object type, too */
+ 	obj->objecttype = getObjectTypeDescription(&obj->address);
+ 
+ 	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[6];
+ 		bool		nulls[6];
+ 
+ 		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);
+ 
+ 		/* object type */
+ 		values[3] = CStringGetTextDatum(obj->objecttype);
+ 
+ 		/* objname */
+ 		if (obj->objname)
+ 			values[4] = CStringGetTextDatum(obj->objname);
+ 		else
+ 			nulls[4] = true;
+ 
+ 		/* schemaname */
+ 		if (obj->schemaname)
+ 			values[5] = CStringGetTextDatum(obj->schemaname);
+ 		else
+ 			nulls[5] = true;
+ 
+ 		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ 	}
+ 
+ 	/* clean up and return the tuplestore */
+ 	tuplestore_donestoring(tupstore);
+ 
+ 	return (Datum) 0;
+ }
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 699,732 **** standard_ProcessUtility(Node *parsetree,
  		case T_DropStmt:
  			{
  				DropStmt   *stmt = (DropStmt *) parsetree;
  
! 				if (isCompleteQuery
! 					&& EventTriggerSupportsObjectType(stmt->removeType))
  					EventTriggerDDLCommandStart(parsetree);
  
! 				switch (stmt->removeType)
  				{
! 					case OBJECT_INDEX:
! 						if (stmt->concurrent)
! 							PreventTransactionChain(isTopLevel,
! 													"DROP INDEX CONCURRENTLY");
! 						/* fall through */
  
! 					case OBJECT_TABLE:
! 					case OBJECT_SEQUENCE:
! 					case OBJECT_VIEW:
! 					case OBJECT_MATVIEW:
! 					case OBJECT_FOREIGN_TABLE:
! 						RemoveRelations((DropStmt *) parsetree);
! 						break;
! 					default:
! 						RemoveObjects((DropStmt *) parsetree);
! 						break;
  				}
  
  				if (isCompleteQuery
  					&& EventTriggerSupportsObjectType(stmt->removeType))
! 					EventTriggerDDLCommandEnd(parsetree);
  
  				break;
  			}
--- 699,759 ----
  		case T_DropStmt:
  			{
  				DropStmt   *stmt = (DropStmt *) parsetree;
+ 				bool		save_inprogress;
+ 				slist_head	save_objlist;
  
! 				/*
! 				 * 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);
  
! 					EventTriggerInitializeDrop(&save_inprogress, &save_objlist);
! 				}
! 
! 				PG_TRY();
  				{
! 					switch (stmt->removeType)
! 					{
! 						case OBJECT_INDEX:
! 						case OBJECT_TABLE:
! 						case OBJECT_SEQUENCE:
! 						case OBJECT_VIEW:
! 						case OBJECT_MATVIEW:
! 						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))
! 					EventTriggerFinalizeDrop(save_inprogress, save_objlist);
  
  				break;
  			}
***************
*** 1248,1256 **** standard_ProcessUtility(Node *parsetree,
  			break;
  
  		case T_DropOwnedStmt:
! 			/* no event triggers for global objects */
! 			DropOwnedObjects((DropOwnedStmt *) parsetree);
! 			break;
  
  		case T_ReassignOwnedStmt:
  			/* no event triggers for global objects */
--- 1275,1311 ----
  			break;
  
  		case T_DropOwnedStmt:
! 			{
! 				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 */
*** a/src/backend/utils/cache/lsyscache.c
--- b/src/backend/utils/cache/lsyscache.c
***************
*** 18,23 ****
--- 18,24 ----
  #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,45 ****
--- 41,47 ----
  #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,2928 **** get_range_subtype(Oid rangeOid)
--- 2928,2981 ----
  	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;
+ }
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
***************
*** 179,184 **** extern ObjectClass getObjectClass(const ObjectAddress *object);
--- 179,186 ----
  extern char *getObjectDescription(const ObjectAddress *object);
  extern char *getObjectDescriptionOids(Oid classid, Oid objid);
  
+ extern char *getObjectTypeDescription(const ObjectAddress *object);
+ 
  extern ObjectAddresses *new_object_addresses(void);
  
  extern void add_exact_object_address(const ObjectAddress *object,
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 4682,4687 **** DATA(insert OID = 3473 (  spg_range_quad_leaf_consistent	PGNSP PGUID 12 1 0 0 0
--- 4682,4690 ----
  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,25}" "{o,o,o,o,o,o}" "{classid, objid, objsubid, object_type, object_name, schema_name}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
+ DESCR("list objects dropped by the current command");
  /*
   * Symbolic values for provolatile column: these indicate whether the result
   * of a function is dependent *only* on the values of its explicit arguments,
*** a/src/include/commands/event_trigger.h
--- b/src/include/commands/event_trigger.h
***************
*** 13,19 ****
--- 13,22 ----
  #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,46 **** extern bool EventTriggerSupportsObjectType(ObjectType obtype);
--- 46,55 ----
  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 */
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 1147,1152 **** extern Datum pg_describe_object(PG_FUNCTION_ARGS);
--- 1147,1155 ----
  /* 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);
*** a/src/include/utils/lsyscache.h
--- b/src/include/utils/lsyscache.h
***************
*** 16,21 ****
--- 16,22 ----
  #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,157 **** extern void free_attstatsslot(Oid atttype,
--- 153,159 ----
  				  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