get_object_address support for additional object types

Started by Alvaro Herreraalmost 11 years ago16 messages
#1Alvaro Herrera
alvherre@2ndquadrant.com
3 attachment(s)

This is extracted from the DDL deparse series. These patches add
get_object_address support for the following object types:

- user mappings
- default ACLs
- operators and functions of operator families

In each case I had to create a new value in ObjectType. These object
types can not be created from the parser, which is why they don't exist
yet. But if we want to be able to process DDL for them, then we need to
cope at this level. This is the kind of fix we need to handle the
failures related to commit a486841eb11517e.

There is one strange thing in the last one, which is that an opfamily
member is represented in two arrays like this (objname, objargs):
{opfamily identity, access method identity, number} , {left type, right type}

This is a bit odd considering that operator families themselves are
addressed like this instead:
{opfamily identity} , {access method identity}

Note that the AM name is originally in objargs and moves to objnames.
The reason I did it this way is that the objargs elements can be
processed completely as an array of TypeName, and therefore there's no
need for an extra strange case in pg_get_object_address. But it does
mean that there is some code that knows to search the strategy or
function number in a specific position in the objname array.

If we had more freedom on general object representation I'm sure we
could do better, but it's what we have. I don't think it's a tremendous
problem, considering that get_object_address gets a fairly ad-hoc
representation for each object type anyway, as each gets constructed by
the grammar.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

0001-deparse-core-get_object_address-support-user-mapping.patchtext/x-diff; charset=us-asciiDownload
>From fad598488c0c15e0962808ad13825374e8a3640e Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Thu, 5 Mar 2015 12:04:39 -0300
Subject: [PATCH 1/3] deparse/core: get_object_address support user mappings

---
 src/backend/catalog/objectaddress.c          | 67 +++++++++++++++++++++++++++-
 src/backend/commands/event_trigger.c         |  1 +
 src/include/nodes/parsenodes.h               |  1 +
 src/test/regress/expected/event_trigger.out  | 12 ++++-
 src/test/regress/expected/object_address.out | 19 +++++---
 src/test/regress/sql/event_trigger.sql       | 11 ++++-
 src/test/regress/sql/object_address.sql      |  9 ++--
 7 files changed, 107 insertions(+), 13 deletions(-)

diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 541912b..5553ec7 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -520,7 +520,7 @@ ObjectTypeMap[] =
 	/* OCLASS_FOREIGN_SERVER */
 	{ "server", OBJECT_FOREIGN_SERVER },
 	/* OCLASS_USER_MAPPING */
-	{ "user mapping", -1 },		/* unmapped */
+	{ "user mapping", OBJECT_USER_MAPPING },
 	/* OCLASS_DEFACL */
 	{ "default acl", -1 },		/* unmapped */
 	/* OCLASS_EXTENSION */
@@ -555,6 +555,8 @@ static ObjectAddress get_object_address_type(ObjectType objtype,
 						List *objname, bool missing_ok);
 static ObjectAddress get_object_address_opcf(ObjectType objtype, List *objname,
 						List *objargs, bool missing_ok);
+static ObjectAddress get_object_address_usermapping(List *objname,
+							   List *objargs, bool missing_ok);
 static const ObjectPropertyType *get_object_property_data(Oid class_id);
 
 static void getRelationDescription(StringInfo buffer, Oid relid);
@@ -769,6 +771,10 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
 				address.objectId = get_ts_config_oid(objname, missing_ok);
 				address.objectSubId = 0;
 				break;
+			case OBJECT_USER_MAPPING:
+				address = get_object_address_usermapping(objname, objargs,
+														 missing_ok);
+				break;
 			default:
 				elog(ERROR, "unrecognized objtype: %d", (int) objtype);
 				/* placate compiler, in case it thinks elog might return */
@@ -1373,6 +1379,64 @@ get_object_address_opcf(ObjectType objtype,
 }
 
 /*
+ * Find the ObjectAddress for a user mapping.
+ */
+static ObjectAddress
+get_object_address_usermapping(List *objname, List *objargs, bool missing_ok)
+{
+	ObjectAddress address;
+	Oid			userid;
+	char	   *username;
+	char	   *servername;
+	ForeignServer *server;
+	HeapTuple	tp;
+
+	ObjectAddressSet(address, UserMappingRelationId, InvalidOid);
+
+	username = strVal(linitial(objname));
+	servername = strVal(linitial(objargs));
+	server = GetForeignServerByName(servername, false);
+
+	if (strcmp(username, "public") == 0)
+		userid = InvalidOid;
+	else
+	{
+		tp = SearchSysCache1(AUTHNAME,
+							 CStringGetDatum(username));
+		if (!HeapTupleIsValid(tp))
+		{
+			if (!missing_ok)
+				ereport(ERROR,
+						(errcode(ERRCODE_UNDEFINED_OBJECT),
+						 errmsg("user mapping for user \"%s\" in server \"%s\" does not exist",
+								username, servername)));
+			return address;
+		}
+		userid = HeapTupleGetOid(tp);
+		ReleaseSysCache(tp);
+	}
+
+	tp = SearchSysCache2(USERMAPPINGUSERSERVER,
+						 ObjectIdGetDatum(userid),
+						 ObjectIdGetDatum(server->serverid));
+	if (!HeapTupleIsValid(tp))
+	{
+		if (!missing_ok)
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_OBJECT),
+					 errmsg("user mapping for user \"%s\" in server \"%s\" does not exist",
+							username, servername)));
+		return address;
+	}
+
+	address.objectId = HeapTupleGetOid(tp);
+
+	ReleaseSysCache(tp);
+
+	return address;
+}
+
+/*
  * Convert an array of TEXT into a List of string Values, as emitted by the
  * parser, which is what get_object_address uses as input.
  */
@@ -1523,6 +1587,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
 		case OBJECT_OPCLASS:
 		case OBJECT_OPFAMILY:
 		case OBJECT_CAST:
+		case OBJECT_USER_MAPPING:
 			if (list_length(args) != 1)
 				ereport(ERROR,
 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index f573c9c..4e446bd 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1092,6 +1092,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
 		case OBJECT_TSPARSER:
 		case OBJECT_TSTEMPLATE:
 		case OBJECT_TYPE:
+		case OBJECT_USER_MAPPING:
 		case OBJECT_VIEW:
 			return true;
 	}
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ac13302..dbde840 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1248,6 +1248,7 @@ typedef enum ObjectType
 	OBJECT_TSPARSER,
 	OBJECT_TSTEMPLATE,
 	OBJECT_TYPE,
+	OBJECT_USER_MAPPING,
 	OBJECT_VIEW
 } ObjectType;
 
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index c330ad2..4ff7f35 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -113,6 +113,15 @@ NOTICE:  test_event_trigger: ddl_command_end REVOKE
 -- regress_event_trigger_end should fire here
 drop table event_trigger_fire1;
 NOTICE:  test_event_trigger: ddl_command_end DROP TABLE
+-- regress_event_trigger_end should fire here
+create foreign data wrapper useless;
+NOTICE:  test_event_trigger: ddl_command_end CREATE FOREIGN DATA WRAPPER
+-- regress_event_trigger_end should fire here
+create server useless_server foreign data wrapper useless;
+NOTICE:  test_event_trigger: ddl_command_end CREATE SERVER
+-- regress_event_trigger_end should fire here
+create user mapping for regression_bob server useless_server;
+NOTICE:  test_event_trigger: ddl_command_end CREATE USER MAPPING
 -- alter owner to non-superuser should fail
 alter event trigger regress_event_trigger owner to regression_bob;
 ERROR:  permission denied to change owner of event trigger "regress_event_trigger"
@@ -128,10 +137,11 @@ alter event trigger regress_event_trigger rename to regress_event_trigger3;
 -- should fail, doesn't exist any more
 drop event trigger regress_event_trigger;
 ERROR:  event trigger "regress_event_trigger" does not exist
--- should fail, regression_bob owns regress_event_trigger2/3
+-- should fail, regression_bob owns some objects
 drop role regression_bob;
 ERROR:  role "regression_bob" cannot be dropped because some objects depend on it
 DETAIL:  owner of event trigger regress_event_trigger3
+owner of user mapping for regression_bob on server useless_server
 -- cleanup before next test
 -- these are all OK; the second one should emit a NOTICE
 drop event trigger if exists regress_event_trigger2;
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index dcf1b46..e72abda 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -28,6 +28,8 @@ CREATE DOMAIN addr_nsp.gendomain AS int4 CONSTRAINT domconstr CHECK (value > 0);
 CREATE FUNCTION addr_nsp.trig() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN END; $$;
 CREATE TRIGGER t BEFORE INSERT ON addr_nsp.gentable FOR EACH ROW EXECUTE PROCEDURE addr_nsp.trig();
 CREATE POLICY genpol ON addr_nsp.gentable;
+CREATE SERVER "integer" FOREIGN DATA WRAPPER addr_fdw;
+CREATE USER MAPPING FOR regtest_addr_user SERVER "integer";
 -- test some error cases
 SELECT pg_get_object_address('stone', '{}', '{}');
 ERROR:  unrecognized object type "stone"
@@ -42,8 +44,7 @@ DECLARE
 BEGIN
 	FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
 		('toast table column'), ('view column'), ('materialized view column'),
-		('operator of access method'), ('function of access method'),
-		('user mapping')
+		('operator of access method'), ('function of access method')
 	LOOP
 		BEGIN
 			PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -61,7 +62,6 @@ 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 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"
-WARNING:  error for user mapping: unsupported object type "user mapping"
 DO $$
 DECLARE
 	objtype text;
@@ -77,7 +77,7 @@ BEGIN
 		('operator'), ('operator class'), ('operator family'), ('rule'), ('trigger'),
 		('text search parser'), ('text search dictionary'),
 		('text search template'), ('text search configuration'),
-		('policy')
+		('policy'), ('user mapping')
 	LOOP
 		FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}')
 		LOOP
@@ -249,6 +249,12 @@ WARNING:  error for policy,{addr_nsp,zwei},{}: relation "addr_nsp" does not exis
 WARNING:  error for policy,{addr_nsp,zwei},{integer}: relation "addr_nsp" does not exist
 WARNING:  error for policy,{eins,zwei,drei},{}: schema "eins" does not exist
 WARNING:  error for policy,{eins,zwei,drei},{integer}: schema "eins" does not exist
+WARNING:  error for user mapping,{eins},{}: argument list length must be exactly 1
+WARNING:  error for user mapping,{eins},{integer}: user mapping for user "eins" in server "integer" does not exist
+WARNING:  error for user mapping,{addr_nsp,zwei},{}: argument list length must be exactly 1
+WARNING:  error for user mapping,{addr_nsp,zwei},{integer}: user mapping for user "addr_nsp" in server "integer" does not exist
+WARNING:  error for user mapping,{eins,zwei,drei},{}: argument list length must be exactly 1
+WARNING:  error for user mapping,{eins,zwei,drei},{integer}: user mapping for user "eins" in server "integer" does not exist
 -- these object types cannot be qualified names
 SELECT pg_get_object_address('language', '{one}', '{}');
 ERROR:  language "one" does not exist
@@ -334,7 +340,7 @@ WITH objects (type, name, args) AS (VALUES
 				-- tablespace
 				('foreign-data wrapper', '{addr_fdw}', '{}'),
 				('server', '{addr_fserv}', '{}'),
-				-- user mapping
+				('user mapping', '{regtest_addr_user}', '{integer}'),
 				-- extension
 				-- event trigger
 				('policy', '{addr_nsp, gentable, genpol}', '{}')
@@ -365,6 +371,7 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
  foreign table             | addr_nsp   | genftable         | addr_nsp.genftable                                                   | t
  role                      |            | regtest_addr_user | regtest_addr_user                                                    | t
  server                    |            | addr_fserv        | addr_fserv                                                           | t
+ user mapping              |            |                   | regtest_addr_user on server integer                                  | t
  foreign-data wrapper      |            | addr_fdw          | addr_fdw                                                             | t
  default value             |            |                   | for addr_nsp.gentable.b                                              | t
  cast                      |            |                   | (bigint AS integer)                                                  | t
@@ -384,7 +391,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 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)
+(36 rows)
 
 ---
 --- Cleanup resources
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index fd4c332..f89fa71 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -114,6 +114,15 @@ revoke all on table event_trigger_fire1 from public;
 -- regress_event_trigger_end should fire here
 drop table event_trigger_fire1;
 
+-- regress_event_trigger_end should fire here
+create foreign data wrapper useless;
+
+-- regress_event_trigger_end should fire here
+create server useless_server foreign data wrapper useless;
+
+-- regress_event_trigger_end should fire here
+create user mapping for regression_bob server useless_server;
+
 -- alter owner to non-superuser should fail
 alter event trigger regress_event_trigger owner to regression_bob;
 
@@ -130,7 +139,7 @@ alter event trigger regress_event_trigger rename to regress_event_trigger3;
 -- should fail, doesn't exist any more
 drop event trigger regress_event_trigger;
 
--- should fail, regression_bob owns regress_event_trigger2/3
+-- should fail, regression_bob owns some objects
 drop role regression_bob;
 
 -- cleanup before next test
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 9fc27d8..b714b52 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -32,6 +32,8 @@ CREATE DOMAIN addr_nsp.gendomain AS int4 CONSTRAINT domconstr CHECK (value > 0);
 CREATE FUNCTION addr_nsp.trig() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN END; $$;
 CREATE TRIGGER t BEFORE INSERT ON addr_nsp.gentable FOR EACH ROW EXECUTE PROCEDURE addr_nsp.trig();
 CREATE POLICY genpol ON addr_nsp.gentable;
+CREATE SERVER "integer" FOREIGN DATA WRAPPER addr_fdw;
+CREATE USER MAPPING FOR regtest_addr_user SERVER "integer";
 
 -- test some error cases
 SELECT pg_get_object_address('stone', '{}', '{}');
@@ -45,8 +47,7 @@ DECLARE
 BEGIN
 	FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
 		('toast table column'), ('view column'), ('materialized view column'),
-		('operator of access method'), ('function of access method'),
-		('user mapping')
+		('operator of access method'), ('function of access method')
 	LOOP
 		BEGIN
 			PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -72,7 +73,7 @@ BEGIN
 		('operator'), ('operator class'), ('operator family'), ('rule'), ('trigger'),
 		('text search parser'), ('text search dictionary'),
 		('text search template'), ('text search configuration'),
-		('policy')
+		('policy'), ('user mapping')
 	LOOP
 		FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}')
 		LOOP
@@ -154,7 +155,7 @@ WITH objects (type, name, args) AS (VALUES
 				-- tablespace
 				('foreign-data wrapper', '{addr_fdw}', '{}'),
 				('server', '{addr_fserv}', '{}'),
-				-- user mapping
+				('user mapping', '{regtest_addr_user}', '{integer}'),
 				-- extension
 				-- event trigger
 				('policy', '{addr_nsp, gentable, genpol}', '{}')
-- 
2.1.4

0002-deparse-core-get_object_address-support-default-ACLs.patchtext/x-diff; charset=us-asciiDownload
>From 21c30e970b1d22482660dfcc154767f3b5c03ae4 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Fri, 6 Mar 2015 17:16:29 -0300
Subject: [PATCH 2/3] deparse/core: get_object_address support default ACLs

---
 src/backend/catalog/objectaddress.c          | 125 +++++++++++++++++++++++++--
 src/backend/commands/event_trigger.c         |   1 +
 src/include/nodes/parsenodes.h               |   1 +
 src/test/regress/expected/event_trigger.out  |   5 ++
 src/test/regress/expected/object_address.out |  17 +++-
 src/test/regress/sql/event_trigger.sql       |   4 +
 src/test/regress/sql/object_address.sql      |   7 +-
 7 files changed, 149 insertions(+), 11 deletions(-)

diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 5553ec7..32eea58 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -522,7 +522,7 @@ ObjectTypeMap[] =
 	/* OCLASS_USER_MAPPING */
 	{ "user mapping", OBJECT_USER_MAPPING },
 	/* OCLASS_DEFACL */
-	{ "default acl", -1 },		/* unmapped */
+	{ "default acl", OBJECT_DEFACL },
 	/* OCLASS_EXTENSION */
 	{ "extension", OBJECT_EXTENSION },
 	/* OCLASS_EVENT_TRIGGER */
@@ -557,6 +557,8 @@ static ObjectAddress get_object_address_opcf(ObjectType objtype, List *objname,
 						List *objargs, bool missing_ok);
 static ObjectAddress get_object_address_usermapping(List *objname,
 							   List *objargs, bool missing_ok);
+static ObjectAddress get_object_address_defacl(List *objname, List *objargs,
+						  bool missing_ok);
 static const ObjectPropertyType *get_object_property_data(Oid class_id);
 
 static void getRelationDescription(StringInfo buffer, Oid relid);
@@ -775,6 +777,10 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
 				address = get_object_address_usermapping(objname, objargs,
 														 missing_ok);
 				break;
+			case OBJECT_DEFACL:
+				address = get_object_address_defacl(objname, objargs,
+													missing_ok);
+				break;
 			default:
 				elog(ERROR, "unrecognized objtype: %d", (int) objtype);
 				/* placate compiler, in case it thinks elog might return */
@@ -1437,6 +1443,95 @@ get_object_address_usermapping(List *objname, List *objargs, bool missing_ok)
 }
 
 /*
+ * Find the ObjectAddress for a default ACL.
+ */
+static ObjectAddress
+get_object_address_defacl(List *objname, List *objargs, bool missing_ok)
+{
+	HeapTuple	tp;
+	Oid			userid;
+	Oid			schemaid;
+	char	   *username;
+	char	   *schema;
+	char		objtyp;
+	char	   *stuff;
+	ObjectAddress address;
+
+	ObjectAddressSet(address, DefaultAclRelationId, InvalidOid);
+
+	/*
+	 * Figure out the textual attributes first so that they can be used for
+	 * error reporting.
+	 */
+	username = strVal(linitial(objname));
+	if (list_length(objargs) >= 2)
+		schema = (char *) strVal(llast(objargs));
+	else
+		schema = NULL;
+
+	objtyp = ((char *) strVal(linitial(objargs)))[0];
+	switch (objtyp)
+	{
+		case DEFACLOBJ_RELATION:
+			stuff = "tables";
+			break;
+		case DEFACLOBJ_SEQUENCE:
+			stuff = "sequences";
+			break;
+		case DEFACLOBJ_FUNCTION:
+			stuff = "functions";
+			break;
+		case DEFACLOBJ_TYPE:
+			stuff = "types";
+			break;
+		default:
+			elog(ERROR, "invalid defacl type %c", objtyp);
+	}
+
+	tp = SearchSysCache1(AUTHNAME,
+						 CStringGetDatum(username));
+	if (!HeapTupleIsValid(tp))
+		goto not_found;
+
+	userid = HeapTupleGetOid(tp);
+	ReleaseSysCache(tp);
+
+	if (schema)
+	{
+		schemaid = get_namespace_oid(schema, true);
+		if (schemaid == InvalidOid)
+			goto not_found;
+	}
+	else
+		schemaid = InvalidOid;
+
+	tp = SearchSysCache3(DEFACLROLENSPOBJ,
+						 ObjectIdGetDatum(userid),
+						 ObjectIdGetDatum(schemaid),
+						 CharGetDatum(objtyp));
+	if (!HeapTupleIsValid(tp))
+		goto not_found;
+
+	address.objectId = HeapTupleGetOid(tp);
+	ReleaseSysCache(tp);
+
+	return address;
+
+not_found:
+	if (schema)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("default ACL for user \"%s\" in schema \"%s\" on %s does not exist",
+						username, schema, stuff)));
+	else
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("default ACL for user \"%s\" on %s does not exist",
+						username, stuff)));
+	return address;
+}
+
+/*
  * Convert an array of TEXT into a List of string Values, as emitted by the
  * parser, which is what get_object_address uses as input.
  */
@@ -1599,6 +1694,13 @@ pg_get_object_address(PG_FUNCTION_ARGS)
 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 						 errmsg("argument list length must be exactly %d", 2)));
 			break;
+		case OBJECT_DEFACL:
+			if ((list_length(args) < 1) ||
+				(list_length(args) > 2))
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("argument list length must be between %d and %d", 1, 2)));
+			break;
 		default:
 			break;
 	}
@@ -4013,10 +4115,8 @@ getObjectIdentityParts(const ObjectAddress *object,
 				SysScanDesc rcscan;
 				HeapTuple	tup;
 				Form_pg_default_acl defacl;
-
-				/* no objname support */
-				if (objname)
-					*objname = NIL;
+				char	   *schema;
+				char	   *username;
 
 				defaclrel = heap_open(DefaultAclRelationId, AccessShareLock);
 
@@ -4036,19 +4136,20 @@ getObjectIdentityParts(const ObjectAddress *object,
 
 				defacl = (Form_pg_default_acl) GETSTRUCT(tup);
 
+				username = GetUserNameFromId(defacl->defaclrole);
 				appendStringInfo(&buffer,
 								 "for role %s",
-					quote_identifier(GetUserNameFromId(defacl->defaclrole)));
+								 quote_identifier(username));
 
 				if (OidIsValid(defacl->defaclnamespace))
 				{
-					char	   *schema;
-
 					schema = get_namespace_name(defacl->defaclnamespace);
 					appendStringInfo(&buffer,
 									 " in schema %s",
 									 quote_identifier(schema));
 				}
+				else
+					schema = NULL;
 
 				switch (defacl->defaclobjtype)
 				{
@@ -4070,6 +4171,14 @@ getObjectIdentityParts(const ObjectAddress *object,
 						break;
 				}
 
+				if (objname)
+				{
+					*objname = list_make1(username);
+					*objargs = list_make1(psprintf("%c", defacl->defaclobjtype));
+					if (schema)
+						*objargs = lappend(*objargs, schema);
+				}
+
 				systable_endscan(rcscan);
 				heap_close(defaclrel, AccessShareLock);
 				break;
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 4e446bd..3fec57e 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1065,6 +1065,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
 		case OBJECT_COLUMN:
 		case OBJECT_COLLATION:
 		case OBJECT_CONVERSION:
+		case OBJECT_DEFACL:
 		case OBJECT_DEFAULT:
 		case OBJECT_DOMAIN:
 		case OBJECT_DOMCONSTRAINT:
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index dbde840..36c5d43 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1219,6 +1219,7 @@ typedef enum ObjectType
 	OBJECT_CONVERSION,
 	OBJECT_DATABASE,
 	OBJECT_DEFAULT,
+	OBJECT_DEFACL,
 	OBJECT_DOMAIN,
 	OBJECT_DOMCONSTRAINT,
 	OBJECT_EVENT_TRIGGER,
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 4ff7f35..47dc023 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -122,6 +122,10 @@ NOTICE:  test_event_trigger: ddl_command_end CREATE SERVER
 -- regress_event_trigger_end should fire here
 create user mapping for regression_bob server useless_server;
 NOTICE:  test_event_trigger: ddl_command_end CREATE USER MAPPING
+-- regress_event_trigger_end should fire here
+alter default privileges for role regression_bob
+ revoke delete on tables from regression_bob;
+NOTICE:  test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
 -- alter owner to non-superuser should fail
 alter event trigger regress_event_trigger owner to regression_bob;
 ERROR:  permission denied to change owner of event trigger "regress_event_trigger"
@@ -141,6 +145,7 @@ ERROR:  event trigger "regress_event_trigger" does not exist
 drop role regression_bob;
 ERROR:  role "regression_bob" cannot be dropped because some objects depend on it
 DETAIL:  owner of event trigger regress_event_trigger3
+owner of default privileges on new relations belonging to role regression_bob
 owner of user mapping for regression_bob on server useless_server
 -- cleanup before next test
 -- these are all OK; the second one should emit a NOTICE
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index e72abda..e7bb06a 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -30,6 +30,8 @@ CREATE TRIGGER t BEFORE INSERT ON addr_nsp.gentable FOR EACH ROW EXECUTE PROCEDU
 CREATE POLICY genpol ON addr_nsp.gentable;
 CREATE SERVER "integer" FOREIGN DATA WRAPPER addr_fdw;
 CREATE USER MAPPING FOR regtest_addr_user SERVER "integer";
+ALTER DEFAULT PRIVILEGES FOR ROLE regtest_addr_user IN SCHEMA public GRANT ALL ON TABLES TO regtest_addr_user;
+ALTER DEFAULT PRIVILEGES FOR ROLE regtest_addr_user REVOKE DELETE ON TABLES FROM regtest_addr_user;
 -- test some error cases
 SELECT pg_get_object_address('stone', '{}', '{}');
 ERROR:  unrecognized object type "stone"
@@ -77,7 +79,7 @@ BEGIN
 		('operator'), ('operator class'), ('operator family'), ('rule'), ('trigger'),
 		('text search parser'), ('text search dictionary'),
 		('text search template'), ('text search configuration'),
-		('policy'), ('user mapping')
+		('policy'), ('user mapping'), ('default acl')
 	LOOP
 		FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}')
 		LOOP
@@ -255,6 +257,12 @@ WARNING:  error for user mapping,{addr_nsp,zwei},{}: argument list length must b
 WARNING:  error for user mapping,{addr_nsp,zwei},{integer}: user mapping for user "addr_nsp" in server "integer" does not exist
 WARNING:  error for user mapping,{eins,zwei,drei},{}: argument list length must be exactly 1
 WARNING:  error for user mapping,{eins,zwei,drei},{integer}: user mapping for user "eins" in server "integer" does not exist
+WARNING:  error for default acl,{eins},{}: argument list length must be between 1 and 2
+WARNING:  error for default acl,{eins},{integer}: invalid defacl type i
+WARNING:  error for default acl,{addr_nsp,zwei},{}: argument list length must be between 1 and 2
+WARNING:  error for default acl,{addr_nsp,zwei},{integer}: invalid defacl type i
+WARNING:  error for default acl,{eins,zwei,drei},{}: argument list length must be between 1 and 2
+WARNING:  error for default acl,{eins,zwei,drei},{integer}: invalid defacl type i
 -- these object types cannot be qualified names
 SELECT pg_get_object_address('language', '{one}', '{}');
 ERROR:  language "one" does not exist
@@ -341,6 +349,8 @@ WITH objects (type, name, args) AS (VALUES
 				('foreign-data wrapper', '{addr_fdw}', '{}'),
 				('server', '{addr_fserv}', '{}'),
 				('user mapping', '{regtest_addr_user}', '{integer}'),
+				('default acl', '{regtest_addr_user}', '{r,public}'),
+				('default acl', '{regtest_addr_user}', '{r}'),
 				-- extension
 				-- event trigger
 				('policy', '{addr_nsp, gentable, genpol}', '{}')
@@ -355,6 +365,8 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
 	ORDER BY addr1.classid, addr1.objid;
            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 on tables                                 | t
  type                      | pg_catalog | _int4             | integer[]                                                            | t
  type                      | addr_nsp   | gencomptype       | addr_nsp.gencomptype                                                 | t
  type                      | addr_nsp   | genenum           | addr_nsp.genenum                                                     | t
@@ -391,11 +403,12 @@ 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 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
-(36 rows)
+(38 rows)
 
 ---
 --- Cleanup resources
 ---
 DROP FOREIGN DATA WRAPPER addr_fdw CASCADE;
 DROP SCHEMA addr_nsp CASCADE;
+DROP OWNED BY regtest_addr_user;
 DROP USER regtest_addr_user;
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index f89fa71..2569a6c 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -123,6 +123,10 @@ create server useless_server foreign data wrapper useless;
 -- regress_event_trigger_end should fire here
 create user mapping for regression_bob server useless_server;
 
+-- regress_event_trigger_end should fire here
+alter default privileges for role regression_bob
+ revoke delete on tables from regression_bob;
+
 -- alter owner to non-superuser should fail
 alter event trigger regress_event_trigger owner to regression_bob;
 
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index b714b52..f4d940c 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -34,6 +34,8 @@ CREATE TRIGGER t BEFORE INSERT ON addr_nsp.gentable FOR EACH ROW EXECUTE PROCEDU
 CREATE POLICY genpol ON addr_nsp.gentable;
 CREATE SERVER "integer" FOREIGN DATA WRAPPER addr_fdw;
 CREATE USER MAPPING FOR regtest_addr_user SERVER "integer";
+ALTER DEFAULT PRIVILEGES FOR ROLE regtest_addr_user IN SCHEMA public GRANT ALL ON TABLES TO regtest_addr_user;
+ALTER DEFAULT PRIVILEGES FOR ROLE regtest_addr_user REVOKE DELETE ON TABLES FROM regtest_addr_user;
 
 -- test some error cases
 SELECT pg_get_object_address('stone', '{}', '{}');
@@ -73,7 +75,7 @@ BEGIN
 		('operator'), ('operator class'), ('operator family'), ('rule'), ('trigger'),
 		('text search parser'), ('text search dictionary'),
 		('text search template'), ('text search configuration'),
-		('policy'), ('user mapping')
+		('policy'), ('user mapping'), ('default acl')
 	LOOP
 		FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}')
 		LOOP
@@ -156,6 +158,8 @@ WITH objects (type, name, args) AS (VALUES
 				('foreign-data wrapper', '{addr_fdw}', '{}'),
 				('server', '{addr_fserv}', '{}'),
 				('user mapping', '{regtest_addr_user}', '{integer}'),
+				('default acl', '{regtest_addr_user}', '{r,public}'),
+				('default acl', '{regtest_addr_user}', '{r}'),
 				-- extension
 				-- event trigger
 				('policy', '{addr_nsp, gentable, genpol}', '{}')
@@ -176,4 +180,5 @@ DROP FOREIGN DATA WRAPPER addr_fdw CASCADE;
 
 DROP SCHEMA addr_nsp CASCADE;
 
+DROP OWNED BY regtest_addr_user;
 DROP USER regtest_addr_user;
-- 
2.1.4

0003-deparse-core-get_object_address-support-opfamily-mem.patchtext/x-diff; charset=us-asciiDownload
>From 2f13e09885c3fed6afd3654a01815a45ba74f34f Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Fri, 6 Mar 2015 12:39:50 -0300
Subject: [PATCH 3/3] deparse/core: get_object_address support opfamily members

---
 src/backend/catalog/objectaddress.c          | 180 ++++++++++++++++++++++++---
 src/backend/commands/event_trigger.c         |   2 +
 src/include/nodes/parsenodes.h               |   2 +
 src/test/regress/expected/object_address.out |  32 +++--
 src/test/regress/sql/object_address.sql      |  12 +-
 5 files changed, 194 insertions(+), 34 deletions(-)

diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 32eea58..b6cde3e 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -492,9 +492,9 @@ ObjectTypeMap[] =
 	/* OCLASS_OPFAMILY */
 	{ "operator family", OBJECT_OPFAMILY },
 	/* OCLASS_AMOP */
-	{ "operator of access method", -1 },	/* unmapped */
+	{ "operator of access method", OBJECT_AMOP },
 	/* OCLASS_AMPROC */
-	{ "function of access method", -1 },	/* unmapped */
+	{ "function of access method", OBJECT_AMPROC },
 	/* OCLASS_REWRITE */
 	{ "rule", OBJECT_RULE },
 	/* OCLASS_TRIGGER */
@@ -552,9 +552,12 @@ static ObjectAddress get_object_address_attrdef(ObjectType objtype,
 						   List *objname, Relation *relp, LOCKMODE lockmode,
 						   bool missing_ok);
 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,
 						List *objargs, 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,
 							   List *objargs, bool missing_ok);
 static ObjectAddress get_object_address_defacl(List *objname, List *objargs,
@@ -661,7 +664,8 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
 					ObjectAddress	domaddr;
 					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));
 
 					address.classId = ConstraintRelationId;
@@ -685,7 +689,7 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
 				break;
 			case OBJECT_TYPE:
 			case OBJECT_DOMAIN:
-				address = get_object_address_type(objtype, objname, missing_ok);
+				address = get_object_address_type(objtype, list_head(objname), missing_ok);
 				break;
 			case OBJECT_AGGREGATE:
 				address.classId = ProcedureRelationId;
@@ -724,6 +728,11 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
 				address = get_object_address_opcf(objtype,
 											   objname, objargs, missing_ok);
 				break;
+			case OBJECT_AMOP:
+			case OBJECT_AMPROC:
+				address = get_object_address_opf_member(objtype, objname,
+														objargs, missing_ok);
+				break;
 			case OBJECT_LARGEOBJECT:
 				Assert(list_length(objname) == 1);
 				address.classId = LargeObjectRelationId;
@@ -1309,13 +1318,13 @@ get_object_address_attrdef(ObjectType objtype, List *objname,
  * Find the ObjectAddress for a type or domain
  */
 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;
 	TypeName   *typename;
 	Type		tup;
 
-	typename = (TypeName *) linitial(objname);
+	typename = (TypeName *) lfirst(typecell);
 
 	address.classId = TypeRelationId;
 	address.objectId = InvalidOid;
@@ -1384,6 +1393,116 @@ get_object_address_opcf(ObjectType objtype,
 	return address;
 }
 
+static ObjectAddress
+get_object_address_opf_member(ObjectType objtype,
+							  List *objname, List *objargs, bool missing_ok)
+{
+	ObjectAddress	famaddr;
+	ObjectAddress	typaddr;
+	ObjectAddress	address;
+	ListCell *cell;
+	Value  *amname;
+	List   *copy;
+	List   *amlist;
+	Oid		lefttype;
+	Oid		righttype;
+	int		stratnum;
+	char   *leftname;
+	char   *rightname;
+
+	/*
+	 * last element of the objname list contains the AM name; previous-to-last
+	 * contains the strategy or procedure number; elements prior to that contain the
+	 * (possibly qualified) opfamily name.  Create a copy of the list that we
+	 * can scribble on to extract those values.
+	 */
+	copy = list_copy(objname);
+	amname = lfirst(list_tail(copy));
+	amlist = list_make1(amname);
+	copy = list_truncate(copy, list_length(copy) - 1);
+
+	stratnum = atoi(strVal(llast(copy)));
+	copy = list_truncate(copy, list_length(copy) - 1);
+
+	/* no missing_ok support here */
+	famaddr = get_object_address_opcf(OBJECT_OPFAMILY, copy, amlist, false);
+
+	cell = list_head(objargs);
+	leftname = strVal(cell);
+	typaddr = get_object_address_type(OBJECT_TYPE, cell, missing_ok);
+	lefttype = typaddr.objectId;
+
+	cell = lnext(cell);
+	typaddr = get_object_address_type(OBJECT_TYPE, cell, missing_ok);
+	rightname = strVal(cell);
+	righttype = typaddr.objectId;
+
+	switch (objtype)
+	{
+		case OBJECT_AMOP:
+			{
+				HeapTuple	tp;
+
+				ObjectAddressSet(address, AccessMethodOperatorRelationId,
+								 InvalidOid);
+
+				tp = SearchSysCache4(AMOPSTRATEGY,
+									 ObjectIdGetDatum(famaddr.objectId),
+									 ObjectIdGetDatum(lefttype),
+									 ObjectIdGetDatum(righttype),
+									 Int16GetDatum(stratnum));
+				if (!HeapTupleIsValid(tp))
+				{
+					if (!missing_ok)
+						ereport(ERROR,
+								(errcode(ERRCODE_UNDEFINED_OBJECT),
+								 errmsg("operator %d (%s, %s) of %s does not exist",
+										stratnum, leftname, rightname,
+										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(lefttype),
+									 ObjectIdGetDatum(righttype),
+									 Int16GetDatum(stratnum));
+				if (!HeapTupleIsValid(tp))
+				{
+					if (!missing_ok)
+						ereport(ERROR,
+								(errcode(ERRCODE_UNDEFINED_OBJECT),
+								 errmsg("function %d (%s, %s) of %s does not exist",
+										stratnum, leftname, rightname,
+										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.
  */
@@ -1644,7 +1763,9 @@ pg_get_object_address(PG_FUNCTION_ARGS)
 	if (type == OBJECT_AGGREGATE ||
 		type == OBJECT_FUNCTION ||
 		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 */
 		Datum  *elems;
@@ -1688,6 +1809,13 @@ pg_get_object_address(PG_FUNCTION_ARGS)
 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 						 errmsg("argument list length must be exactly %d", 1)));
 			break;
+		case OBJECT_AMOP:
+		case OBJECT_AMPROC:
+			if (list_length(name) < 2)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("name list length must be at least %d", 2)));
+			/* fall through to check args length */
 		case OBJECT_OPERATOR:
 			if (list_length(args) != 2)
 				ereport(ERROR,
@@ -3736,10 +3864,6 @@ getObjectIdentityParts(const ObjectAddress *object,
 				Form_pg_amop amopForm;
 				StringInfoData opfam;
 
-				/* no objname support here */
-				if (objname)
-					*objname = NIL;
-
 				amopDesc = heap_open(AccessMethodOperatorRelationId,
 									 AccessShareLock);
 
@@ -3760,7 +3884,19 @@ getObjectIdentityParts(const ObjectAddress *object,
 				amopForm = (Form_pg_amop) GETSTRUCT(tup);
 
 				initStringInfo(&opfam);
-				getOpFamilyIdentity(&opfam, amopForm->amopfamily, NULL, NULL);
+				getOpFamilyIdentity(&opfam, amopForm->amopfamily, objname, objargs);
+
+				if (objname)
+				{
+					*objname = lappend(*objname,
+									   psprintf("%d", amopForm->amopstrategy));
+					*objname = lappend(*objname,
+									   llast(*objargs));
+					*objargs = lappend(NIL,
+									   format_type_be_qualified(amopForm->amoplefttype));
+					*objargs = lappend(*objargs,
+									   format_type_be_qualified(amopForm->amoprighttype));
+				}
 
 				appendStringInfo(&buffer, "operator %d (%s, %s) of %s",
 								 amopForm->amopstrategy,
@@ -3784,10 +3920,6 @@ getObjectIdentityParts(const ObjectAddress *object,
 				Form_pg_amproc amprocForm;
 				StringInfoData opfam;
 
-				/* no objname support here */
-				if (objname)
-					*objname = NIL;
-
 				amprocDesc = heap_open(AccessMethodProcedureRelationId,
 									   AccessShareLock);
 
@@ -3808,7 +3940,19 @@ getObjectIdentityParts(const ObjectAddress *object,
 				amprocForm = (Form_pg_amproc) GETSTRUCT(tup);
 
 				initStringInfo(&opfam);
-				getOpFamilyIdentity(&opfam, amprocForm->amprocfamily, NULL, NULL);
+				getOpFamilyIdentity(&opfam, amprocForm->amprocfamily, objname, objargs);
+
+				if (objname)
+				{
+					*objname = lappend(*objname,
+									   psprintf("%d", amprocForm->amprocnum));
+					*objname = lappend(*objname,
+									   llast(*objargs));
+					*objargs = lappend(NIL,
+									   format_type_be_qualified(amprocForm->amproclefttype));
+					*objargs = lappend(*objargs,
+									   format_type_be_qualified(amprocForm->amprocrighttype));
+				}
 
 				appendStringInfo(&buffer, "function %d (%s, %s) of %s",
 								 amprocForm->amprocnum,
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 3fec57e..4bcc327 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1060,6 +1060,8 @@ EventTriggerSupportsObjectType(ObjectType obtype)
 			/* no support for event triggers on event triggers */
 			return false;
 		case OBJECT_AGGREGATE:
+		case OBJECT_AMOP:
+		case OBJECT_AMPROC:
 		case OBJECT_ATTRIBUTE:
 		case OBJECT_CAST:
 		case OBJECT_COLUMN:
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 36c5d43..d969640 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1212,6 +1212,8 @@ typedef struct SetOperationStmt
 typedef enum ObjectType
 {
 	OBJECT_AGGREGATE,
+	OBJECT_AMOP,
+	OBJECT_AMPROC,
 	OBJECT_ATTRIBUTE,			/* type's attribute, when distinct from column */
 	OBJECT_CAST,
 	OBJECT_COLUMN,
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index e7bb06a..c85f643 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -45,8 +45,7 @@ DECLARE
 	objtype text;
 BEGIN
 	FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
-		('toast table column'), ('view column'), ('materialized view column'),
-		('operator of access method'), ('function of access method')
+		('toast table column'), ('view column'), ('materialized view column')
 	LOOP
 		BEGIN
 			PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -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 view column: unsupported object type "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 $$
 DECLARE
 	objtype text;
@@ -79,7 +76,8 @@ BEGIN
 		('operator'), ('operator class'), ('operator family'), ('rule'), ('trigger'),
 		('text search parser'), ('text search dictionary'),
 		('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
 		FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}')
 		LOOP
@@ -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}: invalid defacl type i
 WARNING:  error for default acl,{eins,zwei,drei},{}: argument list length must be between 1 and 2
 WARNING:  error for default acl,{eins,zwei,drei},{integer}: invalid defacl type i
+WARNING:  error for operator of access method,{eins},{}: name list length must be at least 2
+WARNING:  error for operator of access method,{eins},{integer}: name list length must be at least 2
+WARNING:  error for operator of access method,{addr_nsp,zwei},{}: argument list length must be exactly 2
+WARNING:  error for operator of access method,{addr_nsp,zwei},{integer}: argument list length must be exactly 2
+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 2
+WARNING:  error for function of access method,{eins},{integer}: name list length must be at least 2
+WARNING:  error for function of access method,{addr_nsp,zwei},{}: argument list length must be exactly 2
+WARNING:  error for function of access method,{addr_nsp,zwei},{integer}: argument list length must be exactly 2
+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
 SELECT pg_get_object_address('language', '{one}', '{}');
 ERROR:  language "one" does not exist
@@ -334,8 +344,8 @@ WITH objects (type, name, args) AS (VALUES
 				('operator', '{+}', '{int4, int4}'),
 				('operator class', '{int4_ops}', '{btree}'),
 				('operator family', '{integer_ops}', '{btree}'),
-				-- operator of access method
-				-- function of access method
+				('operator of access method', '{integer_ops,1,btree}', '{integer,integer}'),
+				('function of access method', '{integer_ops,2,btree}', '{integer,integer}'),
 				('rule', '{addr_nsp, genview, _RETURN}', '{}'),
 				('trigger', '{addr_nsp, gentable, t}', '{}'),
 				('schema', '{addr_nsp}', '{}'),
@@ -362,7 +372,7 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.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;
+	ORDER BY addr1.classid, addr1.objid, addr1.subobjid;
            type            |   schema   |       name        |                               identity                               | ?column? 
 ---------------------------+------------+-------------------+----------------------------------------------------------------------+----------
  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)).*,
  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
+ foreign table column      | addr_nsp   | genftable         | addr_nsp.genftable.a                                                 | t
  role                      |            | regtest_addr_user | regtest_addr_user                                                    | t
  server                    |            | addr_fserv        | addr_fserv                                                           | t
  user mapping              |            |                   | regtest_addr_user on server integer                                  | 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
  cast                      |            |                   | (bigint AS integer)                                                  | 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)).*,
  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
-(38 rows)
+(40 rows)
 
 ---
 --- Cleanup resources
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index f4d940c..e1be6bf 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -48,8 +48,7 @@ DECLARE
 	objtype text;
 BEGIN
 	FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
-		('toast table column'), ('view column'), ('materialized view column'),
-		('operator of access method'), ('function of access method')
+		('toast table column'), ('view column'), ('materialized view column')
 	LOOP
 		BEGIN
 			PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -75,7 +74,8 @@ BEGIN
 		('operator'), ('operator class'), ('operator family'), ('rule'), ('trigger'),
 		('text search parser'), ('text search dictionary'),
 		('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
 		FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}')
 		LOOP
@@ -143,8 +143,8 @@ WITH objects (type, name, args) AS (VALUES
 				('operator', '{+}', '{int4, int4}'),
 				('operator class', '{int4_ops}', '{btree}'),
 				('operator family', '{integer_ops}', '{btree}'),
-				-- operator of access method
-				-- function of access method
+				('operator of access method', '{integer_ops,1,btree}', '{integer,integer}'),
+				('function of access method', '{integer_ops,2,btree}', '{integer,integer}'),
 				('rule', '{addr_nsp, genview, _RETURN}', '{}'),
 				('trigger', '{addr_nsp, gentable, t}', '{}'),
 				('schema', '{addr_nsp}', '{}'),
@@ -171,7 +171,7 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.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;
+	ORDER BY addr1.classid, addr1.objid, addr1.subobjid;
 
 ---
 --- Cleanup resources
-- 
2.1.4

#2Stephen Frost
sfrost@snowman.net
In reply to: Alvaro Herrera (#1)
Re: get_object_address support for additional object types

Alvaro,

* Alvaro Herrera (alvherre@2ndquadrant.com) wrote:

This is extracted from the DDL deparse series. These patches add
get_object_address support for the following object types:

- user mappings
- default ACLs
- operators and functions of operator families

I took a (relatively quick) look through these patches.

Subject: [PATCH 1/3] deparse/core: get_object_address support user mappings

[...]

I thought this looked fine. One minor nit-pick is that the function added
doesn't have a single comment, but it's a pretty short too.

Subject: [PATCH 2/3] deparse/core: get_object_address support default ACLs

[...]

+ char *stuff;

Nit-pick, but 'stuff' isn't really a great variable name. :) Perhaps
'defacltype_name'? It's longer, sure, but it's not used a lot..

Subject: [PATCH 3/3] deparse/core: get_object_address support opfamily members

@@ -661,7 +664,8 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
ObjectAddress domaddr;
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));

address.classId = ConstraintRelationId;

I don't really care for how all the get_object_address stuff uses lists
for arguments instead of using straight-forward arguments, but it's how
it's been done and I can kind of see the reasoning behind it. I'm not
following why you're switching this case (get_object_address_type) to
using a ListCell though..?

I thought the rest of it looked alright. I agree it's a bit odd how the
opfamily is handled but I agree with your assessment that there's not
much better we can do with this object representation.

Thanks!

Stephen

#3Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Stephen Frost (#2)
Re: get_object_address support for additional object types

Stephen, thanks for the comments.

Stephen Frost wrote:

I don't really care for how all the get_object_address stuff uses lists
for arguments instead of using straight-forward arguments, but it's how
it's been done and I can kind of see the reasoning behind it. I'm not
following why you're switching this case (get_object_address_type) to
using a ListCell though..?

The reason for changing get_object_address_type is that it's a helper
soubroutine to get_object_address and we can do whatever is more
convenient. It turns out that it's more convenient, for the amop/amproc
cases, to pass a ListCell instead of a List.

get_object_address gets its stuff directly from the parser in some
cases, or from pg_get_object_address otherwise, which constructs things
to mimick exactly what the parser does. We can change what the parser
emits and as long as we keep get_object_address in agreement, there is
no issue. There might be code churn of course (because some of those
object representations travel from the parser through other parts of the
backend), but that doesn't really matter much. We have now exposed that
interface through the SQL-callable pg_get_object_address function, but
I'm pretty sure we could change that easily and it wouldn't be a
tremendous problem -- as long as we do it before 9.5 is released.

For instance we might want to decide that we want to have three lists
instead of two; or two lists and a numeric quantity (which would also
help the large object case, currently a bit ugly), or that we want
something akin to what the parser does to types using struct TypeName
for opclass-related objects.

Anyway I'm not hot on changing anything here. It's a bit cumbersome an
interface to use, but it's not excessively exposed to the user unless
they use event triggers, and even then it is perfectly reliable anyhow.

I thought the rest of it looked alright. I agree it's a bit odd how the
opfamily is handled but I agree with your assessment that there's not
much better we can do with this object representation.

Great, thanks.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#4Stephen Frost
sfrost@snowman.net
In reply to: Alvaro Herrera (#3)
Re: get_object_address support for additional object types

* Alvaro Herrera (alvherre@2ndquadrant.com) wrote:

Anyway I'm not hot on changing anything here. It's a bit cumbersome an
interface to use, but it's not excessively exposed to the user unless
they use event triggers, and even then it is perfectly reliable anyhow.

Works for me.

Thanks!

Stephen

#5Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Stephen Frost (#2)
1 attachment(s)
Re: get_object_address support for additional object types

Stephen Frost wrote:

I thought the rest of it looked alright. I agree it's a bit odd how the
opfamily is handled but I agree with your assessment that there's not
much better we can do with this object representation.

Actually, on second thought I revisited this and changed the
representation for opfamilies and opclasses: instead of putting the AM
name in objargs, we can put it as the first element of objname instead.
That way, objargs is unused for opfamilies and opclasses, and we're free
to use it for the type arguments in amops and amprocs. This makes the
lists consistent for the four cases: in objname, amname first, then
qualified opclass/opfamily name. For amop/amproc, the member number
follows. Objargs is unused in opclass/opfamily, and it's a two-element
list of types in amop/amproc.

The attached patch changes the grammar to comply with the above, and
adds the necessary get_object_address and getObjectIdentityParts support
code for amop/amproc objects.

The only thing a bit unusual is that in does_not_exist_skipping() we
need to do an list_copy_tail() to strip out the amname before reporting
the "skipping" message, for DROP OPERATOR CLASS/FAMILY IF NOT EXISTS.
I don't think this is a problem. In return, the code to deconstruct
amop and amproc addresses is more sensible.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

0001-Support-opfamily-members-in-get_object_address.patchtext/x-diff; charset=us-asciiDownload
>From 35336c997b8be18d890a3a8bdf2822f757f70faf Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Fri, 6 Mar 2015 12:39:50 -0300
Subject: [PATCH] Support opfamily members in get_object_address

In the spirit of 890192e99af and 4464303405f, have get_object_address
understand pg_amop and pg_amproc objects.  There is no way to refer to
such objects directly in the grammar, but in event triggers and
pg_get_object_address it becomes possible to become involved with them.

Reviewed by: Stephen Frost
---
 src/backend/catalog/objectaddress.c          | 234 +++++++++++++++++++++------
 src/backend/commands/dropcmds.c              |  24 ++-
 src/backend/commands/event_trigger.c         |   2 +
 src/backend/parser/gram.y                    |  43 ++---
 src/include/nodes/parsenodes.h               |   2 +
 src/test/regress/expected/object_address.out |  60 ++++---
 src/test/regress/sql/object_address.sql      |  16 +-
 7 files changed, 264 insertions(+), 117 deletions(-)

diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 142bc68..46ea09a 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -492,9 +492,9 @@ ObjectTypeMap[] =
 	/* OCLASS_OPFAMILY */
 	{ "operator family", OBJECT_OPFAMILY },
 	/* OCLASS_AMOP */
-	{ "operator of access method", -1 },	/* unmapped */
+	{ "operator of access method", OBJECT_AMOP },
 	/* OCLASS_AMPROC */
-	{ "function of access method", -1 },	/* unmapped */
+	{ "function of access method", OBJECT_AMPROC },
 	/* OCLASS_REWRITE */
 	{ "rule", OBJECT_RULE },
 	/* OCLASS_TRIGGER */
@@ -552,9 +552,12 @@ static ObjectAddress get_object_address_attrdef(ObjectType objtype,
 						   List *objname, Relation *relp, LOCKMODE lockmode,
 						   bool missing_ok);
 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,
-						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,
 							   List *objargs, bool missing_ok);
 static ObjectAddress get_object_address_defacl(List *objname, List *objargs,
@@ -567,8 +570,7 @@ 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, List **objname,
-					List **objargs);
+static void getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **objname);
 static void getRelationIdentity(StringInfo buffer, Oid relid, List **objname);
 
 /*
@@ -661,7 +663,8 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
 					ObjectAddress	domaddr;
 					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));
 
 					address.classId = ConstraintRelationId;
@@ -685,7 +688,7 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
 				break;
 			case OBJECT_TYPE:
 			case OBJECT_DOMAIN:
-				address = get_object_address_type(objtype, objname, missing_ok);
+				address = get_object_address_type(objtype, list_head(objname), missing_ok);
 				break;
 			case OBJECT_AGGREGATE:
 				address.classId = ProcedureRelationId;
@@ -721,8 +724,12 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
 				break;
 			case OBJECT_OPCLASS:
 			case OBJECT_OPFAMILY:
-				address = get_object_address_opcf(objtype,
-											   objname, objargs, missing_ok);
+				address = get_object_address_opcf(objtype, objname, missing_ok);
+				break;
+			case OBJECT_AMOP:
+			case OBJECT_AMPROC:
+				address = get_object_address_opf_member(objtype, objname,
+														objargs, missing_ok);
 				break;
 			case OBJECT_LARGEOBJECT:
 				Assert(list_length(objname) == 1);
@@ -1309,13 +1316,13 @@ get_object_address_attrdef(ObjectType objtype, List *objname,
  * Find the ObjectAddress for a type or domain
  */
 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;
 	TypeName   *typename;
 	Type		tup;
 
-	typename = (TypeName *) linitial(objname);
+	typename = (TypeName *) lfirst(typecell);
 
 	address.classId = TypeRelationId;
 	address.objectId = InvalidOid;
@@ -1351,15 +1358,14 @@ get_object_address_type(ObjectType objtype, List *objname, bool missing_ok)
  * Find the ObjectAddress for an opclass or opfamily.
  */
 static ObjectAddress
-get_object_address_opcf(ObjectType objtype,
-						List *objname, List *objargs, bool missing_ok)
+get_object_address_opcf(ObjectType objtype, List *objname, bool missing_ok)
 {
 	Oid			amoid;
 	ObjectAddress address;
 
-	Assert(list_length(objargs) == 1);
 	/* 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)
 	{
@@ -1385,6 +1391,114 @@ get_object_address_opcf(ObjectType objtype,
 }
 
 /*
+ * 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.
  */
 static ObjectAddress
@@ -1673,7 +1787,9 @@ pg_get_object_address(PG_FUNCTION_ARGS)
 	if (type == OBJECT_AGGREGATE ||
 		type == OBJECT_FUNCTION ||
 		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 */
 		Datum  *elems;
@@ -1708,8 +1824,6 @@ pg_get_object_address(PG_FUNCTION_ARGS)
 	switch (type)
 	{
 		case OBJECT_DOMCONSTRAINT:
-		case OBJECT_OPCLASS:
-		case OBJECT_OPFAMILY:
 		case OBJECT_CAST:
 		case OBJECT_USER_MAPPING:
 		case OBJECT_DEFACL:
@@ -1718,6 +1832,20 @@ pg_get_object_address(PG_FUNCTION_ARGS)
 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 						 errmsg("argument list length must be exactly %d", 1)));
 			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:
 			if (list_length(args) != 2)
 				ereport(ERROR,
@@ -3730,24 +3858,22 @@ getObjectIdentityParts(const ObjectAddress *object,
 						 opcForm->opcmethod);
 				amForm = (Form_pg_am) GETSTRUCT(amTup);
 
-				appendStringInfoString(&buffer,
-									   quote_qualified_identifier(schema,
-												 NameStr(opcForm->opcname)));
-				appendStringInfo(&buffer, " USING %s",
+				appendStringInfo(&buffer, "%s USING %s",
+								 quote_qualified_identifier(schema,
+															NameStr(opcForm->opcname)),
 								 quote_identifier(NameStr(amForm->amname)));
 				if (objname)
-				{
-					*objname = list_make2(pstrdup(schema),
+					*objname = list_make3(pstrdup(NameStr(amForm->amname)),
+										  schema,
 										  pstrdup(NameStr(opcForm->opcname)));
-					*objargs = list_make1(pstrdup(NameStr(amForm->amname)));
-				}
+
 				ReleaseSysCache(amTup);
 				ReleaseSysCache(opcTup);
 				break;
 			}
 
 		case OCLASS_OPFAMILY:
-			getOpFamilyIdentity(&buffer, object->objectId, objname, objargs);
+			getOpFamilyIdentity(&buffer, object->objectId, objname);
 			break;
 
 		case OCLASS_AMOP:
@@ -3758,10 +3884,8 @@ getObjectIdentityParts(const ObjectAddress *object,
 				SysScanDesc amscan;
 				Form_pg_amop amopForm;
 				StringInfoData opfam;
-
-				/* no objname support here */
-				if (objname)
-					*objname = NIL;
+				char	   *ltype;
+				char	   *rtype;
 
 				amopDesc = heap_open(AccessMethodOperatorRelationId,
 									 AccessShareLock);
@@ -3783,13 +3907,21 @@ getObjectIdentityParts(const ObjectAddress *object,
 				amopForm = (Form_pg_amop) GETSTRUCT(tup);
 
 				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",
 								 amopForm->amopstrategy,
-							format_type_be_qualified(amopForm->amoplefttype),
-						   format_type_be_qualified(amopForm->amoprighttype),
-								 opfam.data);
+								 ltype, rtype, opfam.data);
 
 				pfree(opfam.data);
 
@@ -3806,10 +3938,8 @@ getObjectIdentityParts(const ObjectAddress *object,
 				HeapTuple	tup;
 				Form_pg_amproc amprocForm;
 				StringInfoData opfam;
-
-				/* no objname support here */
-				if (objname)
-					*objname = NIL;
+				char	   *ltype;
+				char	   *rtype;
 
 				amprocDesc = heap_open(AccessMethodProcedureRelationId,
 									   AccessShareLock);
@@ -3831,13 +3961,21 @@ getObjectIdentityParts(const ObjectAddress *object,
 				amprocForm = (Form_pg_amproc) GETSTRUCT(tup);
 
 				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",
 								 amprocForm->amprocnum,
-						format_type_be_qualified(amprocForm->amproclefttype),
-					   format_type_be_qualified(amprocForm->amprocrighttype),
-								 opfam.data);
+								 ltype, rtype, opfam.data);
 
 				pfree(opfam.data);
 
@@ -4263,7 +4401,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 }
 
 static void
-getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **objname, List **objargs)
+getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **objname)
 {
 	HeapTuple	opfTup;
 	Form_pg_opfamily opfForm;
@@ -4289,11 +4427,9 @@ getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **objname, List **objargs
 					 NameStr(amForm->amname));
 
 	if (objname)
-	{
-		*objname = list_make2(pstrdup(schema),
+		*objname = list_make3(pstrdup(NameStr(amForm->amname)),
+							  pstrdup(schema),
 							  pstrdup(NameStr(opfForm->opfname)));
-		*objargs = list_make1(pstrdup(NameStr(amForm->amname)));
-	}
 
 	ReleaseSysCache(amTup);
 	ReleaseSysCache(opfTup);
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index e5185ba..a1b0d4d 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -406,19 +406,27 @@ does_not_exist_skipping(ObjectType objtype, List *objname, List *objargs)
 			name = NameListToString(objname);
 			break;
 		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");
-				name = NameListToString(objname);
-				args = strVal(linitial(objargs));
+				List *opcname = list_copy_tail(objname, 1);
+
+				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;
 		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");
-				name = NameListToString(objname);
-				args = strVal(linitial(objargs));
+				List *opfname = list_copy_tail(objname, 1);
+
+				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;
 		default:
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 3fec57e..4bcc327 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1060,6 +1060,8 @@ EventTriggerSupportsObjectType(ObjectType obtype)
 			/* no support for event triggers on event triggers */
 			return false;
 		case OBJECT_AGGREGATE:
+		case OBJECT_AMOP:
+		case OBJECT_AMPROC:
 		case OBJECT_ATTRIBUTE:
 		case OBJECT_CAST:
 		case OBJECT_COLUMN:
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index cf0d317..b5804f4 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -3950,8 +3950,7 @@ AlterExtensionContentsStmt:
 					n->extname = $3;
 					n->action = $4;
 					n->objtype = OBJECT_OPCLASS;
-					n->objname = $7;
-					n->objargs = list_make1(makeString($9));
+					n->objname = list_make1(lcons(makeString($9), $7));
 					$$ = (Node *)n;
 				}
 			| ALTER EXTENSION name add_drop OPERATOR FAMILY any_name USING access_method
@@ -3960,8 +3959,7 @@ AlterExtensionContentsStmt:
 					n->extname = $3;
 					n->action = $4;
 					n->objtype = OBJECT_OPFAMILY;
-					n->objname = $7;
-					n->objargs = list_make1(makeString($9));
+					n->objname = lcons(makeString($9), $7);
 					$$ = (Node *)n;
 				}
 			| ALTER EXTENSION name add_drop SCHEMA name
@@ -5362,8 +5360,7 @@ DropOpClassStmt:
 			DROP OPERATOR CLASS any_name USING access_method opt_drop_behavior
 				{
 					DropStmt *n = makeNode(DropStmt);
-					n->objects = list_make1($4);
-					n->arguments = list_make1(list_make1(makeString($6)));
+					n->objects = list_make1(lcons(makeString($6), $4));
 					n->removeType = OBJECT_OPCLASS;
 					n->behavior = $7;
 					n->missing_ok = false;
@@ -5373,8 +5370,7 @@ DropOpClassStmt:
 			| DROP OPERATOR CLASS IF_P EXISTS any_name USING access_method opt_drop_behavior
 				{
 					DropStmt *n = makeNode(DropStmt);
-					n->objects = list_make1($6);
-					n->arguments = list_make1(list_make1(makeString($8)));
+					n->objects = list_make1(lcons(makeString($8), $6));
 					n->removeType = OBJECT_OPCLASS;
 					n->behavior = $9;
 					n->missing_ok = true;
@@ -5387,8 +5383,7 @@ DropOpFamilyStmt:
 			DROP OPERATOR FAMILY any_name USING access_method opt_drop_behavior
 				{
 					DropStmt *n = makeNode(DropStmt);
-					n->objects = list_make1($4);
-					n->arguments = list_make1(list_make1(makeString($6)));
+					n->objects = list_make1(lcons(makeString($6), $4));
 					n->removeType = OBJECT_OPFAMILY;
 					n->behavior = $7;
 					n->missing_ok = false;
@@ -5398,8 +5393,7 @@ DropOpFamilyStmt:
 			| DROP OPERATOR FAMILY IF_P EXISTS any_name USING access_method opt_drop_behavior
 				{
 					DropStmt *n = makeNode(DropStmt);
-					n->objects = list_make1($6);
-					n->arguments = list_make1(list_make1(makeString($8)));
+					n->objects = list_make1(lcons(makeString($8), $6));
 					n->removeType = OBJECT_OPFAMILY;
 					n->behavior = $9;
 					n->missing_ok = true;
@@ -5741,8 +5735,7 @@ CommentStmt:
 				{
 					CommentStmt *n = makeNode(CommentStmt);
 					n->objtype = OBJECT_OPCLASS;
-					n->objname = $5;
-					n->objargs = list_make1(makeString($7));
+					n->objname = lcons(makeString($7), $5);
 					n->comment = $9;
 					$$ = (Node *) n;
 				}
@@ -5750,8 +5743,8 @@ CommentStmt:
 				{
 					CommentStmt *n = makeNode(CommentStmt);
 					n->objtype = OBJECT_OPFAMILY;
-					n->objname = $5;
-					n->objargs = list_make1(makeString($7));
+					n->objname = lcons(makeString($7), $5);
+					n->objargs = NIL;
 					n->comment = $9;
 					$$ = (Node *) n;
 				}
@@ -7476,8 +7469,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
 				{
 					RenameStmt *n = makeNode(RenameStmt);
 					n->renameType = OBJECT_OPCLASS;
-					n->object = $4;
-					n->objarg = list_make1(makeString($6));
+					n->object = lcons(makeString($6), $4);
 					n->newname = $9;
 					n->missing_ok = false;
 					$$ = (Node *)n;
@@ -7486,8 +7478,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
 				{
 					RenameStmt *n = makeNode(RenameStmt);
 					n->renameType = OBJECT_OPFAMILY;
-					n->object = $4;
-					n->objarg = list_make1(makeString($6));
+					n->object = lcons(makeString($6), $4);
 					n->newname = $9;
 					n->missing_ok = false;
 					$$ = (Node *)n;
@@ -7924,8 +7915,7 @@ AlterObjectSchemaStmt:
 				{
 					AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
 					n->objectType = OBJECT_OPCLASS;
-					n->object = $4;
-					n->objarg = list_make1(makeString($6));
+					n->object = lcons(makeString($6), $4);
 					n->newschema = $9;
 					n->missing_ok = false;
 					$$ = (Node *)n;
@@ -7934,8 +7924,7 @@ AlterObjectSchemaStmt:
 				{
 					AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
 					n->objectType = OBJECT_OPFAMILY;
-					n->object = $4;
-					n->objarg = list_make1(makeString($6));
+					n->object = lcons(makeString($6), $4);
 					n->newschema = $9;
 					n->missing_ok = false;
 					$$ = (Node *)n;
@@ -8162,8 +8151,7 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleSpec
 				{
 					AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
 					n->objectType = OBJECT_OPCLASS;
-					n->object = $4;
-					n->objarg = list_make1(makeString($6));
+					n->object = lcons(makeString($6), $4);
 					n->newowner = $9;
 					$$ = (Node *)n;
 				}
@@ -8171,8 +8159,7 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleSpec
 				{
 					AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
 					n->objectType = OBJECT_OPFAMILY;
-					n->object = $4;
-					n->objarg = list_make1(makeString($6));
+					n->object = lcons(makeString($6), $4);
 					n->newowner = $9;
 					$$ = (Node *)n;
 				}
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 38ed661..5de1b75 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1232,6 +1232,8 @@ typedef struct SetOperationStmt
 typedef enum ObjectType
 {
 	OBJECT_AGGREGATE,
+	OBJECT_AMOP,
+	OBJECT_AMPROC,
 	OBJECT_ATTRIBUTE,			/* type's attribute, when distinct from column */
 	OBJECT_CAST,
 	OBJECT_COLUMN,
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 3bcbcd8..365dcca 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -45,8 +45,7 @@ DECLARE
 	objtype text;
 BEGIN
 	FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
-		('toast table column'), ('view column'), ('materialized view column'),
-		('operator of access method'), ('function of access method')
+		('toast table column'), ('view column'), ('materialized view column')
 	LOOP
 		BEGIN
 			PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -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 view column: unsupported object type "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 $$
 DECLARE
 	objtype text;
@@ -79,7 +76,8 @@ BEGIN
 		('operator'), ('operator class'), ('operator family'), ('rule'), ('trigger'),
 		('text search parser'), ('text search dictionary'),
 		('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
 		FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}')
 		LOOP
@@ -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,{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 class,{eins},{}: argument list length must be exactly 1
-WARNING:  error for operator class,{eins},{integer}: access method "integer" does not exist
-WARNING:  error for operator class,{addr_nsp,zwei},{}: argument list length must be exactly 1
-WARNING:  error for operator class,{addr_nsp,zwei},{integer}: access method "integer" 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},{integer}: access method "integer" does not exist
-WARNING:  error for operator family,{eins},{}: argument list length must be exactly 1
-WARNING:  error for operator family,{eins},{integer}: access method "integer" does not exist
-WARNING:  error for operator family,{addr_nsp,zwei},{}: argument list length must be exactly 1
-WARNING:  error for operator family,{addr_nsp,zwei},{integer}: access method "integer" 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},{integer}: access method "integer" does not exist
+WARNING:  error for operator class,{eins},{}: name list length must be at least 2
+WARNING:  error for operator class,{eins},{integer}: name list length must be at least 2
+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 "addr_nsp" does not exist
+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 "eins" does not exist
+WARNING:  error for operator family,{eins},{}: name list length must be at least 2
+WARNING:  error for operator family,{eins},{integer}: name list length must be at least 2
+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 "addr_nsp" does not exist
+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 "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,{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
 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},{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
 SELECT pg_get_object_address('language', '{one}', '{}');
 ERROR:  language "one" does not exist
@@ -332,10 +342,10 @@ WITH objects (type, name, args) AS (VALUES
 				('language', '{plpgsql}', '{}'),
 				-- large object
 				('operator', '{+}', '{int4, int4}'),
-				('operator class', '{int4_ops}', '{btree}'),
-				('operator family', '{integer_ops}', '{btree}'),
-				-- operator of access method
-				-- function of access method
+				('operator class', '{btree, int4_ops}', '{}'),
+				('operator family', '{btree, integer_ops}', '{}'),
+				('operator of access method', '{btree,integer_ops,1}', '{integer,integer}'),
+				('function of access method', '{btree,integer_ops,2}', '{integer,integer}'),
 				('rule', '{addr_nsp, genview, _RETURN}', '{}'),
 				('trigger', '{addr_nsp, gentable, t}', '{}'),
 				('schema', '{addr_nsp}', '{}'),
@@ -362,7 +372,7 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.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;
+	ORDER BY addr1.classid, addr1.objid, addr1.subobjid;
            type            |   schema   |       name        |                               identity                               | ?column? 
 ---------------------------+------------+-------------------+----------------------------------------------------------------------+----------
  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)).*,
  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
+ foreign table column      | addr_nsp   | genftable         | addr_nsp.genftable.a                                                 | t
  role                      |            | regtest_addr_user | regtest_addr_user                                                    | t
  server                    |            | addr_fserv        | addr_fserv                                                           | t
  user mapping              |            |                   | regtest_addr_user on server integer                                  | 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
  cast                      |            |                   | (bigint AS integer)                                                  | 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)).*,
  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
-(38 rows)
+(40 rows)
 
 ---
 --- Cleanup resources
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index a49f03f..9cf8097 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -48,8 +48,7 @@ DECLARE
 	objtype text;
 BEGIN
 	FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
-		('toast table column'), ('view column'), ('materialized view column'),
-		('operator of access method'), ('function of access method')
+		('toast table column'), ('view column'), ('materialized view column')
 	LOOP
 		BEGIN
 			PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -75,7 +74,8 @@ BEGIN
 		('operator'), ('operator class'), ('operator family'), ('rule'), ('trigger'),
 		('text search parser'), ('text search dictionary'),
 		('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
 		FOR names IN VALUES ('{eins}'), ('{addr_nsp, zwei}'), ('{eins, zwei, drei}')
 		LOOP
@@ -141,10 +141,10 @@ WITH objects (type, name, args) AS (VALUES
 				('language', '{plpgsql}', '{}'),
 				-- large object
 				('operator', '{+}', '{int4, int4}'),
-				('operator class', '{int4_ops}', '{btree}'),
-				('operator family', '{integer_ops}', '{btree}'),
-				-- operator of access method
-				-- function of access method
+				('operator class', '{btree, int4_ops}', '{}'),
+				('operator family', '{btree, integer_ops}', '{}'),
+				('operator of access method', '{btree,integer_ops,1}', '{integer,integer}'),
+				('function of access method', '{btree,integer_ops,2}', '{integer,integer}'),
 				('rule', '{addr_nsp, genview, _RETURN}', '{}'),
 				('trigger', '{addr_nsp, gentable, t}', '{}'),
 				('schema', '{addr_nsp}', '{}'),
@@ -171,7 +171,7 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.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;
+	ORDER BY addr1.classid, addr1.objid, addr1.subobjid;
 
 ---
 --- Cleanup resources
-- 
2.1.4

#6Stephen Frost
sfrost@snowman.net
In reply to: Alvaro Herrera (#5)
Re: get_object_address support for additional object types

Alvaro,

* Alvaro Herrera (alvherre@2ndquadrant.com) wrote:

Stephen Frost wrote:

I thought the rest of it looked alright. I agree it's a bit odd how the
opfamily is handled but I agree with your assessment that there's not
much better we can do with this object representation.

Actually, on second thought I revisited this and changed the
representation for opfamilies and opclasses: instead of putting the AM
name in objargs, we can put it as the first element of objname instead.
That way, objargs is unused for opfamilies and opclasses, and we're free
to use it for the type arguments in amops and amprocs. This makes the
lists consistent for the four cases: in objname, amname first, then
qualified opclass/opfamily name. For amop/amproc, the member number
follows. Objargs is unused in opclass/opfamily, and it's a two-element
list of types in amop/amproc.

Agreed, that makes more sense to me also.

The attached patch changes the grammar to comply with the above, and
adds the necessary get_object_address and getObjectIdentityParts support
code for amop/amproc objects.

I took a quick look through and it looked fine to me.

The only thing a bit unusual is that in does_not_exist_skipping() we
need to do an list_copy_tail() to strip out the amname before reporting
the "skipping" message, for DROP OPERATOR CLASS/FAMILY IF NOT EXISTS.
I don't think this is a problem. In return, the code to deconstruct
amop and amproc addresses is more sensible.

Works for me.

Thanks!

Stephen

#7Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Stephen Frost (#6)
Re: get_object_address support for additional object types

Stephen Frost wrote:

Alvaro,

* Alvaro Herrera (alvherre@2ndquadrant.com) wrote:

Stephen Frost wrote:

I thought the rest of it looked alright. I agree it's a bit odd how the
opfamily is handled but I agree with your assessment that there's not
much better we can do with this object representation.

Actually, on second thought I revisited this and changed the
representation for opfamilies and opclasses: instead of putting the AM
name in objargs, we can put it as the first element of objname instead.
That way, objargs is unused for opfamilies and opclasses, and we're free
to use it for the type arguments in amops and amprocs. This makes the
lists consistent for the four cases: in objname, amname first, then
qualified opclass/opfamily name. For amop/amproc, the member number
follows. Objargs is unused in opclass/opfamily, and it's a two-element
list of types in amop/amproc.

Agreed, that makes more sense to me also.

Great, thanks for checking -- pushed that way.

The attached patch changes the grammar to comply with the above, and
adds the necessary get_object_address and getObjectIdentityParts support
code for amop/amproc objects.

I took a quick look through and it looked fine to me.

Actually, there was a bug in the changes of the rule for ALTER EXTENSION
ADD OPERATOR CLASS. I noticed by chance only, and upon testing it
manually I realized I had made a mistake. I then remembered I made the
same bug previously, fixed by 5c5ffee80f35, and I'm not wondering why do
we not have any test for ALTER EXTENSION ADD other than pg_upgrading
some database that contains an extension which uses each command. This
seems pretty dangerous to me, generally speaking ... we should
definitely be testing all these ALTER EXTENSION commands.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#8Stephen Frost
sfrost@snowman.net
In reply to: Alvaro Herrera (#7)
Re: get_object_address support for additional object types

* Alvaro Herrera (alvherre@2ndquadrant.com) wrote:

Actually, there was a bug in the changes of the rule for ALTER EXTENSION
ADD OPERATOR CLASS. I noticed by chance only, and upon testing it
manually I realized I had made a mistake. I then remembered I made the
same bug previously, fixed by 5c5ffee80f35, and I'm not wondering why do
we not have any test for ALTER EXTENSION ADD other than pg_upgrading
some database that contains an extension which uses each command. This
seems pretty dangerous to me, generally speaking ... we should
definitely be testing all these ALTER EXTENSION commands.

It'd certainly be great to have more testing in general, but we're not
going to be able to include complete code coverage tests in the normal
set of regression tests which are run.. What I've been thinking for a
while (probably influenced by other discussion) is that we should have
the buildfarm running tests for code coverage as those are async from
the development process. That isn't to say we shouldn't add more tests
to the main regression suite when it makes sense to, we certainly
should, but we really need to be looking at code coverage tools and
adding tests to improve our test coverage which can be run by the
buildfarm animals (or even just a subset of them, if we feel that having
all the animals running them would be excessive).

Thanks!

Stephen

#9Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Stephen Frost (#8)
Re: get_object_address support for additional object types

Stephen Frost wrote:

It'd certainly be great to have more testing in general, but we're not
going to be able to include complete code coverage tests in the normal
set of regression tests which are run.. What I've been thinking for a
while (probably influenced by other discussion) is that we should have
the buildfarm running tests for code coverage as those are async from
the development process. That isn't to say we shouldn't add more tests
to the main regression suite when it makes sense to, we certainly
should, but we really need to be looking at code coverage tools and
adding tests to improve our test coverage which can be run by the
buildfarm animals (or even just a subset of them, if we feel that having
all the animals running them would be excessive).

Well, we already have make targets for gcov and friends; you get some
HTML charts and marked-up source lines with coverage counts, etc. I
don't think we've made any use of that. It'd be neat to have something
similar to our doxygen service, running some test suite and publishing
the reports on the web. I remember trying to convince someone to set
that up for the community, but that seems to have yield no results.

We had someone else trying to submit patches to improve coverage of the
regression tests, but (probably due to wrong stars alignment) they
started with CREATE DATABASE which made the tests a lot slower, which
got the patches turned down -- the submitter disappeared after that
IIRC, probably discouraged by the lack of results.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#10Stephen Frost
sfrost@snowman.net
In reply to: Alvaro Herrera (#9)
Re: get_object_address support for additional object types

Alvaro,

* Alvaro Herrera (alvherre@2ndquadrant.com) wrote:

Stephen Frost wrote:

It'd certainly be great to have more testing in general, but we're not
going to be able to include complete code coverage tests in the normal
set of regression tests which are run.. What I've been thinking for a
while (probably influenced by other discussion) is that we should have
the buildfarm running tests for code coverage as those are async from
the development process. That isn't to say we shouldn't add more tests
to the main regression suite when it makes sense to, we certainly
should, but we really need to be looking at code coverage tools and
adding tests to improve our test coverage which can be run by the
buildfarm animals (or even just a subset of them, if we feel that having
all the animals running them would be excessive).

Well, we already have make targets for gcov and friends; you get some
HTML charts and marked-up source lines with coverage counts, etc. I
don't think we've made any use of that. It'd be neat to have something
similar to our doxygen service, running some test suite and publishing
the reports on the web. I remember trying to convince someone to set
that up for the community, but that seems to have yield no results.

I don't think we've made use of it either. If the targets/code are
already there to make it happen and it's just a matter of having a
system running which is generating the website then I can probably get
that going. I was under the impression that there was more to it than
that though.

We had someone else trying to submit patches to improve coverage of the
regression tests, but (probably due to wrong stars alignment) they
started with CREATE DATABASE which made the tests a lot slower, which
got the patches turned down -- the submitter disappeared after that
IIRC, probably discouraged by the lack of results.

Agreed, and I think that's unfortunate. It's an area which we could
really improve in and would be a good place for someone new to the
community to be able to contribute- but we need to provide the right way
for those tests to be added and that way isn't to include them in the
main suite of tests which are run during development.

Thanks!

Stephen

#11Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Stephen Frost (#10)
Re: get_object_address support for additional object types

Stephen Frost wrote:

Alvaro,

* Alvaro Herrera (alvherre@2ndquadrant.com) wrote:

Well, we already have make targets for gcov and friends; you get some
HTML charts and marked-up source lines with coverage counts, etc. I
don't think we've made any use of that. It'd be neat to have something
similar to our doxygen service, running some test suite and publishing
the reports on the web. I remember trying to convince someone to set
that up for the community, but that seems to have yield no results.

I don't think we've made use of it either. If the targets/code are
already there to make it happen and it's just a matter of having a
system running which is generating the website then I can probably get
that going. I was under the impression that there was more to it than
that though.

"configure --enable-coverage"; install the resulting tree; then run
whatever tests you want, then "make coverage". That's enough to get the
HTML reports.

We had someone else trying to submit patches to improve coverage of the
regression tests, but (probably due to wrong stars alignment) they
started with CREATE DATABASE which made the tests a lot slower, which
got the patches turned down -- the submitter disappeared after that
IIRC, probably discouraged by the lack of results.

Agreed, and I think that's unfortunate. It's an area which we could
really improve in and would be a good place for someone new to the
community to be able to contribute- but we need to provide the right way
for those tests to be added and that way isn't to include them in the
main suite of tests which are run during development.

Well, I don't disagree that we could do with some tests that are run
additionally to the ones we currently have, but some basic stuff that
takes almost no time to run would be adequate to have in the basic
regression tests. The one I'm thinking is something like generate a
VALUES of all the supported object types, then running
"pg_get_object_address" on them to make sure we support all object types
(or at least that we're aware which object types are not supported.)

For instance, Petr Jelinek's patch to add the TABLESAMPLE clause adds a
new object type which needs support in get_object_address, but I'm not
sure it's added to the stuff in the object_address test. It's a very
easy omission to make.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#12Andres Freund
andres@2ndquadrant.com
In reply to: Alvaro Herrera (#9)
Re: get_object_address support for additional object types

On 2015-03-16 12:50:13 -0300, Alvaro Herrera wrote:

Well, we already have make targets for gcov and friends; you get some
HTML charts and marked-up source lines with coverage counts, etc. I
don't think we've made any use of that. It'd be neat to have something
similar to our doxygen service, running some test suite and publishing
the reports on the web. I remember trying to convince someone to set
that up for the community, but that seems to have yield no results.

Actually I think Peter E. has:
http://pgci.eisentraut.org/jenkins/job/postgresql_master_coverage/Coverage/

We had someone else trying to submit patches to improve coverage of the
regression tests, but (probably due to wrong stars alignment) they
started with CREATE DATABASE which made the tests a lot slower, which
got the patches turned down -- the submitter disappeared after that
IIRC, probably discouraged by the lack of results.

I seem to recall that he'd also submitted a bunch of other things, and
that some of it was applied?

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#13Stephen Frost
sfrost@snowman.net
In reply to: Alvaro Herrera (#11)
Re: get_object_address support for additional object types

Alvaro,

* Alvaro Herrera (alvherre@2ndquadrant.com) wrote:

Stephen Frost wrote:

I don't think we've made use of it either. If the targets/code are
already there to make it happen and it's just a matter of having a
system running which is generating the website then I can probably get
that going. I was under the impression that there was more to it than
that though.

"configure --enable-coverage"; install the resulting tree; then run
whatever tests you want, then "make coverage". That's enough to get the
HTML reports.

Ok, cool, I'll take a look at that.

We had someone else trying to submit patches to improve coverage of the
regression tests, but (probably due to wrong stars alignment) they
started with CREATE DATABASE which made the tests a lot slower, which
got the patches turned down -- the submitter disappeared after that
IIRC, probably discouraged by the lack of results.

Agreed, and I think that's unfortunate. It's an area which we could
really improve in and would be a good place for someone new to the
community to be able to contribute- but we need to provide the right way
for those tests to be added and that way isn't to include them in the
main suite of tests which are run during development.

Well, I don't disagree that we could do with some tests that are run
additionally to the ones we currently have, but some basic stuff that
takes almost no time to run would be adequate to have in the basic
regression tests. The one I'm thinking is something like generate a
VALUES of all the supported object types, then running
"pg_get_object_address" on them to make sure we support all object types
(or at least that we're aware which object types are not supported.)

Agreed, that sounds perfectly reasonable to me. I didn't mean to imply
that we shouldn't add tests where they make sense, including to the core
regression suites (particularly coverage tests like that one you're
suggesting here), just pointing out that we need a way to address code
coverage based tested also which is done outside of the main regression
suite.

For instance, Petr Jelinek's patch to add the TABLESAMPLE clause adds a
new object type which needs support in get_object_address, but I'm not
sure it's added to the stuff in the object_address test. It's a very
easy omission to make.

Agreed.

Thanks!

Stephen

#14Stephen Frost
sfrost@snowman.net
In reply to: Andres Freund (#12)
Re: get_object_address support for additional object types

* Andres Freund (andres@2ndquadrant.com) wrote:

On 2015-03-16 12:50:13 -0300, Alvaro Herrera wrote:

Well, we already have make targets for gcov and friends; you get some
HTML charts and marked-up source lines with coverage counts, etc. I
don't think we've made any use of that. It'd be neat to have something
similar to our doxygen service, running some test suite and publishing
the reports on the web. I remember trying to convince someone to set
that up for the community, but that seems to have yield no results.

Actually I think Peter E. has:
http://pgci.eisentraut.org/jenkins/job/postgresql_master_coverage/Coverage/

Neat!

Thanks,

Stephen

#15Dean Rasheed
dean.a.rasheed@gmail.com
In reply to: Alvaro Herrera (#7)
Re: get_object_address support for additional object types

On 16 March 2015 at 15:11, Alvaro Herrera <alvherre@2ndquadrant.com> wrote:

Actually, on second thought I revisited this and changed the
representation for opfamilies and opclasses: instead of putting the AM
name in objargs, we can put it as the first element of objname instead.
That way, objargs is unused for opfamilies and opclasses, and we're free
to use it for the type arguments in amops and amprocs. This makes the
lists consistent for the four cases: in objname, amname first, then
qualified opclass/opfamily name. For amop/amproc, the member number
follows. Objargs is unused in opclass/opfamily, and it's a two-element
list of types in amop/amproc.

Agreed, that makes more sense to me also.

Great, thanks for checking -- pushed that way.

I'm getting a couple of compiler warnings that I think are coming from
this commit:

objectaddress.c: In function ‘get_object_address’:
objectaddress.c:1428:12: warning: array subscript is above array bounds
objectaddress.c:1430:11: warning: array subscript is above array bounds

I think because the compiler doesn't know the list size, so i could be 0,1,2.

Regards,
Dean

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#16Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Dean Rasheed (#15)
Re: [HACKERS] get_object_address support for additional object types

I pushed a fix for this.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-committers mailing list (pgsql-committers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-committers