aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorAlvaro Herrera <alvherre@alvh.no-ip.org>2014-12-30 15:41:50 -0300
committerAlvaro Herrera <alvherre@alvh.no-ip.org>2014-12-30 15:41:50 -0300
commita676201490c8113b4692562126c77a29dfd8dac1 (patch)
tree79c188b9f7402d4b006a4a9d86b24b82f0fa3401 /src/backend
parent5b447ad3a98aa6f059ba467eb10a832eb1544985 (diff)
downloadpostgresql-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.
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/catalog/objectaddress.c274
-rw-r--r--src/backend/utils/adt/regproc.c60
2 files changed, 314 insertions, 20 deletions
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)"
*/