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