diff options
author | Alvaro Herrera <alvherre@alvh.no-ip.org> | 2014-12-30 15:41:50 -0300 |
---|---|---|
committer | Alvaro Herrera <alvherre@alvh.no-ip.org> | 2014-12-30 15:41:50 -0300 |
commit | a676201490c8113b4692562126c77a29dfd8dac1 (patch) | |
tree | 79c188b9f7402d4b006a4a9d86b24b82f0fa3401 | |
parent | 5b447ad3a98aa6f059ba467eb10a832eb1544985 (diff) | |
download | postgresql-a676201490c8113b4692562126c77a29dfd8dac1.tar.gz postgresql-a676201490c8113b4692562126c77a29dfd8dac1.zip |
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.
-rw-r--r-- | doc/src/sgml/func.sgml | 149 | ||||
-rw-r--r-- | src/backend/catalog/objectaddress.c | 274 | ||||
-rw-r--r-- | src/backend/utils/adt/regproc.c | 60 | ||||
-rw-r--r-- | src/include/catalog/catversion.h | 2 | ||||
-rw-r--r-- | src/include/catalog/objectaddress.h | 3 | ||||
-rw-r--r-- | src/include/catalog/pg_proc.h | 3 | ||||
-rw-r--r-- | src/include/utils/builtins.h | 5 | ||||
-rw-r--r-- | src/test/regress/expected/object_address.out | 85 | ||||
-rw-r--r-- | 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 24c64b7187f..53aeb12f9a5 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -15308,14 +15308,6 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); </indexterm> <indexterm> - <primary>pg_describe_object</primary> - </indexterm> - - <indexterm> - <primary>pg_identify_object</primary> - </indexterm> - - <indexterm> <primary>pg_get_constraintdef</primary> </indexterm> @@ -15430,16 +15422,6 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); <entry>get SQL name of a data type</entry> </row> <row> - <entry><literal><function>pg_describe_object(<parameter>catalog_id</parameter>, <parameter>object_id</parameter>, <parameter>object_sub_id</parameter>)</function></literal></entry> - <entry><type>text</type></entry> - <entry>get description of a database object</entry> - </row> - <row> - <entry><literal><function>pg_identify_object(<parameter>catalog_id</parameter> <type>oid</>, <parameter>object_id</parameter> <type>oid</>, <parameter>object_sub_id</parameter> <type>integer</>)</function></literal></entry> - <entry><parameter>type</> <type>text</>, <parameter>schema</> <type>text</>, <parameter>name</> <type>text</>, <parameter>identity</> <type>text</></entry> - <entry>get identity of a database object</entry> - </row> - <row> <entry><literal><function>pg_get_constraintdef(<parameter>constraint_oid</parameter>)</function></literal></entry> <entry><type>text</type></entry> <entry>get definition of a constraint</entry> @@ -15708,31 +15690,6 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); </para> <para> - <function>pg_describe_object</function> 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 - <structname>pg_depend</structname> catalog. - </para> - - <para> - <function>pg_identify_object</function> 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. - <parameter>type</> identifies the type of database object; - <parameter>schema</> is the schema name that the object belongs in, or - <literal>NULL</> for object types that do not belong to schemas; - <parameter>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 <literal>NULL</>; - <parameter>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. - </para> - - <para> <function>pg_typeof</function> returns the OID of the data type of the value that is passed to it. This can be helpful for troubleshooting or dynamically constructing SQL queries. The function is declared as @@ -15791,6 +15748,112 @@ SELECT collation for ('foo' COLLATE "de_DE"); </para> <indexterm> + <primary>pg_describe_object</primary> + </indexterm> + + <indexterm> + <primary>pg_identify_object</primary> + </indexterm> + + <indexterm> + <primary>pg_identify_object_as_address</primary> + </indexterm> + + <indexterm> + <primary>pg_get_object_address</primary> + </indexterm> + + <para> + <xref linkend="functions-info-object-table"> lists functions related to + database object identification and addressing. + </para> + + <table id="functions-info-object-table"> + <title>Object Information and Addressing Functions</title> + <tgroup cols="3"> + <thead> + <row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry></row> + </thead> + + <tbody> + <row> + <entry><literal><function>pg_describe_object(<parameter>catalog_id</parameter>, <parameter>object_id</parameter>, <parameter>object_sub_id</parameter>)</function></literal></entry> + <entry><type>text</type></entry> + <entry>get description of a database object</entry> + </row> + <row> + <entry><literal><function>pg_identify_object(<parameter>catalog_id</parameter> <type>oid</>, <parameter>object_id</parameter> <type>oid</>, <parameter>object_sub_id</parameter> <type>integer</>)</function></literal></entry> + <entry><parameter>type</> <type>text</>, <parameter>schema</> <type>text</>, <parameter>name</> <type>text</>, <parameter>identity</> <type>text</></entry> + <entry>get identity of a database object</entry> + </row> + <row> + <entry><literal><function>pg_identify_object_as_address(<parameter>catalog_id</parameter> <type>oid</>, <parameter>object_id</parameter> <type>oid</>, <parameter>object_sub_id</parameter> <type>integer</>)</function></literal></entry> + <entry><parameter>type</> <type>text</>, <parameter>name</> <type>text[]</>, <parameter>args</> <type>text[]</></entry> + <entry>get external representation of a database object's address</entry> + </row> + <row> + <entry><literal><function>pg_get_object_address(<parameter>type</parameter> <type>text</>, <parameter>name</parameter> <type>text[]</>, <parameter>args</parameter> <type>text[]</>)</function></literal></entry> + <entry><parameter>catalog_id</> <type>oid</>, <parameter>object_id</> <type>oid</>, <parameter>object_sub_id</> <type>int32</></entry> + <entry>get address of a database object, from its external representation</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + <function>pg_describe_object</function> 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 + <structname>pg_depend</structname> catalog. + </para> + + <para> + <function>pg_identify_object</function> 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. + <parameter>type</> identifies the type of database object; + <parameter>schema</> is the schema name that the object belongs in, or + <literal>NULL</> for object types that do not belong to schemas; + <parameter>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 <literal>NULL</>; + <parameter>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. + </para> + + <para> + <function>pg_identify_object_as_address</function> 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. + <parameter>type</> identifies the type of database object; + <parameter>name</> and <parameter>args</> are text arrays that together + form a reference to the object. These three columns can be passed to + <function>pg_get_object_address</> to obtain the internal address + of the object. + This function is the inverse of <function>pg_get_object_address</function>. + </para> + + <para> + <function>pg_get_object_address</function> 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 <structname>pg_depend</> + and can be passed to other system functions such as + <function>pg_identify_object</> or <function>pg_describe_object</>. + <parameter>catalog_id</> is the OID of the system catalog containing the + object; + <parameter>object_id</> is the OID of the object itself, and + <parameter>object_sub_id</> is the object sub-ID, or zero if none. + This function is the inverse of <function>pg_identify_object_as_address</function>. + </para> + + <indexterm> <primary>col_description</primary> </indexterm> diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index 9ca609d8868..cd763b3b91d 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 @@ -2932,6 +2934,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. * @@ -3187,7 +3249,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. @@ -3195,14 +3257,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; @@ -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 c0314ee5322..8cda52ba8cb 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/catversion.h b/src/include/catalog/catversion.h index 042ecef8029..b4c08136f62 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 d885692a43a..27cae445a1a 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 484b853a10e..5c10d96ce29 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 7c4d29145e9..e05ffb28ca4 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 87c08daba81..8e11b427590 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 dc55895d932..9fc27d8f6e6 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 |