Commit a61fd533 authored by Alvaro Herrera's avatar Alvaro Herrera

Support opfamily members in get_object_address

In the spirit of 890192e9 and 44643034: have get_object_address
understand individual pg_amop and pg_amproc objects.  There is no way to
refer to such objects directly in the grammar -- rather, they are almost
always considered an integral part of the opfamily that contains them.
(The only case that deals with them individually is ALTER OPERATOR
FAMILY ADD/DROP, which carries the opfamily address separately and thus
does not need it to be part of each added/dropped element's address.)
In event triggers it becomes possible to become involved with individual
amop/amproc elements, and this commit enables pg_get_object_address to
do so as well.

To make the overall coding simpler, this commit also slightly changes
the get_object_address representation for opclasses and opfamilies:
instead of having the AM name in the objargs array, I moved it as the
first element of the objnames array.  This enables the new code to use
objargs for the type names used by pg_amop and pg_amproc.

Reviewed by: Stephen Frost
parent 8d1f2390
...@@ -492,9 +492,9 @@ ObjectTypeMap[] = ...@@ -492,9 +492,9 @@ ObjectTypeMap[] =
/* OCLASS_OPFAMILY */ /* OCLASS_OPFAMILY */
{ "operator family", OBJECT_OPFAMILY }, { "operator family", OBJECT_OPFAMILY },
/* OCLASS_AMOP */ /* OCLASS_AMOP */
{ "operator of access method", -1 }, /* unmapped */ { "operator of access method", OBJECT_AMOP },
/* OCLASS_AMPROC */ /* OCLASS_AMPROC */
{ "function of access method", -1 }, /* unmapped */ { "function of access method", OBJECT_AMPROC },
/* OCLASS_REWRITE */ /* OCLASS_REWRITE */
{ "rule", OBJECT_RULE }, { "rule", OBJECT_RULE },
/* OCLASS_TRIGGER */ /* OCLASS_TRIGGER */
...@@ -552,9 +552,12 @@ static ObjectAddress get_object_address_attrdef(ObjectType objtype, ...@@ -552,9 +552,12 @@ static ObjectAddress get_object_address_attrdef(ObjectType objtype,
List *objname, Relation *relp, LOCKMODE lockmode, List *objname, Relation *relp, LOCKMODE lockmode,
bool missing_ok); bool missing_ok);
static ObjectAddress get_object_address_type(ObjectType objtype, static ObjectAddress get_object_address_type(ObjectType objtype,
List *objname, bool missing_ok); ListCell *typecell, bool missing_ok);
static ObjectAddress get_object_address_opcf(ObjectType objtype, List *objname, static ObjectAddress get_object_address_opcf(ObjectType objtype, List *objname,
List *objargs, bool missing_ok); bool missing_ok);
static ObjectAddress get_object_address_opf_member(ObjectType objtype,
List *objname, List *objargs, bool missing_ok);
static ObjectAddress get_object_address_usermapping(List *objname, static ObjectAddress get_object_address_usermapping(List *objname,
List *objargs, bool missing_ok); List *objargs, bool missing_ok);
static ObjectAddress get_object_address_defacl(List *objname, List *objargs, static ObjectAddress get_object_address_defacl(List *objname, List *objargs,
...@@ -567,8 +570,7 @@ static void getRelationTypeDescription(StringInfo buffer, Oid relid, ...@@ -567,8 +570,7 @@ static void getRelationTypeDescription(StringInfo buffer, Oid relid,
int32 objectSubId); int32 objectSubId);
static void getProcedureTypeDescription(StringInfo buffer, Oid procid); static void getProcedureTypeDescription(StringInfo buffer, Oid procid);
static void getConstraintTypeDescription(StringInfo buffer, Oid constroid); static void getConstraintTypeDescription(StringInfo buffer, Oid constroid);
static void getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **objname, static void getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **objname);
List **objargs);
static void getRelationIdentity(StringInfo buffer, Oid relid, List **objname); static void getRelationIdentity(StringInfo buffer, Oid relid, List **objname);
/* /*
...@@ -661,7 +663,8 @@ get_object_address(ObjectType objtype, List *objname, List *objargs, ...@@ -661,7 +663,8 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
ObjectAddress domaddr; ObjectAddress domaddr;
char *constrname; char *constrname;
domaddr = get_object_address_type(OBJECT_DOMAIN, objname, missing_ok); domaddr = get_object_address_type(OBJECT_DOMAIN,
list_head(objname), missing_ok);
constrname = strVal(linitial(objargs)); constrname = strVal(linitial(objargs));
address.classId = ConstraintRelationId; address.classId = ConstraintRelationId;
...@@ -685,7 +688,7 @@ get_object_address(ObjectType objtype, List *objname, List *objargs, ...@@ -685,7 +688,7 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
break; break;
case OBJECT_TYPE: case OBJECT_TYPE:
case OBJECT_DOMAIN: case OBJECT_DOMAIN:
address = get_object_address_type(objtype, objname, missing_ok); address = get_object_address_type(objtype, list_head(objname), missing_ok);
break; break;
case OBJECT_AGGREGATE: case OBJECT_AGGREGATE:
address.classId = ProcedureRelationId; address.classId = ProcedureRelationId;
...@@ -721,8 +724,12 @@ get_object_address(ObjectType objtype, List *objname, List *objargs, ...@@ -721,8 +724,12 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
break; break;
case OBJECT_OPCLASS: case OBJECT_OPCLASS:
case OBJECT_OPFAMILY: case OBJECT_OPFAMILY:
address = get_object_address_opcf(objtype, address = get_object_address_opcf(objtype, objname, missing_ok);
objname, objargs, missing_ok); break;
case OBJECT_AMOP:
case OBJECT_AMPROC:
address = get_object_address_opf_member(objtype, objname,
objargs, missing_ok);
break; break;
case OBJECT_LARGEOBJECT: case OBJECT_LARGEOBJECT:
Assert(list_length(objname) == 1); Assert(list_length(objname) == 1);
...@@ -1309,13 +1316,13 @@ get_object_address_attrdef(ObjectType objtype, List *objname, ...@@ -1309,13 +1316,13 @@ get_object_address_attrdef(ObjectType objtype, List *objname,
* Find the ObjectAddress for a type or domain * Find the ObjectAddress for a type or domain
*/ */
static ObjectAddress static ObjectAddress
get_object_address_type(ObjectType objtype, List *objname, bool missing_ok) get_object_address_type(ObjectType objtype, ListCell *typecell, bool missing_ok)
{ {
ObjectAddress address; ObjectAddress address;
TypeName *typename; TypeName *typename;
Type tup; Type tup;
typename = (TypeName *) linitial(objname); typename = (TypeName *) lfirst(typecell);
address.classId = TypeRelationId; address.classId = TypeRelationId;
address.objectId = InvalidOid; address.objectId = InvalidOid;
...@@ -1351,15 +1358,14 @@ get_object_address_type(ObjectType objtype, List *objname, bool missing_ok) ...@@ -1351,15 +1358,14 @@ get_object_address_type(ObjectType objtype, List *objname, bool missing_ok)
* Find the ObjectAddress for an opclass or opfamily. * Find the ObjectAddress for an opclass or opfamily.
*/ */
static ObjectAddress static ObjectAddress
get_object_address_opcf(ObjectType objtype, get_object_address_opcf(ObjectType objtype, List *objname, bool missing_ok)
List *objname, List *objargs, bool missing_ok)
{ {
Oid amoid; Oid amoid;
ObjectAddress address; ObjectAddress address;
Assert(list_length(objargs) == 1);
/* XXX no missing_ok support here */ /* XXX no missing_ok support here */
amoid = get_am_oid(strVal(linitial(objargs)), false); amoid = get_am_oid(strVal(linitial(objname)), false);
objname = list_copy_tail(objname, 1);
switch (objtype) switch (objtype)
{ {
...@@ -1384,6 +1390,114 @@ get_object_address_opcf(ObjectType objtype, ...@@ -1384,6 +1390,114 @@ get_object_address_opcf(ObjectType objtype,
return address; return address;
} }
/*
* Find the ObjectAddress for an opclass/opfamily member.
*
* (The returned address corresponds to a pg_amop/pg_amproc object).
*/
static ObjectAddress
get_object_address_opf_member(ObjectType objtype,
List *objname, List *objargs, bool missing_ok)
{
ObjectAddress famaddr;
ObjectAddress address;
ListCell *cell;
List *copy;
char *typenames[2];
Oid typeoids[2];
int membernum;
int i;
/*
* The last element of the objname list contains the strategy or procedure
* number. We need to strip that out before getting the opclass/family
* address. The rest can be used directly by get_object_address_opcf().
*/
membernum = atoi(strVal(llast(objname)));
copy = list_truncate(list_copy(objname), list_length(objname) - 1);
/* no missing_ok support here */
famaddr = get_object_address_opcf(OBJECT_OPFAMILY, copy, false);
/* find out left/right type names and OIDs */
i = 0;
foreach (cell, objargs)
{
ObjectAddress typaddr;
typenames[i] = strVal(lfirst(cell));
typaddr = get_object_address_type(OBJECT_TYPE, cell, missing_ok);
typeoids[i] = typaddr.objectId;
if (i++ >= 2)
break;
}
switch (objtype)
{
case OBJECT_AMOP:
{
HeapTuple tp;
ObjectAddressSet(address, AccessMethodOperatorRelationId,
InvalidOid);
tp = SearchSysCache4(AMOPSTRATEGY,
ObjectIdGetDatum(famaddr.objectId),
ObjectIdGetDatum(typeoids[0]),
ObjectIdGetDatum(typeoids[1]),
Int16GetDatum(membernum));
if (!HeapTupleIsValid(tp))
{
if (!missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("operator %d (%s, %s) of %s does not exist",
membernum, typenames[0], typenames[1],
getObjectDescription(&famaddr))));
}
else
{
address.objectId = HeapTupleGetOid(tp);
ReleaseSysCache(tp);
}
}
break;
case OBJECT_AMPROC:
{
HeapTuple tp;
ObjectAddressSet(address, AccessMethodProcedureRelationId,
InvalidOid);
tp = SearchSysCache4(AMPROCNUM,
ObjectIdGetDatum(famaddr.objectId),
ObjectIdGetDatum(typeoids[0]),
ObjectIdGetDatum(typeoids[1]),
Int16GetDatum(membernum));
if (!HeapTupleIsValid(tp))
{
if (!missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("function %d (%s, %s) of %s does not exist",
membernum, typenames[0], typenames[1],
getObjectDescription(&famaddr))));
}
else
{
address.objectId = HeapTupleGetOid(tp);
ReleaseSysCache(tp);
}
}
break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
}
return address;
}
/* /*
* Find the ObjectAddress for a user mapping. * Find the ObjectAddress for a user mapping.
*/ */
...@@ -1673,7 +1787,9 @@ pg_get_object_address(PG_FUNCTION_ARGS) ...@@ -1673,7 +1787,9 @@ pg_get_object_address(PG_FUNCTION_ARGS)
if (type == OBJECT_AGGREGATE || if (type == OBJECT_AGGREGATE ||
type == OBJECT_FUNCTION || type == OBJECT_FUNCTION ||
type == OBJECT_OPERATOR || type == OBJECT_OPERATOR ||
type == OBJECT_CAST) type == OBJECT_CAST ||
type == OBJECT_AMOP ||
type == OBJECT_AMPROC)
{ {
/* in these cases, the args list must be of TypeName */ /* in these cases, the args list must be of TypeName */
Datum *elems; Datum *elems;
...@@ -1708,8 +1824,6 @@ pg_get_object_address(PG_FUNCTION_ARGS) ...@@ -1708,8 +1824,6 @@ pg_get_object_address(PG_FUNCTION_ARGS)
switch (type) switch (type)
{ {
case OBJECT_DOMCONSTRAINT: case OBJECT_DOMCONSTRAINT:
case OBJECT_OPCLASS:
case OBJECT_OPFAMILY:
case OBJECT_CAST: case OBJECT_CAST:
case OBJECT_USER_MAPPING: case OBJECT_USER_MAPPING:
case OBJECT_DEFACL: case OBJECT_DEFACL:
...@@ -1718,6 +1832,20 @@ pg_get_object_address(PG_FUNCTION_ARGS) ...@@ -1718,6 +1832,20 @@ pg_get_object_address(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("argument list length must be exactly %d", 1))); errmsg("argument list length must be exactly %d", 1)));
break; break;
case OBJECT_OPFAMILY:
case OBJECT_OPCLASS:
if (list_length(name) < 2)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("name list length must be at least %d", 2)));
break;
case OBJECT_AMOP:
case OBJECT_AMPROC:
if (list_length(name) < 3)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("name list length must be at least %d", 3)));
/* fall through to check args length */
case OBJECT_OPERATOR: case OBJECT_OPERATOR:
if (list_length(args) != 2) if (list_length(args) != 2)
ereport(ERROR, ereport(ERROR,
...@@ -3730,24 +3858,22 @@ getObjectIdentityParts(const ObjectAddress *object, ...@@ -3730,24 +3858,22 @@ getObjectIdentityParts(const ObjectAddress *object,
opcForm->opcmethod); opcForm->opcmethod);
amForm = (Form_pg_am) GETSTRUCT(amTup); amForm = (Form_pg_am) GETSTRUCT(amTup);
appendStringInfoString(&buffer, appendStringInfo(&buffer, "%s USING %s",
quote_qualified_identifier(schema, quote_qualified_identifier(schema,
NameStr(opcForm->opcname))); NameStr(opcForm->opcname)),
appendStringInfo(&buffer, " USING %s",
quote_identifier(NameStr(amForm->amname))); quote_identifier(NameStr(amForm->amname)));
if (objname) if (objname)
{ *objname = list_make3(pstrdup(NameStr(amForm->amname)),
*objname = list_make2(pstrdup(schema), schema,
pstrdup(NameStr(opcForm->opcname))); pstrdup(NameStr(opcForm->opcname)));
*objargs = list_make1(pstrdup(NameStr(amForm->amname)));
}
ReleaseSysCache(amTup); ReleaseSysCache(amTup);
ReleaseSysCache(opcTup); ReleaseSysCache(opcTup);
break; break;
} }
case OCLASS_OPFAMILY: case OCLASS_OPFAMILY:
getOpFamilyIdentity(&buffer, object->objectId, objname, objargs); getOpFamilyIdentity(&buffer, object->objectId, objname);
break; break;
case OCLASS_AMOP: case OCLASS_AMOP:
...@@ -3758,10 +3884,8 @@ getObjectIdentityParts(const ObjectAddress *object, ...@@ -3758,10 +3884,8 @@ getObjectIdentityParts(const ObjectAddress *object,
SysScanDesc amscan; SysScanDesc amscan;
Form_pg_amop amopForm; Form_pg_amop amopForm;
StringInfoData opfam; StringInfoData opfam;
char *ltype;
/* no objname support here */ char *rtype;
if (objname)
*objname = NIL;
amopDesc = heap_open(AccessMethodOperatorRelationId, amopDesc = heap_open(AccessMethodOperatorRelationId,
AccessShareLock); AccessShareLock);
...@@ -3783,13 +3907,21 @@ getObjectIdentityParts(const ObjectAddress *object, ...@@ -3783,13 +3907,21 @@ getObjectIdentityParts(const ObjectAddress *object,
amopForm = (Form_pg_amop) GETSTRUCT(tup); amopForm = (Form_pg_amop) GETSTRUCT(tup);
initStringInfo(&opfam); initStringInfo(&opfam);
getOpFamilyIdentity(&opfam, amopForm->amopfamily, NULL, NULL); getOpFamilyIdentity(&opfam, amopForm->amopfamily, objname);
ltype = format_type_be_qualified(amopForm->amoplefttype);
rtype = format_type_be_qualified(amopForm->amoprighttype);
if (objname)
{
*objname = lappend(*objname,
psprintf("%d", amopForm->amopstrategy));
*objargs = list_make2(ltype, rtype);
}
appendStringInfo(&buffer, "operator %d (%s, %s) of %s", appendStringInfo(&buffer, "operator %d (%s, %s) of %s",
amopForm->amopstrategy, amopForm->amopstrategy,
format_type_be_qualified(amopForm->amoplefttype), ltype, rtype, opfam.data);
format_type_be_qualified(amopForm->amoprighttype),
opfam.data);
pfree(opfam.data); pfree(opfam.data);
...@@ -3806,10 +3938,8 @@ getObjectIdentityParts(const ObjectAddress *object, ...@@ -3806,10 +3938,8 @@ getObjectIdentityParts(const ObjectAddress *object,
HeapTuple tup; HeapTuple tup;
Form_pg_amproc amprocForm; Form_pg_amproc amprocForm;
StringInfoData opfam; StringInfoData opfam;
char *ltype;
/* no objname support here */ char *rtype;
if (objname)
*objname = NIL;
amprocDesc = heap_open(AccessMethodProcedureRelationId, amprocDesc = heap_open(AccessMethodProcedureRelationId,
AccessShareLock); AccessShareLock);
...@@ -3831,13 +3961,21 @@ getObjectIdentityParts(const ObjectAddress *object, ...@@ -3831,13 +3961,21 @@ getObjectIdentityParts(const ObjectAddress *object,
amprocForm = (Form_pg_amproc) GETSTRUCT(tup); amprocForm = (Form_pg_amproc) GETSTRUCT(tup);
initStringInfo(&opfam); initStringInfo(&opfam);
getOpFamilyIdentity(&opfam, amprocForm->amprocfamily, NULL, NULL); getOpFamilyIdentity(&opfam, amprocForm->amprocfamily, objname);
ltype = format_type_be_qualified(amprocForm->amproclefttype);
rtype = format_type_be_qualified(amprocForm->amprocrighttype);
if (objname)
{
*objname = lappend(*objname,
psprintf("%d", amprocForm->amprocnum));
*objargs = list_make2(ltype, rtype);
}
appendStringInfo(&buffer, "function %d (%s, %s) of %s", appendStringInfo(&buffer, "function %d (%s, %s) of %s",
amprocForm->amprocnum, amprocForm->amprocnum,
format_type_be_qualified(amprocForm->amproclefttype), ltype, rtype, opfam.data);
format_type_be_qualified(amprocForm->amprocrighttype),
opfam.data);
pfree(opfam.data); pfree(opfam.data);
...@@ -4263,7 +4401,7 @@ getObjectIdentityParts(const ObjectAddress *object, ...@@ -4263,7 +4401,7 @@ getObjectIdentityParts(const ObjectAddress *object,
} }
static void static void
getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **objname, List **objargs) getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **objname)
{ {
HeapTuple opfTup; HeapTuple opfTup;
Form_pg_opfamily opfForm; Form_pg_opfamily opfForm;
...@@ -4289,11 +4427,9 @@ getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **objname, List **objargs ...@@ -4289,11 +4427,9 @@ getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **objname, List **objargs
NameStr(amForm->amname)); NameStr(amForm->amname));
if (objname) if (objname)
{ *objname = list_make3(pstrdup(NameStr(amForm->amname)),
*objname = list_make2(pstrdup(schema), pstrdup(schema),
pstrdup(NameStr(opfForm->opfname))); pstrdup(NameStr(opfForm->opfname)));
*objargs = list_make1(pstrdup(NameStr(amForm->amname)));
}
ReleaseSysCache(amTup); ReleaseSysCache(amTup);
ReleaseSysCache(opfTup); ReleaseSysCache(opfTup);
......
...@@ -406,19 +406,27 @@ does_not_exist_skipping(ObjectType objtype, List *objname, List *objargs) ...@@ -406,19 +406,27 @@ does_not_exist_skipping(ObjectType objtype, List *objname, List *objargs)
name = NameListToString(objname); name = NameListToString(objname);
break; break;
case OBJECT_OPCLASS: case OBJECT_OPCLASS:
if (!schema_does_not_exist_skipping(objname, &msg, &name))
{ {
msg = gettext_noop("operator class \"%s\" does not exist for access method \"%s\", skipping"); List *opcname = list_copy_tail(objname, 1);
name = NameListToString(objname);
args = strVal(linitial(objargs)); if (!schema_does_not_exist_skipping(opcname, &msg, &name))
{
msg = gettext_noop("operator class \"%s\" does not exist for access method \"%s\", skipping");
name = NameListToString(opcname);
args = strVal(linitial(objname));
}
} }
break; break;
case OBJECT_OPFAMILY: case OBJECT_OPFAMILY:
if (!schema_does_not_exist_skipping(objname, &msg, &name))
{ {
msg = gettext_noop("operator family \"%s\" does not exist for access method \"%s\", skipping"); List *opfname = list_copy_tail(objname, 1);
name = NameListToString(objname);
args = strVal(linitial(objargs)); if (!schema_does_not_exist_skipping(opfname, &msg, &name))
{
msg = gettext_noop("operator family \"%s\" does not exist for access method \"%s\", skipping");
name = NameListToString(opfname);
args = strVal(linitial(objname));
}
} }
break; break;
default: default:
......
...@@ -1060,6 +1060,8 @@ EventTriggerSupportsObjectType(ObjectType obtype) ...@@ -1060,6 +1060,8 @@ EventTriggerSupportsObjectType(ObjectType obtype)
/* no support for event triggers on event triggers */ /* no support for event triggers on event triggers */
return false; return false;
case OBJECT_AGGREGATE: case OBJECT_AGGREGATE:
case OBJECT_AMOP:
case OBJECT_AMPROC:
case OBJECT_ATTRIBUTE: case OBJECT_ATTRIBUTE:
case OBJECT_CAST: case OBJECT_CAST:
case OBJECT_COLUMN: case OBJECT_COLUMN:
......
...@@ -3950,8 +3950,7 @@ AlterExtensionContentsStmt: ...@@ -3950,8 +3950,7 @@ AlterExtensionContentsStmt:
n->extname = $3; n->extname = $3;
n->action = $4; n->action = $4;
n->objtype = OBJECT_OPCLASS; n->objtype = OBJECT_OPCLASS;
n->objname = $7; n->objname = lcons(makeString($9), $7);
n->objargs = list_make1(makeString($9));
$$ = (Node *)n; $$ = (Node *)n;
} }
| ALTER EXTENSION name add_drop OPERATOR FAMILY any_name USING access_method | ALTER EXTENSION name add_drop OPERATOR FAMILY any_name USING access_method
...@@ -3960,8 +3959,7 @@ AlterExtensionContentsStmt: ...@@ -3960,8 +3959,7 @@ AlterExtensionContentsStmt:
n->extname = $3; n->extname = $3;
n->action = $4; n->action = $4;
n->objtype = OBJECT_OPFAMILY; n->objtype = OBJECT_OPFAMILY;
n->objname = $7; n->objname = lcons(makeString($9), $7);
n->objargs = list_make1(makeString($9));
$$ = (Node *)n; $$ = (Node *)n;
} }
| ALTER EXTENSION name add_drop SCHEMA name | ALTER EXTENSION name add_drop SCHEMA name
...@@ -5362,8 +5360,7 @@ DropOpClassStmt: ...@@ -5362,8 +5360,7 @@ DropOpClassStmt:
DROP OPERATOR CLASS any_name USING access_method opt_drop_behavior DROP OPERATOR CLASS any_name USING access_method opt_drop_behavior
{ {
DropStmt *n = makeNode(DropStmt); DropStmt *n = makeNode(DropStmt);
n->objects = list_make1($4); n->objects = list_make1(lcons(makeString($6), $4));
n->arguments = list_make1(list_make1(makeString($6)));
n->removeType = OBJECT_OPCLASS; n->removeType = OBJECT_OPCLASS;
n->behavior = $7; n->behavior = $7;
n->missing_ok = false; n->missing_ok = false;
...@@ -5373,8 +5370,7 @@ DropOpClassStmt: ...@@ -5373,8 +5370,7 @@ DropOpClassStmt:
| DROP OPERATOR CLASS IF_P EXISTS any_name USING access_method opt_drop_behavior | DROP OPERATOR CLASS IF_P EXISTS any_name USING access_method opt_drop_behavior
{ {
DropStmt *n = makeNode(DropStmt); DropStmt *n = makeNode(DropStmt);
n->objects = list_make1($6); n->objects = list_make1(lcons(makeString($8), $6));
n->arguments = list_make1(list_make1(makeString($8)));
n->removeType = OBJECT_OPCLASS; n->removeType = OBJECT_OPCLASS;
n->behavior = $9; n->behavior = $9;
n->missing_ok = true; n->missing_ok = true;
...@@ -5387,8 +5383,7 @@ DropOpFamilyStmt: ...@@ -5387,8 +5383,7 @@ DropOpFamilyStmt:
DROP OPERATOR FAMILY any_name USING access_method opt_drop_behavior DROP OPERATOR FAMILY any_name USING access_method opt_drop_behavior
{ {
DropStmt *n = makeNode(DropStmt); DropStmt *n = makeNode(DropStmt);
n->objects = list_make1($4); n->objects = list_make1(lcons(makeString($6), $4));
n->arguments = list_make1(list_make1(makeString($6)));
n->removeType = OBJECT_OPFAMILY; n->removeType = OBJECT_OPFAMILY;
n->behavior = $7; n->behavior = $7;
n->missing_ok = false; n->missing_ok = false;
...@@ -5398,8 +5393,7 @@ DropOpFamilyStmt: ...@@ -5398,8 +5393,7 @@ DropOpFamilyStmt:
| DROP OPERATOR FAMILY IF_P EXISTS any_name USING access_method opt_drop_behavior | DROP OPERATOR FAMILY IF_P EXISTS any_name USING access_method opt_drop_behavior
{ {
DropStmt *n = makeNode(DropStmt); DropStmt *n = makeNode(DropStmt);
n->objects = list_make1($6); n->objects = list_make1(lcons(makeString($8), $6));
n->arguments = list_make1(list_make1(makeString($8)));
n->removeType = OBJECT_OPFAMILY; n->removeType = OBJECT_OPFAMILY;
n->behavior = $9; n->behavior = $9;
n->missing_ok = true; n->missing_ok = true;
...@@ -5741,8 +5735,7 @@ CommentStmt: ...@@ -5741,8 +5735,7 @@ CommentStmt:
{ {
CommentStmt *n = makeNode(CommentStmt); CommentStmt *n = makeNode(CommentStmt);
n->objtype = OBJECT_OPCLASS; n->objtype = OBJECT_OPCLASS;
n->objname = $5; n->objname = lcons(makeString($7), $5);
n->objargs = list_make1(makeString($7));
n->comment = $9; n->comment = $9;
$$ = (Node *) n; $$ = (Node *) n;
} }
...@@ -5750,8 +5743,8 @@ CommentStmt: ...@@ -5750,8 +5743,8 @@ CommentStmt:
{ {
CommentStmt *n = makeNode(CommentStmt); CommentStmt *n = makeNode(CommentStmt);
n->objtype = OBJECT_OPFAMILY; n->objtype = OBJECT_OPFAMILY;
n->objname = $5; n->objname = lcons(makeString($7), $5);
n->objargs = list_make1(makeString($7)); n->objargs = NIL;
n->comment = $9; n->comment = $9;
$$ = (Node *) n; $$ = (Node *) n;
} }
...@@ -7476,8 +7469,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name ...@@ -7476,8 +7469,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
{ {
RenameStmt *n = makeNode(RenameStmt); RenameStmt *n = makeNode(RenameStmt);
n->renameType = OBJECT_OPCLASS; n->renameType = OBJECT_OPCLASS;
n->object = $4; n->object = lcons(makeString($6), $4);
n->objarg = list_make1(makeString($6));
n->newname = $9; n->newname = $9;
n->missing_ok = false; n->missing_ok = false;
$$ = (Node *)n; $$ = (Node *)n;
...@@ -7486,8 +7478,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name ...@@ -7486,8 +7478,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
{ {
RenameStmt *n = makeNode(RenameStmt); RenameStmt *n = makeNode(RenameStmt);
n->renameType = OBJECT_OPFAMILY; n->renameType = OBJECT_OPFAMILY;
n->object = $4; n->object = lcons(makeString($6), $4);
n->objarg = list_make1(makeString($6));
n->newname = $9; n->newname = $9;
n->missing_ok = false; n->missing_ok = false;
$$ = (Node *)n; $$ = (Node *)n;
...@@ -7924,8 +7915,7 @@ AlterObjectSchemaStmt: ...@@ -7924,8 +7915,7 @@ AlterObjectSchemaStmt:
{ {
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
n->objectType = OBJECT_OPCLASS; n->objectType = OBJECT_OPCLASS;
n->object = $4; n->object = lcons(makeString($6), $4);
n->objarg = list_make1(makeString($6));
n->newschema = $9; n->newschema = $9;
n->missing_ok = false; n->missing_ok = false;
$$ = (Node *)n; $$ = (Node *)n;
...@@ -7934,8 +7924,7 @@ AlterObjectSchemaStmt: ...@@ -7934,8 +7924,7 @@ AlterObjectSchemaStmt:
{ {
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
n->objectType = OBJECT_OPFAMILY; n->objectType = OBJECT_OPFAMILY;
n->object = $4; n->object = lcons(makeString($6), $4);
n->objarg = list_make1(makeString($6));
n->newschema = $9; n->newschema = $9;
n->missing_ok = false; n->missing_ok = false;
$$ = (Node *)n; $$ = (Node *)n;
...@@ -8162,8 +8151,7 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleSpec ...@@ -8162,8 +8151,7 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleSpec
{ {
AlterOwnerStmt *n = makeNode(AlterOwnerStmt); AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
n->objectType = OBJECT_OPCLASS; n->objectType = OBJECT_OPCLASS;
n->object = $4; n->object = lcons(makeString($6), $4);
n->objarg = list_make1(makeString($6));
n->newowner = $9; n->newowner = $9;
$$ = (Node *)n; $$ = (Node *)n;
} }
...@@ -8171,8 +8159,7 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleSpec ...@@ -8171,8 +8159,7 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleSpec
{ {
AlterOwnerStmt *n = makeNode(AlterOwnerStmt); AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
n->objectType = OBJECT_OPFAMILY; n->objectType = OBJECT_OPFAMILY;
n->object = $4; n->object = lcons(makeString($6), $4);
n->objarg = list_make1(makeString($6));
n->newowner = $9; n->newowner = $9;
$$ = (Node *)n; $$ = (Node *)n;
} }
......
...@@ -1223,6 +1223,8 @@ typedef struct SetOperationStmt ...@@ -1223,6 +1223,8 @@ typedef struct SetOperationStmt
typedef enum ObjectType typedef enum ObjectType
{ {
OBJECT_AGGREGATE, OBJECT_AGGREGATE,
OBJECT_AMOP,
OBJECT_AMPROC,
OBJECT_ATTRIBUTE, /* type's attribute, when distinct from column */ OBJECT_ATTRIBUTE, /* type's attribute, when distinct from column */
OBJECT_CAST, OBJECT_CAST,
OBJECT_COLUMN, OBJECT_COLUMN,
......
...@@ -45,8 +45,7 @@ DECLARE ...@@ -45,8 +45,7 @@ DECLARE
objtype text; objtype text;
BEGIN BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'), FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'), ('toast table column'), ('view column'), ('materialized view column')
('operator of access method'), ('function of access method')
LOOP LOOP
BEGIN BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}'); PERFORM pg_get_object_address(objtype, '{one}', '{}');
...@@ -62,8 +61,6 @@ WARNING: error for sequence column: unsupported object type "sequence column" ...@@ -62,8 +61,6 @@ WARNING: error for sequence column: unsupported object type "sequence column"
WARNING: error for toast table column: unsupported object type "toast table column" WARNING: error for toast table column: unsupported object type "toast table column"
WARNING: error for view column: unsupported object type "view column" WARNING: error for view column: unsupported object type "view column"
WARNING: error for materialized view column: unsupported object type "materialized view column" WARNING: error for materialized view column: unsupported object type "materialized view column"
WARNING: error for operator of access method: unsupported object type "operator of access method"
WARNING: error for function of access method: unsupported object type "function of access method"
DO $$ DO $$
DECLARE DECLARE
objtype text; objtype text;
...@@ -79,7 +76,8 @@ BEGIN ...@@ -79,7 +76,8 @@ BEGIN
('operator'), ('operator class'), ('operator family'), ('rule'), ('trigger'), ('operator'), ('operator class'), ('operator family'), ('rule'), ('trigger'),
('text search parser'), ('text search dictionary'), ('text search parser'), ('text search dictionary'),
('text search template'), ('text search configuration'), ('text search template'), ('text search configuration'),
('policy'), ('user mapping'), ('default acl') ('policy'), ('user mapping'), ('default acl'),
('operator of access method'), ('function of access method')
LOOP LOOP
FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}') FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}')
LOOP LOOP
...@@ -197,18 +195,18 @@ WARNING: error for operator,{addr_nsp,zwei},{}: argument list length must be ex ...@@ -197,18 +195,18 @@ WARNING: error for operator,{addr_nsp,zwei},{}: argument list length must be ex
WARNING: error for operator,{addr_nsp,zwei},{integer}: argument list length must be exactly 2 WARNING: error for operator,{addr_nsp,zwei},{integer}: argument list length must be exactly 2
WARNING: error for operator,{eins,zwei,drei},{}: argument list length must be exactly 2 WARNING: error for operator,{eins,zwei,drei},{}: argument list length must be exactly 2
WARNING: error for operator,{eins,zwei,drei},{integer}: argument list length must be exactly 2 WARNING: error for operator,{eins,zwei,drei},{integer}: argument list length must be exactly 2
WARNING: error for operator class,{eins},{}: argument list length must be exactly 1 WARNING: error for operator class,{eins},{}: name list length must be at least 2
WARNING: error for operator class,{eins},{integer}: access method "integer" does not exist WARNING: error for operator class,{eins},{integer}: name list length must be at least 2
WARNING: error for operator class,{addr_nsp,zwei},{}: argument list length must be exactly 1 WARNING: error for operator class,{addr_nsp,zwei},{}: access method "addr_nsp" does not exist
WARNING: error for operator class,{addr_nsp,zwei},{integer}: access method "integer" does not exist WARNING: error for operator class,{addr_nsp,zwei},{integer}: access method "addr_nsp" does not exist
WARNING: error for operator class,{eins,zwei,drei},{}: argument list length must be exactly 1 WARNING: error for operator class,{eins,zwei,drei},{}: access method "eins" does not exist
WARNING: error for operator class,{eins,zwei,drei},{integer}: access method "integer" does not exist WARNING: error for operator class,{eins,zwei,drei},{integer}: access method "eins" does not exist
WARNING: error for operator family,{eins},{}: argument list length must be exactly 1 WARNING: error for operator family,{eins},{}: name list length must be at least 2
WARNING: error for operator family,{eins},{integer}: access method "integer" does not exist WARNING: error for operator family,{eins},{integer}: name list length must be at least 2
WARNING: error for operator family,{addr_nsp,zwei},{}: argument list length must be exactly 1 WARNING: error for operator family,{addr_nsp,zwei},{}: access method "addr_nsp" does not exist
WARNING: error for operator family,{addr_nsp,zwei},{integer}: access method "integer" does not exist WARNING: error for operator family,{addr_nsp,zwei},{integer}: access method "addr_nsp" does not exist
WARNING: error for operator family,{eins,zwei,drei},{}: argument list length must be exactly 1 WARNING: error for operator family,{eins,zwei,drei},{}: access method "eins" does not exist
WARNING: error for operator family,{eins,zwei,drei},{integer}: access method "integer" does not exist WARNING: error for operator family,{eins,zwei,drei},{integer}: access method "eins" does not exist
WARNING: error for rule,{eins},{}: rule "eins" does not exist WARNING: error for rule,{eins},{}: rule "eins" does not exist
WARNING: error for rule,{eins},{integer}: rule "eins" does not exist WARNING: error for rule,{eins},{integer}: rule "eins" does not exist
WARNING: error for rule,{addr_nsp,zwei},{}: relation "addr_nsp" does not exist WARNING: error for rule,{addr_nsp,zwei},{}: relation "addr_nsp" does not exist
...@@ -263,6 +261,18 @@ WARNING: error for default acl,{addr_nsp,zwei},{}: argument list length must be ...@@ -263,6 +261,18 @@ WARNING: error for default acl,{addr_nsp,zwei},{}: argument list length must be
WARNING: error for default acl,{addr_nsp,zwei},{integer}: unrecognized default ACL object type i WARNING: error for default acl,{addr_nsp,zwei},{integer}: unrecognized default ACL object type i
WARNING: error for default acl,{eins,zwei,drei},{}: argument list length must be exactly 1 WARNING: error for default acl,{eins,zwei,drei},{}: argument list length must be exactly 1
WARNING: error for default acl,{eins,zwei,drei},{integer}: unrecognized default ACL object type i WARNING: error for default acl,{eins,zwei,drei},{integer}: unrecognized default ACL object type i
WARNING: error for operator of access method,{eins},{}: name list length must be at least 3
WARNING: error for operator of access method,{eins},{integer}: name list length must be at least 3
WARNING: error for operator of access method,{addr_nsp,zwei},{}: name list length must be at least 3
WARNING: error for operator of access method,{addr_nsp,zwei},{integer}: name list length must be at least 3
WARNING: error for operator of access method,{eins,zwei,drei},{}: argument list length must be exactly 2
WARNING: error for operator of access method,{eins,zwei,drei},{integer}: argument list length must be exactly 2
WARNING: error for function of access method,{eins},{}: name list length must be at least 3
WARNING: error for function of access method,{eins},{integer}: name list length must be at least 3
WARNING: error for function of access method,{addr_nsp,zwei},{}: name list length must be at least 3
WARNING: error for function of access method,{addr_nsp,zwei},{integer}: name list length must be at least 3
WARNING: error for function of access method,{eins,zwei,drei},{}: argument list length must be exactly 2
WARNING: error for function of access method,{eins,zwei,drei},{integer}: argument list length must be exactly 2
-- these object types cannot be qualified names -- these object types cannot be qualified names
SELECT pg_get_object_address('language', '{one}', '{}'); SELECT pg_get_object_address('language', '{one}', '{}');
ERROR: language "one" does not exist ERROR: language "one" does not exist
...@@ -332,10 +342,10 @@ WITH objects (type, name, args) AS (VALUES ...@@ -332,10 +342,10 @@ WITH objects (type, name, args) AS (VALUES
('language', '{plpgsql}', '{}'), ('language', '{plpgsql}', '{}'),
-- large object -- large object
('operator', '{+}', '{int4, int4}'), ('operator', '{+}', '{int4, int4}'),
('operator class', '{int4_ops}', '{btree}'), ('operator class', '{btree, int4_ops}', '{}'),
('operator family', '{integer_ops}', '{btree}'), ('operator family', '{btree, integer_ops}', '{}'),
-- operator of access method ('operator of access method', '{btree,integer_ops,1}', '{integer,integer}'),
-- function of access method ('function of access method', '{btree,integer_ops,2}', '{integer,integer}'),
('rule', '{addr_nsp, genview, _RETURN}', '{}'), ('rule', '{addr_nsp, genview, _RETURN}', '{}'),
('trigger', '{addr_nsp, gentable, t}', '{}'), ('trigger', '{addr_nsp, gentable, t}', '{}'),
('schema', '{addr_nsp}', '{}'), ('schema', '{addr_nsp}', '{}'),
...@@ -362,7 +372,7 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*, ...@@ -362,7 +372,7 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
FROM objects, pg_get_object_address(type, name, args) addr1, FROM objects, pg_get_object_address(type, name, args) addr1,
pg_identify_object_as_address(classid, objid, subobjid) ioa(typ,nms,args), pg_identify_object_as_address(classid, objid, subobjid) ioa(typ,nms,args),
pg_get_object_address(typ, nms, ioa.args) as addr2 pg_get_object_address(typ, nms, ioa.args) as addr2
ORDER BY addr1.classid, addr1.objid; ORDER BY addr1.classid, addr1.objid, addr1.subobjid;
type | schema | name | identity | ?column? type | schema | name | identity | ?column?
---------------------------+------------+-------------------+----------------------------------------------------------------------+---------- ---------------------------+------------+-------------------+----------------------------------------------------------------------+----------
default acl | | | for role regtest_addr_user in schema public on tables | t default acl | | | for role regtest_addr_user in schema public on tables | t
...@@ -379,12 +389,14 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*, ...@@ -379,12 +389,14 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
index | addr_nsp | gentable_pkey | addr_nsp.gentable_pkey | t index | addr_nsp | gentable_pkey | addr_nsp.gentable_pkey | t
view | addr_nsp | genview | addr_nsp.genview | t view | addr_nsp | genview | addr_nsp.genview | t
materialized view | addr_nsp | genmatview | addr_nsp.genmatview | 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 foreign table | addr_nsp | genftable | addr_nsp.genftable | t
foreign table column | addr_nsp | genftable | addr_nsp.genftable.a | t
role | | regtest_addr_user | regtest_addr_user | t role | | regtest_addr_user | regtest_addr_user | t
server | | addr_fserv | addr_fserv | t server | | addr_fserv | addr_fserv | t
user mapping | | | regtest_addr_user on server integer | t user mapping | | | regtest_addr_user on server integer | t
foreign-data wrapper | | addr_fdw | addr_fdw | t foreign-data wrapper | | addr_fdw | addr_fdw | t
operator of access method | | | operator 1 (integer, integer) of pg_catalog.integer_ops USING btree | t
function of access method | | | function 2 (integer, integer) of pg_catalog.integer_ops USING btree | t
default value | | | for addr_nsp.gentable.b | t default value | | | for addr_nsp.gentable.b | t
cast | | | (bigint AS integer) | t cast | | | (bigint AS integer) | t
table constraint | addr_nsp | | a_chk on addr_nsp.gentable | t table constraint | addr_nsp | | a_chk on addr_nsp.gentable | t
...@@ -403,7 +415,7 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*, ...@@ -403,7 +415,7 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
text search parser | addr_nsp | addr_ts_prs | addr_nsp.addr_ts_prs | 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 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 text search template | addr_nsp | addr_ts_temp | addr_nsp.addr_ts_temp | t
(38 rows) (40 rows)
--- ---
--- Cleanup resources --- Cleanup resources
......
...@@ -48,8 +48,7 @@ DECLARE ...@@ -48,8 +48,7 @@ DECLARE
objtype text; objtype text;
BEGIN BEGIN
FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'), FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
('toast table column'), ('view column'), ('materialized view column'), ('toast table column'), ('view column'), ('materialized view column')
('operator of access method'), ('function of access method')
LOOP LOOP
BEGIN BEGIN
PERFORM pg_get_object_address(objtype, '{one}', '{}'); PERFORM pg_get_object_address(objtype, '{one}', '{}');
...@@ -75,7 +74,8 @@ BEGIN ...@@ -75,7 +74,8 @@ BEGIN
('operator'), ('operator class'), ('operator family'), ('rule'), ('trigger'), ('operator'), ('operator class'), ('operator family'), ('rule'), ('trigger'),
('text search parser'), ('text search dictionary'), ('text search parser'), ('text search dictionary'),
('text search template'), ('text search configuration'), ('text search template'), ('text search configuration'),
('policy'), ('user mapping'), ('default acl') ('policy'), ('user mapping'), ('default acl'),
('operator of access method'), ('function of access method')
LOOP LOOP
FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}') FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}')
LOOP LOOP
...@@ -141,10 +141,10 @@ WITH objects (type, name, args) AS (VALUES ...@@ -141,10 +141,10 @@ WITH objects (type, name, args) AS (VALUES
('language', '{plpgsql}', '{}'), ('language', '{plpgsql}', '{}'),
-- large object -- large object
('operator', '{+}', '{int4, int4}'), ('operator', '{+}', '{int4, int4}'),
('operator class', '{int4_ops}', '{btree}'), ('operator class', '{btree, int4_ops}', '{}'),
('operator family', '{integer_ops}', '{btree}'), ('operator family', '{btree, integer_ops}', '{}'),
-- operator of access method ('operator of access method', '{btree,integer_ops,1}', '{integer,integer}'),
-- function of access method ('function of access method', '{btree,integer_ops,2}', '{integer,integer}'),
('rule', '{addr_nsp, genview, _RETURN}', '{}'), ('rule', '{addr_nsp, genview, _RETURN}', '{}'),
('trigger', '{addr_nsp, gentable, t}', '{}'), ('trigger', '{addr_nsp, gentable, t}', '{}'),
('schema', '{addr_nsp}', '{}'), ('schema', '{addr_nsp}', '{}'),
...@@ -171,7 +171,7 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*, ...@@ -171,7 +171,7 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
FROM objects, pg_get_object_address(type, name, args) addr1, FROM objects, pg_get_object_address(type, name, args) addr1,
pg_identify_object_as_address(classid, objid, subobjid) ioa(typ,nms,args), pg_identify_object_as_address(classid, objid, subobjid) ioa(typ,nms,args),
pg_get_object_address(typ, nms, ioa.args) as addr2 pg_get_object_address(typ, nms, ioa.args) as addr2
ORDER BY addr1.classid, addr1.objid; ORDER BY addr1.classid, addr1.objid, addr1.subobjid;
--- ---
--- Cleanup resources --- Cleanup resources
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment