(The changes in the regression test are bogus, BTW; I didn't care enough to get them fixed before sorting out the rest of the mess.)
-- Álvaro Herrera http://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
commit 89c8cbed0072ad4d921128b834fcb4f9e2eb4c33 Author: Alvaro Herrera <alvhe...@alvh.no-ip.org> Date: Mon Dec 22 18:32:43 2014 -0300 array objname/objargs stuff diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 24c64b7..112b6a0 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -17772,6 +17772,23 @@ FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger(); identifier present in the identity is quoted if necessary. </entry> </row> + <row> + <entry><literal>address_names</literal></entry> + <entry><type>text[]</type></entry> + <entry> + An array that, together with <literal>address_args</literal>, + can be used by the <function>pg_get_object_address()</function> to + recreate the object address in a remote server containing an + identically named object of the same kind. + </entry> + </row> + <row> + <entry><literal>address_args</literal></entry> + <entry><type>text[]</type></entry> + <entry> + Complement for <literal>address_names</literal> above. + </entry> + </row> </tbody> </tgroup> </informaltable> diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index 85079d6..789af5f 100644 --- a/src/backend/catalog/objectaddress.c +++ b/src/backend/catalog/objectaddress.c @@ -74,6 +74,7 @@ #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" +#include "utils/memutils.h" #include "utils/syscache.h" #include "utils/tqual.h" @@ -556,8 +557,9 @@ static void getRelationTypeDescription(StringInfo buffer, Oid relid, int32 objectSubId); static void getProcedureTypeDescription(StringInfo buffer, Oid procid); static void getConstraintTypeDescription(StringInfo buffer, Oid constroid); -static void getOpFamilyIdentity(StringInfo buffer, Oid opfid); -static void getRelationIdentity(StringInfo buffer, Oid relid); +static void getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **objname, + List **objargs); +static void getRelationIdentity(StringInfo buffer, Oid relid, List **objname); /* * Translate an object name and arguments (as passed by the parser) to an @@ -2960,6 +2962,66 @@ pg_identify_object(PG_FUNCTION_ARGS) } /* + * SQL-level callable function to obtain object type + identity + */ +Datum +pg_identify_object_as_address(PG_FUNCTION_ARGS) +{ + Oid classid = PG_GETARG_OID(0); + Oid objid = PG_GETARG_OID(1); + int32 subobjid = PG_GETARG_INT32(2); + ObjectAddress address; + char *identity; + List *names; + List *args; + Datum values[3]; + bool nulls[3]; + TupleDesc tupdesc; + HeapTuple htup; + + address.classId = classid; + address.objectId = objid; + address.objectSubId = subobjid; + + /* + * Construct a tuple descriptor for the result row. This must match this + * function's pg_proc entry! + */ + tupdesc = CreateTemplateTupleDesc(3, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "type", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "object_names", + TEXTARRAYOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "object_args", + TEXTARRAYOID, -1, 0); + + tupdesc = BlessTupleDesc(tupdesc); + + /* object type */ + values[0] = CStringGetTextDatum(getObjectTypeDescription(&address)); + nulls[0] = false; + + /* object identity */ + identity = getObjectIdentityParts(&address, &names, &args); + pfree(identity); + + /* object_names */ + values[1] = PointerGetDatum(strlist_to_textarray(names)); + nulls[1] = false; + + /* object_args */ + if (args) + values[2] = PointerGetDatum(strlist_to_textarray(args)); + else + values[2] = PointerGetDatum(construct_empty_array(TEXTOID)); + nulls[2] = false; + + htup = heap_form_tuple(tupdesc, values, nulls); + + PG_RETURN_DATUM(HeapTupleGetDatum(htup)); +} + +/* * Return a palloc'ed string that describes the type of object that the * passed address is for. * @@ -3215,7 +3277,7 @@ getProcedureTypeDescription(StringInfo buffer, Oid procid) } /* - * Return a palloc'ed string that identifies an object. + * Obtain a given object's identity, as a palloc'ed string. * * This is for machine consumption, so it's not translated. All elements are * schema-qualified when appropriate. @@ -3223,14 +3285,42 @@ getProcedureTypeDescription(StringInfo buffer, Oid procid) char * getObjectIdentity(const ObjectAddress *object) { + return getObjectIdentityParts(object, NULL, NULL); +} + +/* + * As above, but more detailed. + * + * There are two sets of return values: the identity itself as a palloc'd + * string is returned. objname and objargs, if not NULL, are output parameters + * that receive lists of C-strings that are useful to give back to + * get_object_address() to reconstruct the ObjectAddress. + */ +char * +getObjectIdentityParts(const ObjectAddress *object, + List **objname, List **objargs) +{ StringInfoData buffer; initStringInfo(&buffer); + /* + * Make sure that both objname and objargs were passed, or none was; and + * initialize them to empty lists. For objname this is useless because it + * will be initialized in all cases inside the switch; but we do it anyway + * so that we can test below that no branch leaves it unset. + */ + Assert(PointerIsValid(objname) == PointerIsValid(objargs)); + if (objname) + { + *objname = NIL; + *objargs = NIL; + } + switch (getObjectClass(object)) { case OCLASS_CLASS: - getRelationIdentity(&buffer, object->objectId); + getRelationIdentity(&buffer, object->objectId, objname); if (object->objectSubId != 0) { char *attr; @@ -3238,17 +3328,27 @@ getObjectIdentity(const ObjectAddress *object) attr = get_relid_attribute_name(object->objectId, object->objectSubId); appendStringInfo(&buffer, ".%s", quote_identifier(attr)); + if (objname) + *objname = lappend(*objname, attr); } break; case OCLASS_PROC: appendStringInfoString(&buffer, format_procedure_qualified(object->objectId)); + if (objname) + format_procedure_parts(object->objectId, objname, objargs); break; case OCLASS_TYPE: - appendStringInfoString(&buffer, - format_type_be_qualified(object->objectId)); + { + char *typeout; + + typeout = format_type_be_qualified(object->objectId); + appendStringInfoString(&buffer, typeout); + if (objname) + *objname = list_make1(typeout); + } break; case OCLASS_CAST: @@ -3271,6 +3371,12 @@ getObjectIdentity(const ObjectAddress *object) format_type_be_qualified(castForm->castsource), format_type_be_qualified(castForm->casttarget)); + if (objname) + { + *objname = list_make1(format_type_be_qualified(castForm->castsource)); + *objargs = list_make1(format_type_be_qualified(castForm->casttarget)); + } + heap_close(castRel, AccessShareLock); break; } @@ -3291,6 +3397,8 @@ getObjectIdentity(const ObjectAddress *object) appendStringInfoString(&buffer, quote_qualified_identifier(schema, NameStr(coll->collname))); + if (objname) + *objname = list_make2(schema, NameStr(coll->collname)); ReleaseSysCache(collTup); break; } @@ -3311,19 +3419,35 @@ getObjectIdentity(const ObjectAddress *object) { appendStringInfo(&buffer, "%s on ", quote_identifier(NameStr(con->conname))); - getRelationIdentity(&buffer, con->conrelid); + getRelationIdentity(&buffer, con->conrelid, objname); + if (objname) + *objname = lappend(*objname, pstrdup(NameStr(con->conname))); } else { - ObjectAddress domain; + HeapTuple domainTup; + Form_pg_type typ; - domain.classId = TypeRelationId; - domain.objectId = con->contypid; - domain.objectSubId = 0; + Assert(OidIsValid(con->contypid)); + domainTup = SearchSysCache1(TYPEOID, + ObjectIdGetDatum(con->contypid)); + if (!HeapTupleIsValid(domainTup)) + elog(ERROR, "cache lookup failed for domain %u", + con->contypid); + typ = (Form_pg_type) GETSTRUCT(domainTup); appendStringInfo(&buffer, "%s on %s", quote_identifier(NameStr(con->conname)), - getObjectIdentity(&domain)); + quote_qualified_identifier(get_namespace_name(typ->typnamespace), + NameStr(typ->typname))); + + if (objname) + { + *objname = lappend(*objname, get_namespace_name(typ->typnamespace)); + *objname = lappend(*objname, NameStr(typ->typname)); + *objname = lappend(*objname, pstrdup(NameStr(con->conname))); + } + ReleaseSysCache(domainTup); } ReleaseSysCache(conTup); @@ -3343,6 +3467,8 @@ getObjectIdentity(const ObjectAddress *object) conForm = (Form_pg_conversion) GETSTRUCT(conTup); appendStringInfoString(&buffer, quote_identifier(NameStr(conForm->conname))); + if (objname) + *objname = list_make1(pstrdup(NameStr(conForm->conname))); ReleaseSysCache(conTup); break; } @@ -3380,7 +3506,8 @@ getObjectIdentity(const ObjectAddress *object) colobject.objectSubId = attrdef->adnum; appendStringInfo(&buffer, "for %s", - getObjectIdentity(&colobject)); + getObjectIdentityParts(&colobject, + objname, objargs)); systable_endscan(adscan); heap_close(attrdefDesc, AccessShareLock); @@ -3400,17 +3527,23 @@ getObjectIdentity(const ObjectAddress *object) langForm = (Form_pg_language) GETSTRUCT(langTup); appendStringInfoString(&buffer, quote_identifier(NameStr(langForm->lanname))); + if (objname) + *objname = list_make1(pstrdup(NameStr(langForm->lanname))); ReleaseSysCache(langTup); break; } case OCLASS_LARGEOBJECT: appendStringInfo(&buffer, "%u", object->objectId); + if (objname) + *objname = list_make1(psprintf("%u", object->objectId)); break; case OCLASS_OPERATOR: appendStringInfoString(&buffer, format_operator_qualified(object->objectId)); + if (objname) + format_operator_parts(object->objectId, objname, objargs); break; case OCLASS_OPCLASS: @@ -3441,14 +3574,19 @@ getObjectIdentity(const ObjectAddress *object) NameStr(opcForm->opcname))); appendStringInfo(&buffer, " for %s", quote_identifier(NameStr(amForm->amname))); - + if (objname) + { + *objname = list_make2(pstrdup(schema), + pstrdup(NameStr(opcForm->opcname))); + *objargs = list_make1(pstrdup(NameStr(amForm->amname))); + } ReleaseSysCache(amTup); ReleaseSysCache(opcTup); break; } case OCLASS_OPFAMILY: - getOpFamilyIdentity(&buffer, object->objectId); + getOpFamilyIdentity(&buffer, object->objectId, objname, objargs); break; case OCLASS_AMOP: @@ -3460,6 +3598,10 @@ getObjectIdentity(const ObjectAddress *object) Form_pg_amop amopForm; StringInfoData opfam; + /* no objname support here */ + if (objname) + *objname = NIL; + amopDesc = heap_open(AccessMethodOperatorRelationId, AccessShareLock); @@ -3480,7 +3622,7 @@ getObjectIdentity(const ObjectAddress *object) amopForm = (Form_pg_amop) GETSTRUCT(tup); initStringInfo(&opfam); - getOpFamilyIdentity(&opfam, amopForm->amopfamily); + getOpFamilyIdentity(&opfam, amopForm->amopfamily, NULL, NULL); appendStringInfo(&buffer, "operator %d (%s, %s) of %s", amopForm->amopstrategy, @@ -3504,6 +3646,10 @@ getObjectIdentity(const ObjectAddress *object) Form_pg_amproc amprocForm; StringInfoData opfam; + /* no objname support here */ + if (objname) + *objname = NIL; + amprocDesc = heap_open(AccessMethodProcedureRelationId, AccessShareLock); @@ -3524,7 +3670,7 @@ getObjectIdentity(const ObjectAddress *object) amprocForm = (Form_pg_amproc) GETSTRUCT(tup); initStringInfo(&opfam); - getOpFamilyIdentity(&opfam, amprocForm->amprocfamily); + getOpFamilyIdentity(&opfam, amprocForm->amprocfamily, NULL, NULL); appendStringInfo(&buffer, "function %d (%s, %s) of %s", amprocForm->amprocnum, @@ -3557,7 +3703,9 @@ getObjectIdentity(const ObjectAddress *object) appendStringInfo(&buffer, "%s on ", quote_identifier(NameStr(rule->rulename))); - getRelationIdentity(&buffer, rule->ev_class); + getRelationIdentity(&buffer, rule->ev_class, objname); + if (objname) + *objname = lappend(*objname, NameStr(rule->rulename)); heap_close(ruleDesc, AccessShareLock); break; @@ -3581,7 +3729,9 @@ getObjectIdentity(const ObjectAddress *object) appendStringInfo(&buffer, "%s on ", quote_identifier(NameStr(trig->tgname))); - getRelationIdentity(&buffer, trig->tgrelid); + getRelationIdentity(&buffer, trig->tgrelid, objname); + if (objname) + *objname = lappend(*objname, NameStr(trig->tgname)); heap_close(trigDesc, AccessShareLock); break; @@ -3605,7 +3755,9 @@ getObjectIdentity(const ObjectAddress *object) appendStringInfo(&buffer, "%s on ", quote_identifier(NameStr(policy->polname))); - getRelationIdentity(&buffer, policy->polrelid); + getRelationIdentity(&buffer, policy->polrelid, objname); + if (objname) + *objname = lappend(*objname, NameStr(policy->polname)); heap_close(polDesc, AccessShareLock); break; @@ -3621,6 +3773,8 @@ getObjectIdentity(const ObjectAddress *object) object->objectId); appendStringInfoString(&buffer, quote_identifier(nspname)); + if (objname) + *objname = list_make1(nspname); break; } @@ -3640,6 +3794,9 @@ getObjectIdentity(const ObjectAddress *object) appendStringInfoString(&buffer, quote_qualified_identifier(schema, NameStr(formParser->prsname))); + if (objname) + *objname = list_make2(schema, + pstrdup(NameStr(formParser->prsname))); ReleaseSysCache(tup); break; } @@ -3660,6 +3817,9 @@ getObjectIdentity(const ObjectAddress *object) appendStringInfoString(&buffer, quote_qualified_identifier(schema, NameStr(formDict->dictname))); + if (objname) + *objname = list_make2(schema, + pstrdup(NameStr(formDict->dictname))); ReleaseSysCache(tup); break; } @@ -3680,7 +3840,9 @@ getObjectIdentity(const ObjectAddress *object) appendStringInfoString(&buffer, quote_qualified_identifier(schema, NameStr(formTmpl->tmplname))); - pfree(schema); + if (objname) + *objname = list_make2(schema, + pstrdup(NameStr(formTmpl->tmplname))); ReleaseSysCache(tup); break; } @@ -3701,6 +3863,9 @@ getObjectIdentity(const ObjectAddress *object) appendStringInfoString(&buffer, quote_qualified_identifier(schema, NameStr(formCfg->cfgname))); + if (objname) + *objname = list_make2(schema, + pstrdup(NameStr(formCfg->cfgname))); ReleaseSysCache(tup); break; } @@ -3710,6 +3875,8 @@ getObjectIdentity(const ObjectAddress *object) char *username; username = GetUserNameFromId(object->objectId); + if (objname) + *objname = list_make1(username); appendStringInfoString(&buffer, quote_identifier(username)); break; @@ -3723,6 +3890,8 @@ getObjectIdentity(const ObjectAddress *object) if (!datname) elog(ERROR, "cache lookup failed for database %u", object->objectId); + if (objname) + *objname = list_make1(datname); appendStringInfoString(&buffer, quote_identifier(datname)); break; @@ -3736,6 +3905,8 @@ getObjectIdentity(const ObjectAddress *object) if (!tblspace) elog(ERROR, "cache lookup failed for tablespace %u", object->objectId); + if (objname) + *objname = list_make1(tblspace); appendStringInfoString(&buffer, quote_identifier(tblspace)); break; @@ -3747,6 +3918,8 @@ getObjectIdentity(const ObjectAddress *object) fdw = GetForeignDataWrapper(object->objectId); appendStringInfoString(&buffer, quote_identifier(fdw->fdwname)); + if (objname) + *objname = list_make1(pstrdup(fdw->fdwname)); break; } @@ -3757,6 +3930,8 @@ getObjectIdentity(const ObjectAddress *object) srv = GetForeignServer(object->objectId); appendStringInfoString(&buffer, quote_identifier(srv->servername)); + if (objname) + *objname = list_make1(pstrdup(srv->servername)); break; } @@ -3766,6 +3941,10 @@ getObjectIdentity(const ObjectAddress *object) Oid useid; const char *usename; + /* no objname support */ + if (objname) + *objname = NIL; + tup = SearchSysCache1(USERMAPPINGOID, ObjectIdGetDatum(object->objectId)); if (!HeapTupleIsValid(tup)) @@ -3790,10 +3969,13 @@ getObjectIdentity(const ObjectAddress *object) Relation defaclrel; ScanKeyData skey[1]; SysScanDesc rcscan; - HeapTuple tup; Form_pg_default_acl defacl; + /* no objname support */ + if (objname) + *objname = NIL; + defaclrel = heap_open(DefaultAclRelationId, AccessShareLock); ScanKeyInit(&skey[0], @@ -3860,6 +4042,8 @@ getObjectIdentity(const ObjectAddress *object) elog(ERROR, "cache lookup failed for extension %u", object->objectId); appendStringInfoString(&buffer, quote_identifier(extname)); + if (objname) + *objname = list_make1(extname); break; } @@ -3868,6 +4052,10 @@ getObjectIdentity(const ObjectAddress *object) HeapTuple tup; Form_pg_event_trigger trigForm; + /* no objname support here */ + if (objname) + *objname = NIL; + tup = SearchSysCache1(EVENTTRIGGEROID, ObjectIdGetDatum(object->objectId)); if (!HeapTupleIsValid(tup)) @@ -3888,11 +4076,21 @@ getObjectIdentity(const ObjectAddress *object) break; } + /* + * If a get_object_address representation was requested, make sure we are + * providing one. We don't check for objargs, because many of the cases + * above leave it as NIL. + */ + if (objname && *objname == NIL) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("requested object address for object type that cannot support it"))); + return buffer.data; } static void -getOpFamilyIdentity(StringInfo buffer, Oid opfid) +getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **objname, List **objargs) { HeapTuple opfTup; Form_pg_opfamily opfForm; @@ -3917,6 +4115,13 @@ getOpFamilyIdentity(StringInfo buffer, Oid opfid) NameStr(opfForm->opfname)), NameStr(amForm->amname)); + if (objname) + { + *objname = list_make2(pstrdup(schema), + pstrdup(NameStr(opfForm->opfname))); + *objargs = list_make1(pstrdup(NameStr(amForm->amname))); + } + ReleaseSysCache(amTup); ReleaseSysCache(opfTup); } @@ -3926,7 +4131,7 @@ getOpFamilyIdentity(StringInfo buffer, Oid opfid) * StringInfo. */ static void -getRelationIdentity(StringInfo buffer, Oid relid) +getRelationIdentity(StringInfo buffer, Oid relid, List **objname) { HeapTuple relTup; Form_pg_class relForm; @@ -3942,6 +4147,45 @@ getRelationIdentity(StringInfo buffer, Oid relid) appendStringInfoString(buffer, quote_qualified_identifier(schema, NameStr(relForm->relname))); + if (objname) + *objname = list_make2(schema, pstrdup(NameStr(relForm->relname))); ReleaseSysCache(relTup); } + +/* + * Auxiliary function to return a TEXT array out of a list of C-strings. + */ +ArrayType * +strlist_to_textarray(List *list) +{ + ArrayType *arr; + Datum *datums; + int j = 0; + ListCell *cell; + MemoryContext memcxt; + MemoryContext oldcxt; + + memcxt = AllocSetContextCreate(CurrentMemoryContext, + "strlist to array", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + oldcxt = MemoryContextSwitchTo(memcxt); + + datums = palloc(sizeof(text *) * list_length(list)); + foreach(cell, list) + { + char *name = lfirst(cell); + + datums[j++] = CStringGetTextDatum(name); + } + + MemoryContextSwitchTo(oldcxt); + + arr = construct_array(datums, list_length(list), + TEXTOID, -1, false, 'i'); + MemoryContextDelete(memcxt); + + return arr; +} diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index 34dd3c0..33f9db4 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -117,6 +117,8 @@ typedef struct SQLDropObject const char *objname; const char *objidentity; const char *objecttype; + List *addrnames; + List *addrargs; bool original; bool normal; slist_node next; @@ -1324,10 +1326,11 @@ EventTriggerSQLDropAddObject(const ObjectAddress *object, bool original, bool no heap_close(catalog, AccessShareLock); } - /* object identity */ - obj->objidentity = getObjectIdentity(&obj->address); + /* object identity, objname and objargs */ + obj->objidentity = + getObjectIdentityParts(&obj->address, &obj->addrnames, &obj->addrargs); - /* and object type, too */ + /* object type */ obj->objecttype = getObjectTypeDescription(&obj->address); slist_push_head(&(currentEventTriggerState->SQLDropList), &obj->next); @@ -1390,8 +1393,8 @@ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS) { SQLDropObject *obj; int i = 0; - Datum values[9]; - bool nulls[9]; + Datum values[11]; + bool nulls[11]; obj = slist_container(SQLDropObject, next, iter.cur); @@ -1434,6 +1437,18 @@ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS) else nulls[i++] = true; + /* address_names */ + if (obj->addrnames) + values[i++] = PointerGetDatum(strlist_to_textarray(obj->addrnames)); + else + nulls[i++] = true; + + /* address_args */ + if (obj->addrargs) + values[i++] = PointerGetDatum(strlist_to_textarray(obj->addrargs)); + else + nulls[i++] = true; + tuplestore_putvalues(tupstore, tupdesc, values, nulls); } diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c index c0314ee..8cda52b 100644 --- a/src/backend/utils/adt/regproc.c +++ b/src/backend/utils/adt/regproc.c @@ -439,6 +439,41 @@ format_procedure_internal(Oid procedure_oid, bool force_qualify) } /* + * Output a objname/objargs representation for the procedure with the + * given OID. If it doesn't exist, an error is thrown. + * + * This can be used to feed get_object_address. + */ +void +format_procedure_parts(Oid procedure_oid, List **objnames, List **objargs) +{ + HeapTuple proctup; + Form_pg_proc procform; + int nargs; + int i; + + proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procedure_oid)); + + if (!HeapTupleIsValid(proctup)) + elog(ERROR, "cache lookup failed for procedure with OID %u", procedure_oid); + + procform = (Form_pg_proc) GETSTRUCT(proctup); + nargs = procform->pronargs; + + *objnames = list_make2(get_namespace_name(procform->pronamespace), + pstrdup(NameStr(procform->proname))); + *objargs = NIL; + for (i = 0; i < nargs; i++) + { + Oid thisargtype = procform->proargtypes.values[i]; + + *objargs = lappend(*objargs, format_type_be_qualified(thisargtype)); + } + + ReleaseSysCache(proctup); +} + +/* * regprocedureout - converts proc OID to "pro_name(args)" */ Datum @@ -875,6 +910,31 @@ format_operator_qualified(Oid operator_oid) return format_operator_internal(operator_oid, true); } +void +format_operator_parts(Oid operator_oid, List **objnames, List **objargs) +{ + HeapTuple opertup; + Form_pg_operator oprForm; + + opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operator_oid)); + if (!HeapTupleIsValid(opertup)) + elog(ERROR, "cache lookup failed for operator with OID %u", + operator_oid); + + oprForm = (Form_pg_operator) GETSTRUCT(opertup); + *objnames = list_make2(get_namespace_name(oprForm->oprnamespace), + pstrdup(NameStr(oprForm->oprname))); + *objargs = NIL; + if (oprForm->oprleft) + *objargs = lappend(*objargs, + format_type_be_qualified(oprForm->oprleft)); + if (oprForm->oprright) + *objargs = lappend(*objargs, + format_type_be_qualified(oprForm->oprright)); + + ReleaseSysCache(opertup); +} + /* * regoperatorout - converts operator OID to "opr_name(args)" */ diff --git a/src/include/catalog/objectaddress.h b/src/include/catalog/objectaddress.h index d885692..27cae44 100644 --- a/src/include/catalog/objectaddress.h +++ b/src/include/catalog/objectaddress.h @@ -58,5 +58,8 @@ extern char *getObjectDescriptionOids(Oid classid, Oid objid); extern int read_objtype_from_string(const char *objtype); extern char *getObjectTypeDescription(const ObjectAddress *object); extern char *getObjectIdentity(const ObjectAddress *address); +extern char *getObjectIdentityParts(const ObjectAddress *address, + List **objname, List **objargs); +extern ArrayType *strlist_to_textarray(List *list); #endif /* OBJECTADDRESS_H */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 484b853..54d1f2e 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -3036,6 +3036,9 @@ DESCR("get identification of SQL object"); DATA(insert OID = 3839 ( pg_identify_object PGNSP PGUID 12 1 0 0 0 f f f f t f s 3 0 2249 "26 26 23" "{26,26,23,25,25,25,25}" "{i,i,i,o,o,o,o}" "{classid,objid,subobjid,type,schema,name,identity}" _null_ pg_identify_object _null_ _null_ _null_ )); DESCR("get machine-parseable identification of SQL object"); +DATA(insert OID = 3382 ( pg_identify_object_as_address PGNSP PGUID 12 1 0 0 0 f f f f t f s 3 0 2249 "26 26 23" "{26,26,23,25,1009,1009}" "{i,i,i,o,o,o}" "{classid,objid,subobjid,type,object_names,object_args}" _null_ pg_identify_object_as_address _null_ _null_ _null_ )); +DESCR("get identification of SQL object for pg_get_object_address()"); + DATA(insert OID = 3954 ( pg_get_object_address PGNSP PGUID 12 1 0 0 0 f f f f t f s 3 0 2249 "25 1009 1009" "{25,1009,1009,26,26,23}" "{i,i,i,o,o,o}" "{type,name,args,classid,objid,subobjid}" _null_ pg_get_object_address _null_ _null_ _null_ )); DESCR("get OID-based object address from name/args arrays"); @@ -5078,7 +5081,8 @@ DATA(insert OID = 3785 ( pg_logical_slot_peek_binary_changes PGNSP PGUID 12 100 DESCR("peek at binary changes from replication slot"); /* 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,23,16,16,25,25,25,25}" "{o,o,o,o,o,o,o,o,o}" "{classid, objid, objsubid, original, normal, object_type, schema_name, object_name, object_identity}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ )); +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,23,16,16,25,25,25,25,1009,1009}" "{o,o,o,o,o,o,o,o,o,o,o}" "{classid, objid, objsubid, original, normal, object_type, schema_name, object_name, object_identity, address_names, address_args}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ )); + DESCR("list objects dropped by the current command"); DATA(insert OID = 4566 ( pg_event_trigger_table_rewrite_oid PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 26 "" "{26}" "{o}" "{oid}" _null_ pg_event_trigger_table_rewrite_oid _null_ _null_ _null_ )); DESCR("return Oid of the table getting rewritten"); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 7c4d291..e05ffb2 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -642,8 +642,12 @@ extern Datum text_regclass(PG_FUNCTION_ARGS); extern List *stringToQualifiedNameList(const char *string); extern char *format_procedure(Oid procedure_oid); extern char *format_procedure_qualified(Oid procedure_oid); +extern void format_procedure_parts(Oid operator_oid, List **objnames, + List **objargs); extern char *format_operator(Oid operator_oid); extern char *format_operator_qualified(Oid operator_oid); +extern void format_operator_parts(Oid operator_oid, List **objnames, + List **objargs); /* rowtypes.c */ extern Datum record_in(PG_FUNCTION_ARGS); @@ -1194,6 +1198,7 @@ extern Datum pg_last_committed_xact(PG_FUNCTION_ARGS); /* catalogs/dependency.c */ extern Datum pg_describe_object(PG_FUNCTION_ARGS); extern Datum pg_identify_object(PG_FUNCTION_ARGS); +extern Datum pg_identify_object_as_address(PG_FUNCTION_ARGS); /* catalog/objectaddress.c */ extern Datum pg_get_object_address(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out index ca9a6d6..baa94a8 100644 --- a/src/test/regress/expected/object_address.out +++ b/src/test/regress/expected/object_address.out @@ -339,46 +339,47 @@ WITH objects (type, name, args) AS (VALUES -- event trigger ('policy', '{addr_nsp, gentable, genpol}', '{}') ) -SELECT (pg_identify_object(classid, objid, subobjid)).* +SELECT (pg_identify_object(classid, objid, subobjid)).*, + pg_identify_object_as_address(classid, objid, subobjid) FROM objects, pg_get_object_address(type, name, args) ORDER BY classid, objid; - type | schema | name | identity ----------------------------+------------+-------------------+---------------------------------------------------------------------- - type | pg_catalog | _int4 | integer[] - type | addr_nsp | gencomptype | addr_nsp.gencomptype - type | addr_nsp | genenum | addr_nsp.genenum - type | addr_nsp | gendomain | addr_nsp.gendomain - function | pg_catalog | | pg_catalog.pg_identify_object(pg_catalog.oid,pg_catalog.oid,integer) - aggregate | addr_nsp | | addr_nsp.genaggr(integer) - sequence | addr_nsp | gentable_a_seq | addr_nsp.gentable_a_seq - table | addr_nsp | gentable | addr_nsp.gentable - table column | addr_nsp | gentable | addr_nsp.gentable.b - index | addr_nsp | gentable_pkey | addr_nsp.gentable_pkey - view | addr_nsp | genview | addr_nsp.genview - materialized view | addr_nsp | genmatview | addr_nsp.genmatview - foreign table column | addr_nsp | genftable | addr_nsp.genftable.a - foreign table | addr_nsp | genftable | addr_nsp.genftable - role | | regtest_addr_user | regtest_addr_user - server | | addr_fserv | addr_fserv - foreign-data wrapper | | addr_fdw | addr_fdw - default value | | | for addr_nsp.gentable.b - cast | | | (bigint AS integer) - table constraint | addr_nsp | | a_chk on addr_nsp.gentable - domain constraint | addr_nsp | | domconstr on addr_nsp.gendomain - conversion | pg_catalog | ascii_to_mic | ascii_to_mic - language | | plpgsql | plpgsql - schema | | addr_nsp | addr_nsp - operator class | pg_catalog | int4_ops | pg_catalog.int4_ops for btree - operator | pg_catalog | | pg_catalog.+(integer,integer) - rule | | | "_RETURN" on addr_nsp.genview - trigger | | | t on addr_nsp.gentable - operator family | pg_catalog | integer_ops | pg_catalog.integer_ops for btree - policy | | | genpol on addr_nsp.gentable - collation | pg_catalog | "default" | pg_catalog."default" - text search dictionary | addr_nsp | addr_ts_dict | addr_nsp.addr_ts_dict - text search parser | addr_nsp | addr_ts_prs | addr_nsp.addr_ts_prs - text search configuration | addr_nsp | addr_ts_conf | addr_nsp.addr_ts_conf - text search template | addr_nsp | addr_ts_temp | addr_nsp.addr_ts_temp + type | schema | name | identity | pg_identify_object_as_address +---------------------------+------------+-------------------+----------------------------------------------------------------------+---------------------------------------------------------------------------------------- + type | pg_catalog | _int4 | integer[] | (type,{integer[]},{}) + type | addr_nsp | gencomptype | addr_nsp.gencomptype | (type,{addr_nsp.gencomptype},{}) + type | addr_nsp | genenum | addr_nsp.genenum | (type,{addr_nsp.genenum},{}) + type | addr_nsp | gendomain | addr_nsp.gendomain | (type,{addr_nsp.gendomain},{}) + function | pg_catalog | | pg_catalog.pg_identify_object(pg_catalog.oid,pg_catalog.oid,integer) | (function,"{pg_catalog,pg_identify_object}","{pg_catalog.oid,pg_catalog.oid,integer}") + aggregate | addr_nsp | | addr_nsp.genaggr(integer) | (aggregate,"{addr_nsp,genaggr}",{integer}) + sequence | addr_nsp | gentable_a_seq | addr_nsp.gentable_a_seq | (sequence,"{addr_nsp,gentable_a_seq}",{}) + table | addr_nsp | gentable | addr_nsp.gentable | (table,"{addr_nsp,gentable}",{}) + table column | addr_nsp | gentable | addr_nsp.gentable.b | ("table column","{addr_nsp,gentable,b}",{}) + index | addr_nsp | gentable_pkey | addr_nsp.gentable_pkey | (index,"{addr_nsp,gentable_pkey}",{}) + view | addr_nsp | genview | addr_nsp.genview | (view,"{addr_nsp,genview}",{}) + materialized view | addr_nsp | genmatview | addr_nsp.genmatview | ("materialized view","{addr_nsp,genmatview}",{}) + foreign table column | addr_nsp | genftable | addr_nsp.genftable.a | ("foreign table column","{addr_nsp,genftable,a}",{}) + foreign table | addr_nsp | genftable | addr_nsp.genftable | ("foreign table","{addr_nsp,genftable}",{}) + role | | regtest_addr_user | regtest_addr_user | (role,{regtest_addr_user},{}) + server | | addr_fserv | addr_fserv | (server,{addr_fserv},{}) + foreign-data wrapper | | addr_fdw | addr_fdw | ("foreign-data wrapper",{addr_fdw},{}) + default value | | | for addr_nsp.gentable.b | ("default value","{addr_nsp,gentable,b}",{}) + cast | | | (bigint AS integer) | (cast,{bigint},{integer}) + table constraint | addr_nsp | | a_chk on addr_nsp.gentable | ("table constraint","{addr_nsp,gentable,a_chk}",{}) + domain constraint | addr_nsp | | domconstr on addr_nsp.gendomain | ("domain constraint","{addr_nsp.gendomain,domconstr}",{}) + conversion | pg_catalog | ascii_to_mic | ascii_to_mic | (conversion,{ascii_to_mic},{}) + language | | plpgsql | plpgsql | (language,{plpgsql},{}) + schema | | addr_nsp | addr_nsp | (schema,{addr_nsp},{}) + operator class | pg_catalog | int4_ops | pg_catalog.int4_ops for btree | ("operator class","{pg_catalog,int4_ops}",{btree}) + operator | pg_catalog | | pg_catalog.+(integer,integer) | (operator,"{pg_catalog,+}","{integer,integer}") + rule | | | "_RETURN" on addr_nsp.genview | (rule,"{addr_nsp,genview,_RETURN}",{}) + trigger | | | t on addr_nsp.gentable | (trigger,"{addr_nsp,gentable,t}",{}) + operator family | pg_catalog | integer_ops | pg_catalog.integer_ops for btree | ("operator family","{pg_catalog,integer_ops}",{btree}) + policy | | | genpol on addr_nsp.gentable | (policy,"{addr_nsp,gentable,genpol}",{}) + collation | pg_catalog | "default" | pg_catalog."default" | (collation,"{pg_catalog,default}",{}) + text search dictionary | addr_nsp | addr_ts_dict | addr_nsp.addr_ts_dict | ("text search dictionary","{addr_nsp,addr_ts_dict}",{}) + text search parser | addr_nsp | addr_ts_prs | addr_nsp.addr_ts_prs | ("text search parser","{addr_nsp,addr_ts_prs}",{}) + text search configuration | addr_nsp | addr_ts_conf | addr_nsp.addr_ts_conf | ("text search configuration","{addr_nsp,addr_ts_conf}",{}) + text search template | addr_nsp | addr_ts_temp | addr_nsp.addr_ts_temp | ("text search template","{addr_nsp,addr_ts_temp}",{}) (35 rows) --- diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql index e5209b9..84032fe 100644 --- a/src/test/regress/sql/object_address.sql +++ b/src/test/regress/sql/object_address.sql @@ -159,7 +159,8 @@ WITH objects (type, name, args) AS (VALUES -- event trigger ('policy', '{addr_nsp, gentable, genpol}', '{}') ) -SELECT (pg_identify_object(classid, objid, subobjid)).* +SELECT (pg_identify_object(classid, objid, subobjid)).*, + pg_identify_object_as_address(classid, objid, subobjid) FROM objects, pg_get_object_address(type, name, args) ORDER BY classid, objid;
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers