From a676201490c8113b4692562126c77a29dfd8dac1 Mon Sep 17 00:00:00 2001 From: Alvaro Herrera Date: Tue, 30 Dec 2014 15:41:50 -0300 Subject: [PATCH] Add pg_identify_object_as_address This function returns object type and objname/objargs arrays, which can be passed to pg_get_object_address. This is especially useful because the textual representation can be copied to a remote server in order to obtain the corresponding OID-based address. In essence, this function is the inverse of recently added pg_get_object_address(). Catalog version bumped due to the addition of the new function. Also add docs to pg_get_object_address. --- doc/src/sgml/func.sgml | 149 +++++++--- src/backend/catalog/objectaddress.c | 274 +++++++++++++++++-- src/backend/utils/adt/regproc.c | 60 ++++ src/include/catalog/catversion.h | 2 +- src/include/catalog/objectaddress.h | 3 + src/include/catalog/pg_proc.h | 3 + src/include/utils/builtins.h | 5 + src/test/regress/expected/object_address.out | 85 +++--- src/test/regress/sql/object_address.sql | 11 +- 9 files changed, 485 insertions(+), 107 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 24c64b7187..53aeb12f9a 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -15307,14 +15307,6 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); format_type - - pg_describe_object - - - - pg_identify_object - - pg_get_constraintdef @@ -15429,16 +15421,6 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); text get SQL name of a data type - - pg_describe_object(catalog_id, object_id, object_sub_id) - text - get description of a database object - - - pg_identify_object(catalog_id oid, object_id oid, object_sub_id integer) - type text, schema text, name text, identity text - get identity of a database object - pg_get_constraintdef(constraint_oid) text @@ -15707,31 +15689,6 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); pg_class catalogs. - - pg_describe_object returns a textual description of a database - object specified by catalog OID, object OID and a (possibly zero) sub-object ID. - This description is intended to be human-readable, and might be translated, - depending on server configuration. - This is useful to determine the identity of an object as stored in the - pg_depend catalog. - - - - pg_identify_object returns a row containing enough information - to uniquely identify the database object specified by catalog OID, object OID and a - (possibly zero) sub-object ID. This information is intended to be machine-readable, - and is never translated. - type identifies the type of database object; - schema is the schema name that the object belongs in, or - NULL for object types that do not belong to schemas; - name is the name of the object, quoted if necessary, only - present if it can be used (alongside schema name, if pertinent) as a unique - identifier of the object, otherwise NULL; - identity is the complete object identity, with the precise format - depending on object type, and each part within the format being - schema-qualified and quoted as necessary. - - pg_typeof returns the OID of the data type of the value that is passed to it. This can be helpful for troubleshooting or @@ -15790,6 +15747,112 @@ SELECT collation for ('foo' COLLATE "de_DE"); the given name matches multiple objects). + + pg_describe_object + + + + pg_identify_object + + + + pg_identify_object_as_address + + + + pg_get_object_address + + + + lists functions related to + database object identification and addressing. + + + + Object Information and Addressing Functions + + + Name Return Type Description + + + + + pg_describe_object(catalog_id, object_id, object_sub_id) + text + get description of a database object + + + pg_identify_object(catalog_id oid, object_id oid, object_sub_id integer) + type text, schema text, name text, identity text + get identity of a database object + + + pg_identify_object_as_address(catalog_id oid, object_id oid, object_sub_id integer) + type text, name text[], args text[] + get external representation of a database object's address + + + pg_get_object_address(type text, name text[], args text[]) + catalog_id oid, object_id oid, object_sub_id int32 + get address of a database object, from its external representation + + + +
+ + + pg_describe_object returns a textual description of a database + object specified by catalog OID, object OID and a (possibly zero) sub-object ID. + This description is intended to be human-readable, and might be translated, + depending on server configuration. + This is useful to determine the identity of an object as stored in the + pg_depend catalog. + + + + pg_identify_object returns a row containing enough information + to uniquely identify the database object specified by catalog OID, object OID and a + (possibly zero) sub-object ID. This information is intended to be machine-readable, + and is never translated. + type identifies the type of database object; + schema is the schema name that the object belongs in, or + NULL for object types that do not belong to schemas; + name is the name of the object, quoted if necessary, only + present if it can be used (alongside schema name, if pertinent) as a unique + identifier of the object, otherwise NULL; + identity is the complete object identity, with the precise format + depending on object type, and each part within the format being + schema-qualified and quoted as necessary. + + + + pg_identify_object_as_address returns a row containing + enough information to uniquely identify the database object specified by + catalog OID, object OID and a (possibly zero) sub-object ID. The returned + information is independent of the current server, that is, it could be used + to identify an identically named object in another server. + type identifies the type of database object; + name and args are text arrays that together + form a reference to the object. These three columns can be passed to + pg_get_object_address to obtain the internal address + of the object. + This function is the inverse of pg_get_object_address. + + + + pg_get_object_address returns a row containing enough + information to uniquely identify the database object specified by its + type and object name and argument arrays. The returned values are the + ones that would be used in system catalogs such as pg_depend + and can be passed to other system functions such as + pg_identify_object or pg_describe_object. + catalog_id is the OID of the system catalog containing the + object; + object_id is the OID of the object itself, and + object_sub_id is the object sub-ID, or zero if none. + This function is the inverse of pg_identify_object_as_address. + + col_description diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index 9ca609d886..cd763b3b91 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 @@ -2931,6 +2933,66 @@ pg_identify_object(PG_FUNCTION_ARGS) PG_RETURN_DATUM(HeapTupleGetDatum(htup)); } +/* + * 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. @@ -3187,22 +3249,50 @@ 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. */ 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; @@ -3210,17 +3300,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: @@ -3243,6 +3343,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; } @@ -3263,6 +3369,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; } @@ -3283,19 +3391,25 @@ 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; + Assert(OidIsValid(con->contypid)); domain.classId = TypeRelationId; domain.objectId = con->contypid; domain.objectSubId = 0; appendStringInfo(&buffer, "%s on %s", quote_identifier(NameStr(con->conname)), - getObjectIdentity(&domain)); + getObjectIdentityParts(&domain, objname, objargs)); + + if (objname) + *objargs = lappend(*objargs, pstrdup(NameStr(con->conname))); } ReleaseSysCache(conTup); @@ -3315,6 +3429,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; } @@ -3352,7 +3468,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); @@ -3372,17 +3489,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: @@ -3413,14 +3536,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: @@ -3432,6 +3560,10 @@ getObjectIdentity(const ObjectAddress *object) Form_pg_amop amopForm; StringInfoData opfam; + /* no objname support here */ + if (objname) + *objname = NIL; + amopDesc = heap_open(AccessMethodOperatorRelationId, AccessShareLock); @@ -3452,7 +3584,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, @@ -3476,6 +3608,10 @@ getObjectIdentity(const ObjectAddress *object) Form_pg_amproc amprocForm; StringInfoData opfam; + /* no objname support here */ + if (objname) + *objname = NIL; + amprocDesc = heap_open(AccessMethodProcedureRelationId, AccessShareLock); @@ -3496,7 +3632,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, @@ -3529,7 +3665,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; @@ -3553,7 +3691,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; @@ -3577,7 +3717,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; @@ -3593,6 +3735,8 @@ getObjectIdentity(const ObjectAddress *object) object->objectId); appendStringInfoString(&buffer, quote_identifier(nspname)); + if (objname) + *objname = list_make1(nspname); break; } @@ -3612,6 +3756,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; } @@ -3632,6 +3779,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; } @@ -3652,7 +3802,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; } @@ -3673,6 +3825,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; } @@ -3682,6 +3837,8 @@ getObjectIdentity(const ObjectAddress *object) char *username; username = GetUserNameFromId(object->objectId); + if (objname) + *objname = list_make1(username); appendStringInfoString(&buffer, quote_identifier(username)); break; @@ -3695,6 +3852,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; @@ -3708,6 +3867,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; @@ -3719,6 +3880,8 @@ getObjectIdentity(const ObjectAddress *object) fdw = GetForeignDataWrapper(object->objectId); appendStringInfoString(&buffer, quote_identifier(fdw->fdwname)); + if (objname) + *objname = list_make1(pstrdup(fdw->fdwname)); break; } @@ -3729,6 +3892,8 @@ getObjectIdentity(const ObjectAddress *object) srv = GetForeignServer(object->objectId); appendStringInfoString(&buffer, quote_identifier(srv->servername)); + if (objname) + *objname = list_make1(pstrdup(srv->servername)); break; } @@ -3738,6 +3903,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)) @@ -3762,10 +3931,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], @@ -3832,6 +4004,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; } @@ -3840,6 +4014,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)) @@ -3860,11 +4038,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; @@ -3889,6 +4077,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); } @@ -3898,7 +4093,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; @@ -3914,6 +4109,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/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c index c0314ee532..8cda52ba8c 100644 --- a/src/backend/utils/adt/regproc.c +++ b/src/backend/utils/adt/regproc.c @@ -438,6 +438,41 @@ format_procedure_internal(Oid procedure_oid, bool force_qualify) return result; } +/* + * 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)" */ @@ -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/catversion.h b/src/include/catalog/catversion.h index 042ecef802..b4c08136f6 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201412234 +#define CATALOG_VERSION_NO 201412301 #endif diff --git a/src/include/catalog/objectaddress.h b/src/include/catalog/objectaddress.h index d885692a43..27cae445a1 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 484b853a10..5c10d96ce2 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"); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 7c4d29145e..e05ffb28ca 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 87c08daba8..8e11b42759 100644 --- a/src/test/regress/expected/object_address.out +++ b/src/test/regress/expected/object_address.out @@ -339,46 +339,51 @@ WITH objects (type, name, args) AS (VALUES -- event trigger ('policy', '{addr_nsp, gentable, genpol}', '{}') ) -SELECT (pg_identify_object(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 +SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*, + -- test roundtrip through pg_identify_object_as_address + ROW(pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)) = + ROW(pg_identify_object(addr2.classid, addr2.objid, addr2.subobjid)) + FROM objects, pg_get_object_address(type, name, args) addr1, + pg_identify_object_as_address(classid, objid, subobjid) ioa(typ,nms,args), + pg_get_object_address(typ, nms, ioa.args) as addr2 + ORDER BY addr1.classid, addr1.objid; + type | schema | name | identity | ?column? +---------------------------+------------+-------------------+----------------------------------------------------------------------+---------- + type | pg_catalog | _int4 | integer[] | t + type | addr_nsp | gencomptype | addr_nsp.gencomptype | t + type | addr_nsp | genenum | addr_nsp.genenum | t + type | addr_nsp | gendomain | addr_nsp.gendomain | t + function | pg_catalog | | pg_catalog.pg_identify_object(pg_catalog.oid,pg_catalog.oid,integer) | t + aggregate | addr_nsp | | addr_nsp.genaggr(integer) | t + sequence | addr_nsp | gentable_a_seq | addr_nsp.gentable_a_seq | t + table | addr_nsp | gentable | addr_nsp.gentable | t + table column | addr_nsp | gentable | addr_nsp.gentable.b | t + index | addr_nsp | gentable_pkey | addr_nsp.gentable_pkey | t + view | addr_nsp | genview | addr_nsp.genview | t + materialized view | addr_nsp | genmatview | addr_nsp.genmatview | t + foreign table column | addr_nsp | genftable | addr_nsp.genftable.a | t + foreign table | addr_nsp | genftable | addr_nsp.genftable | t + role | | regtest_addr_user | regtest_addr_user | t + server | | addr_fserv | addr_fserv | t + foreign-data wrapper | | addr_fdw | addr_fdw | t + default value | | | for addr_nsp.gentable.b | t + cast | | | (bigint AS integer) | t + table constraint | addr_nsp | | a_chk on addr_nsp.gentable | t + domain constraint | addr_nsp | | domconstr on addr_nsp.gendomain | t + conversion | pg_catalog | ascii_to_mic | ascii_to_mic | t + language | | plpgsql | plpgsql | t + schema | | addr_nsp | addr_nsp | t + operator class | pg_catalog | int4_ops | pg_catalog.int4_ops for btree | t + operator | pg_catalog | | pg_catalog.+(integer,integer) | t + rule | | | "_RETURN" on addr_nsp.genview | t + trigger | | | t on addr_nsp.gentable | t + operator family | pg_catalog | integer_ops | pg_catalog.integer_ops for btree | t + policy | | | genpol on addr_nsp.gentable | t + collation | pg_catalog | "default" | pg_catalog."default" | t + text search dictionary | addr_nsp | addr_ts_dict | addr_nsp.addr_ts_dict | t + text search parser | addr_nsp | addr_ts_prs | addr_nsp.addr_ts_prs | t + text search configuration | addr_nsp | addr_ts_conf | addr_nsp.addr_ts_conf | t + text search template | addr_nsp | addr_ts_temp | addr_nsp.addr_ts_temp | t (35 rows) --- diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql index dc55895d93..9fc27d8f6e 100644 --- a/src/test/regress/sql/object_address.sql +++ b/src/test/regress/sql/object_address.sql @@ -159,9 +159,14 @@ WITH objects (type, name, args) AS (VALUES -- event trigger ('policy', '{addr_nsp, gentable, genpol}', '{}') ) -SELECT (pg_identify_object(classid, objid, subobjid)).* - FROM objects, pg_get_object_address(type, name, args) -ORDER BY classid, objid; +SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*, + -- test roundtrip through pg_identify_object_as_address + ROW(pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)) = + ROW(pg_identify_object(addr2.classid, addr2.objid, addr2.subobjid)) + FROM objects, pg_get_object_address(type, name, args) addr1, + pg_identify_object_as_address(classid, objid, subobjid) ioa(typ,nms,args), + pg_get_object_address(typ, nms, ioa.args) as addr2 + ORDER BY addr1.classid, addr1.objid; --- --- Cleanup resources -- 2.40.0