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