Here's a five-part split of the remaining pieces of this patch.

Patch 0001 is the one I posted in 
http://www.postgresql.org/message-id/20141220022308.gy1...@alvh.no-ip.org
which adds support for COMMENT ON CONSTRAINT .. ON DOMAIN.  This just
splits OBJECT_CONSTRAINT in OBJECT_TABCONSTRAINT and
OBJECT_DOMCONSTRAINT.  It includes \dd support and pg_dump support for
comments on domain constraint comments.

I intend to commit this one first thing tomorrow.

Patch 0002 adds OBJECT_DEFAULT support.  This is not needed currently,
so there's no bug being fixed; we just need it if we want to use
get_object_address in a way different from currently.

Patch 0003 adds an (unused) table and routine to map the strings
returned by getObjectTypeDescription into enum ObjectType, for use of
0004.  It also splits a part of parseTypeString into a new function
typeStringToTypeName(), for use of 0004.

Patch 0004 adds a SQL-callable interface to get_object_address,
imaginatively called pg_get_object_address; this uses the stuff in patch
0003.  It includes a simple regression test.  The code that prepares
from text arrays into the appropriate List structure is messy because it
needs to mimic parser output.

I intend to push these three patches as a single commit tomorrow.

Patch 0005 adds getObjectIdentityParts(), which returns the object
identity in arrays that can be passed to pg_get_object_address.  This
part needs slight revisions so I'm not sure I will be able to push
tomorrow.

-- 
Álvaro Herrera                http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
>From f5a324e864baf60df989e313740744732c04404d Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvhe...@alvh.no-ip.org>
Date: Fri, 19 Dec 2014 17:03:29 -0300
Subject: [PATCH 1/5] Distinguish domain constraint from table constraints

---
 doc/src/sgml/ref/comment.sgml              | 14 ++++++++++++++
 src/backend/catalog/objectaddress.c        | 26 ++++++++++++++++++++++----
 src/backend/commands/alter.c               |  3 ++-
 src/backend/commands/event_trigger.c       |  3 ++-
 src/backend/commands/tablecmds.c           |  2 +-
 src/backend/parser/gram.y                  | 18 +++++++++++++-----
 src/backend/parser/parse_utilcmd.c         |  2 +-
 src/backend/tcop/utility.c                 |  5 ++---
 src/bin/pg_dump/pg_dump.c                  | 17 +++++++++++++++++
 src/bin/psql/describe.c                    | 27 +++++++++++++++++++++++++--
 src/include/nodes/parsenodes.h             |  3 ++-
 src/test/regress/input/constraints.source  | 21 +++++++++++++++++++++
 src/test/regress/output/constraints.source | 19 +++++++++++++++++++
 13 files changed, 141 insertions(+), 19 deletions(-)

diff --git a/doc/src/sgml/ref/comment.sgml b/doc/src/sgml/ref/comment.sgml
index 36a7312..62e1968 100644
--- a/doc/src/sgml/ref/comment.sgml
+++ b/doc/src/sgml/ref/comment.sgml
@@ -28,6 +28,7 @@ COMMENT ON
   COLLATION <replaceable class="PARAMETER">object_name</replaceable> |
   COLUMN <replaceable class="PARAMETER">relation_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> |
   CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ON <replaceable class="PARAMETER">table_name</replaceable> |
+  CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ON DOMAIN <replaceable class="PARAMETER">domain_name</replaceable> |
   CONVERSION <replaceable class="PARAMETER">object_name</replaceable> |
   DATABASE <replaceable class="PARAMETER">object_name</replaceable> |
   DOMAIN <replaceable class="PARAMETER">object_name</replaceable> |
@@ -127,6 +128,18 @@ COMMENT ON
    </varlistentry>
 
    <varlistentry>
+    <term><replaceable class="parameter">table_name</replaceable></term>
+    <term><replaceable class="parameter">domain_name</replaceable></term>
+    <listitem>
+     <para>
+      When creating a comment on a constraint on a table or a domain, these
+      parameteres specify the name of the table or domain on which the
+      constraint is defined.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
      <term><replaceable>source_type</replaceable></term>
      <listitem>
       <para>
@@ -266,6 +279,7 @@ COMMENT ON COLLATION "fr_CA" IS 'Canadian French';
 COMMENT ON COLUMN my_table.my_column IS 'Employee ID number';
 COMMENT ON CONVERSION my_conv IS 'Conversion to UTF8';
 COMMENT ON CONSTRAINT bar_col_cons ON bar IS 'Constrains column col';
+COMMENT ON CONSTRAINT dom_col_constr ON DOMAIN dom IS 'Constrains col of domain';
 COMMENT ON DATABASE my_database IS 'Development Database';
 COMMENT ON DOMAIN my_domain IS 'Email Address Domain';
 COMMENT ON EXTENSION hstore IS 'implements the hstore data type';
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index e261307..297deb5 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -530,11 +530,28 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
 				break;
 			case OBJECT_RULE:
 			case OBJECT_TRIGGER:
-			case OBJECT_CONSTRAINT:
+			case OBJECT_TABCONSTRAINT:
 			case OBJECT_POLICY:
 				address = get_object_address_relobject(objtype, objname,
 													   &relation, missing_ok);
 				break;
+			case OBJECT_DOMCONSTRAINT:
+				{
+					List		   *domname;
+					ObjectAddress	domaddr;
+					char		   *constrname;
+
+					domname = list_truncate(list_copy(objname), list_length(objname) - 1);
+					constrname = strVal(llast(objname));
+					domaddr = get_object_address_type(OBJECT_DOMAIN, domname, missing_ok);
+
+					address.classId = ConstraintRelationId;
+					address.objectId = get_domain_constraint_oid(domaddr.objectId,
+															  constrname, missing_ok);
+					address.objectSubId = 0;
+
+				}
+				break;
 			case OBJECT_DATABASE:
 			case OBJECT_EXTENSION:
 			case OBJECT_TABLESPACE:
@@ -934,7 +951,7 @@ get_object_address_relobject(ObjectType objtype, List *objname,
 	const char *depname;
 
 	/* Extract name of dependent object. */
-	depname = strVal(lfirst(list_tail(objname)));
+	depname = strVal(llast(objname));
 
 	/* Separate relation name from dependent object name. */
 	nnames = list_length(objname);
@@ -990,7 +1007,7 @@ get_object_address_relobject(ObjectType objtype, List *objname,
 					get_trigger_oid(reloid, depname, missing_ok) : InvalidOid;
 				address.objectSubId = 0;
 				break;
-			case OBJECT_CONSTRAINT:
+			case OBJECT_TABCONSTRAINT:
 				address.classId = ConstraintRelationId;
 				address.objectId = relation ?
 					get_relation_constraint_oid(reloid, depname, missing_ok) :
@@ -1178,7 +1195,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
 		case OBJECT_RULE:
 		case OBJECT_TRIGGER:
 		case OBJECT_POLICY:
-		case OBJECT_CONSTRAINT:
+		case OBJECT_TABCONSTRAINT:
 			if (!pg_class_ownercheck(RelationGetRelid(relation), roleid))
 				aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
 							   RelationGetRelationName(relation));
@@ -1191,6 +1208,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
 		case OBJECT_TYPE:
 		case OBJECT_DOMAIN:
 		case OBJECT_ATTRIBUTE:
+		case OBJECT_DOMCONSTRAINT:
 			if (!pg_type_ownercheck(address.objectId, roleid))
 				aclcheck_error_type(ACLCHECK_NOT_OWNER, address.objectId);
 			break;
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index c9a9baf..e7f4ef3 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -305,7 +305,8 @@ ExecRenameStmt(RenameStmt *stmt)
 {
 	switch (stmt->renameType)
 	{
-		case OBJECT_CONSTRAINT:
+		case OBJECT_TABCONSTRAINT:
+		case OBJECT_DOMCONSTRAINT:
 			return RenameConstraint(stmt);
 
 		case OBJECT_DATABASE:
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 8b88ecb..6bdb774 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1053,10 +1053,10 @@ EventTriggerSupportsObjectType(ObjectType obtype)
 		case OBJECT_ATTRIBUTE:
 		case OBJECT_CAST:
 		case OBJECT_COLUMN:
-		case OBJECT_CONSTRAINT:
 		case OBJECT_COLLATION:
 		case OBJECT_CONVERSION:
 		case OBJECT_DOMAIN:
+		case OBJECT_DOMCONSTRAINT:
 		case OBJECT_EXTENSION:
 		case OBJECT_FDW:
 		case OBJECT_FOREIGN_SERVER:
@@ -1073,6 +1073,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
 		case OBJECT_RULE:
 		case OBJECT_SCHEMA:
 		case OBJECT_SEQUENCE:
+		case OBJECT_TABCONSTRAINT:
 		case OBJECT_TABLE:
 		case OBJECT_TRIGGER:
 		case OBJECT_TSCONFIGURATION:
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 81c5ab2..3c0cdea 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -2457,7 +2457,7 @@ RenameConstraint(RenameStmt *stmt)
 	Oid			relid = InvalidOid;
 	Oid			typid = InvalidOid;
 
-	if (stmt->relationType == OBJECT_DOMAIN)
+	if (stmt->renameType == OBJECT_DOMCONSTRAINT)
 	{
 		Relation	rel;
 		HeapTuple	tup;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 1f4fe9d..6431601 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -5572,6 +5572,7 @@ opt_restart_seqs:
  *				 CAST (<src type> AS <dst type>) |
  *				 COLUMN <relname>.<colname> |
  *				 CONSTRAINT <constraintname> ON <relname> |
+ *				 CONSTRAINT <constraintname> ON DOMAIN <domainname> |
  *				 FUNCTION <funcname> (arg1, arg2, ...) |
  *				 LARGE OBJECT <oid> |
  *				 OPERATOR <op> (leftoperand_typ, rightoperand_typ) |
@@ -5623,12 +5624,21 @@ CommentStmt:
 			| COMMENT ON CONSTRAINT name ON any_name IS comment_text
 				{
 					CommentStmt *n = makeNode(CommentStmt);
-					n->objtype = OBJECT_CONSTRAINT;
+					n->objtype = OBJECT_TABCONSTRAINT;
 					n->objname = lappend($6, makeString($4));
 					n->objargs = NIL;
 					n->comment = $8;
 					$$ = (Node *) n;
 				}
+			| COMMENT ON CONSTRAINT name ON DOMAIN_P any_name IS comment_text
+				{
+					CommentStmt *n = makeNode(CommentStmt);
+					n->objtype = OBJECT_DOMCONSTRAINT;
+					n->objname = lappend($7, makeString($4));
+					n->objargs = NIL;
+					n->comment = $9;
+					$$ = (Node *) n;
+				}
 			| COMMENT ON POLICY name ON any_name IS comment_text
 				{
 					CommentStmt *n = makeNode(CommentStmt);
@@ -7355,8 +7365,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
 			| ALTER DOMAIN_P any_name RENAME CONSTRAINT name TO name
 				{
 					RenameStmt *n = makeNode(RenameStmt);
-					n->renameType = OBJECT_CONSTRAINT;
-					n->relationType = OBJECT_DOMAIN;
+					n->renameType = OBJECT_DOMCONSTRAINT;
 					n->object = $3;
 					n->subname = $6;
 					n->newname = $8;
@@ -7624,8 +7633,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
 			| ALTER TABLE relation_expr RENAME CONSTRAINT name TO name
 				{
 					RenameStmt *n = makeNode(RenameStmt);
-					n->renameType = OBJECT_CONSTRAINT;
-					n->relationType = OBJECT_TABLE;
+					n->renameType = OBJECT_TABCONSTRAINT;
 					n->relation = $3;
 					n->subname = $6;
 					n->newname = $8;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index b9fbb5b..a85327d 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -896,7 +896,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
 			{
 				CommentStmt *stmt = makeNode(CommentStmt);
 
-				stmt->objtype = OBJECT_CONSTRAINT;
+				stmt->objtype = OBJECT_TABCONSTRAINT;
 				stmt->objname = list_make3(makeString(cxt->relation->schemaname),
 										   makeString(cxt->relation->relname),
 										   makeString(n->conname));
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index aa8fe88..71580e8 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1589,9 +1589,6 @@ AlterObjectTypeCommandTag(ObjectType objtype)
 		case OBJECT_COLUMN:
 			tag = "ALTER TABLE";
 			break;
-		case OBJECT_CONSTRAINT:
-			tag = "ALTER TABLE";
-			break;
 		case OBJECT_CONVERSION:
 			tag = "ALTER CONVERSION";
 			break;
@@ -1599,6 +1596,7 @@ AlterObjectTypeCommandTag(ObjectType objtype)
 			tag = "ALTER DATABASE";
 			break;
 		case OBJECT_DOMAIN:
+		case OBJECT_DOMCONSTRAINT:
 			tag = "ALTER DOMAIN";
 			break;
 		case OBJECT_EXTENSION:
@@ -1650,6 +1648,7 @@ AlterObjectTypeCommandTag(ObjectType objtype)
 			tag = "ALTER SEQUENCE";
 			break;
 		case OBJECT_TABLE:
+		case OBJECT_TABCONSTRAINT:
 			tag = "ALTER TABLE";
 			break;
 		case OBJECT_TABLESPACE:
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 4175ddc..6658fda 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -9261,6 +9261,23 @@ dumpDomain(Archive *fout, DumpOptions *dopt, TypeInfo *tyinfo)
 			tyinfo->dobj.namespace->dobj.name,
 			tyinfo->rolname, tyinfo->typacl);
 
+	/* Dump any per-constraint comments */
+	for (i = 0; i < tyinfo->nDomChecks; i++)
+	{
+		ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
+		PQExpBuffer	labelq = createPQExpBuffer();
+
+		appendPQExpBuffer(labelq, "CONSTRAINT %s ",
+						  fmtId(domcheck->dobj.name));
+		appendPQExpBuffer(labelq, "ON DOMAIN %s",
+						  fmtId(qtypname));
+		dumpComment(fout, dopt, labelq->data,
+					tyinfo->dobj.namespace->dobj.name,
+					tyinfo->rolname,
+					domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
+		destroyPQExpBuffer(labelq);
+	}
+
 	destroyPQExpBuffer(q);
 	destroyPQExpBuffer(delq);
 	destroyPQExpBuffer(labelq);
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 5a9ceca..f2d3325 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -952,7 +952,7 @@ objectDescription(const char *pattern, bool showSystem)
 					  gettext_noop("Object"),
 					  gettext_noop("Description"));
 
-	/* Constraint descriptions */
+	/* Table constraint descriptions */
 	appendPQExpBuffer(&buf,
 					  "  SELECT pgc.oid as oid, pgc.tableoid AS tableoid,\n"
 					  "  n.nspname as nspname,\n"
@@ -963,7 +963,7 @@ objectDescription(const char *pattern, bool showSystem)
 					  "ON c.oid = pgc.conrelid\n"
 					  "    LEFT JOIN pg_catalog.pg_namespace n "
 					  "    ON n.oid = c.relnamespace\n",
-					  gettext_noop("constraint"));
+					  gettext_noop("table constraint"));
 
 	if (!showSystem && !pattern)
 		appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
@@ -973,6 +973,29 @@ objectDescription(const char *pattern, bool showSystem)
 						  false, "n.nspname", "pgc.conname", NULL,
 						  "pg_catalog.pg_table_is_visible(c.oid)");
 
+	/* Domain constraint descriptions */
+	appendPQExpBuffer(&buf,
+					  "UNION ALL\n"
+					  "  SELECT pgc.oid as oid, pgc.tableoid AS tableoid,\n"
+					  "  n.nspname as nspname,\n"
+					  "  CAST(pgc.conname AS pg_catalog.text) as name,"
+					  "  CAST('%s' AS pg_catalog.text) as object\n"
+					  "  FROM pg_catalog.pg_constraint pgc\n"
+					  "    JOIN pg_catalog.pg_type t "
+					  "ON t.oid = pgc.contypid\n"
+					  "    LEFT JOIN pg_catalog.pg_namespace n "
+					  "    ON n.oid = t.typnamespace\n",
+					  gettext_noop("domain constraint"));
+
+	if (!showSystem && !pattern)
+		appendPQExpBufferStr(&buf, "WHERE n.nspname <> 'pg_catalog'\n"
+							 "      AND n.nspname <> 'information_schema'\n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, !showSystem && !pattern,
+						  false, "n.nspname", "pgc.conname", NULL,
+						  "pg_catalog.pg_type_is_visible(t.oid)");
+
+
 	/*
 	 * pg_opclass.opcmethod only available in 8.3+
 	 */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 458eeb0..64508f0 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1208,11 +1208,11 @@ typedef enum ObjectType
 	OBJECT_ATTRIBUTE,			/* type's attribute, when distinct from column */
 	OBJECT_CAST,
 	OBJECT_COLUMN,
-	OBJECT_CONSTRAINT,
 	OBJECT_COLLATION,
 	OBJECT_CONVERSION,
 	OBJECT_DATABASE,
 	OBJECT_DOMAIN,
+	OBJECT_DOMCONSTRAINT,
 	OBJECT_EVENT_TRIGGER,
 	OBJECT_EXTENSION,
 	OBJECT_FDW,
@@ -1231,6 +1231,7 @@ typedef enum ObjectType
 	OBJECT_RULE,
 	OBJECT_SCHEMA,
 	OBJECT_SEQUENCE,
+	OBJECT_TABCONSTRAINT,
 	OBJECT_TABLE,
 	OBJECT_TABLESPACE,
 	OBJECT_TRIGGER,
diff --git a/src/test/regress/input/constraints.source b/src/test/regress/input/constraints.source
index 16d38f6..8ec0054 100644
--- a/src/test/regress/input/constraints.source
+++ b/src/test/regress/input/constraints.source
@@ -478,3 +478,24 @@ UPDATE deferred_excl SET f1 = 3;
 ALTER TABLE deferred_excl ADD EXCLUDE (f1 WITH =);
 
 DROP TABLE deferred_excl;
+
+-- Comments
+CREATE TABLE constraint_comments_tbl (a int CONSTRAINT the_constraint CHECK (a > 0));
+CREATE DOMAIN constraint_comments_dom AS int CONSTRAINT the_constraint CHECK (value > 0);
+
+COMMENT ON CONSTRAINT the_constraint ON constraint_comments_tbl IS 'yes, the comment';
+COMMENT ON CONSTRAINT the_constraint ON DOMAIN constraint_comments_dom IS 'yes, another comment';
+
+-- no such constraint
+COMMENT ON CONSTRAINT no_constraint ON constraint_comments_tbl IS 'yes, the comment';
+COMMENT ON CONSTRAINT no_constraint ON DOMAIN constraint_comments_dom IS 'yes, another comment';
+
+-- no such table/domain
+COMMENT ON CONSTRAINT the_constraint ON no_comments_tbl IS 'bad comment';
+COMMENT ON CONSTRAINT the_constraint ON DOMAIN no_comments_dom IS 'another bad comment';
+
+COMMENT ON CONSTRAINT the_constraint ON constraint_comments_tbl IS NULL;
+COMMENT ON CONSTRAINT the_constraint ON DOMAIN constraint_comments_dom IS NULL;
+
+DROP TABLE constraint_comments_tbl;
+DROP DOMAIN constraint_comments_dom;
diff --git a/src/test/regress/output/constraints.source b/src/test/regress/output/constraints.source
index 2ffd263..0d32a9e 100644
--- a/src/test/regress/output/constraints.source
+++ b/src/test/regress/output/constraints.source
@@ -645,3 +645,22 @@ ALTER TABLE deferred_excl ADD EXCLUDE (f1 WITH =);
 ERROR:  could not create exclusion constraint "deferred_excl_f1_excl"
 DETAIL:  Key (f1)=(3) conflicts with key (f1)=(3).
 DROP TABLE deferred_excl;
+-- Comments
+CREATE TABLE constraint_comments_tbl (a int CONSTRAINT the_constraint CHECK (a > 0));
+CREATE DOMAIN constraint_comments_dom AS int CONSTRAINT the_constraint CHECK (value > 0);
+COMMENT ON CONSTRAINT the_constraint ON constraint_comments_tbl IS 'yes, the comment';
+COMMENT ON CONSTRAINT the_constraint ON DOMAIN constraint_comments_dom IS 'yes, another comment';
+-- no such constraint
+COMMENT ON CONSTRAINT no_constraint ON constraint_comments_tbl IS 'yes, the comment';
+ERROR:  constraint "no_constraint" for table "constraint_comments_tbl" does not exist
+COMMENT ON CONSTRAINT no_constraint ON DOMAIN constraint_comments_dom IS 'yes, another comment';
+ERROR:  constraint "no_constraint" for domain "constraint_comments_dom" does not exist
+-- no such table/domain
+COMMENT ON CONSTRAINT the_constraint ON no_comments_tbl IS 'bad comment';
+ERROR:  relation "no_comments_tbl" does not exist
+COMMENT ON CONSTRAINT the_constraint ON DOMAIN no_comments_dom IS 'another bad comment';
+ERROR:  type "no_comments_dom" does not exist
+COMMENT ON CONSTRAINT the_constraint ON constraint_comments_tbl IS NULL;
+COMMENT ON CONSTRAINT the_constraint ON DOMAIN constraint_comments_dom IS NULL;
+DROP TABLE constraint_comments_tbl;
+DROP DOMAIN constraint_comments_dom;
-- 
2.1.3

>From 92562fa5cd116e1a11cc93d275ff6079639d4cf4 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvhe...@alvh.no-ip.org>
Date: Wed, 15 Oct 2014 18:02:45 -0300
Subject: [PATCH 2/5] add support for OBJECT_DEFAULT in get_object_address

---
 src/backend/catalog/objectaddress.c  | 91 ++++++++++++++++++++++++++++++++++++
 src/backend/commands/event_trigger.c |  1 +
 src/include/nodes/parsenodes.h       |  1 +
 3 files changed, 93 insertions(+)

diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 297deb5..7a0b24b 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -441,6 +441,9 @@ static ObjectAddress get_object_address_relobject(ObjectType objtype,
 static ObjectAddress get_object_address_attribute(ObjectType objtype,
 							 List *objname, Relation *relp,
 							 LOCKMODE lockmode, bool missing_ok);
+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);
 static ObjectAddress get_object_address_opcf(ObjectType objtype, List *objname,
@@ -528,6 +531,12 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
 												 &relation, lockmode,
 												 missing_ok);
 				break;
+			case OBJECT_DEFAULT:
+				address =
+					get_object_address_attrdef(objtype, objname,
+											   &relation, lockmode,
+											   missing_ok);
+				break;
 			case OBJECT_RULE:
 			case OBJECT_TRIGGER:
 			case OBJECT_TABCONSTRAINT:
@@ -1097,6 +1106,88 @@ get_object_address_attribute(ObjectType objtype, List *objname,
 }
 
 /*
+ * Find the ObjectAddress for an attribute's default value.
+ */
+static ObjectAddress
+get_object_address_attrdef(ObjectType objtype, List *objname,
+						   Relation *relp, LOCKMODE lockmode,
+						   bool missing_ok)
+{
+	ObjectAddress address;
+	List	   *relname;
+	Oid			reloid;
+	Relation	relation;
+	const char *attname;
+	AttrNumber	attnum;
+	TupleDesc	tupdesc;
+	Oid			defoid;
+
+	/* Extract relation name and open relation. */
+	if (list_length(objname) < 2)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("column name must be qualified")));
+	attname = strVal(llast(objname));
+	relname = list_truncate(list_copy(objname), list_length(objname) - 1);
+	/* XXX no missing_ok support here */
+	relation = relation_openrv(makeRangeVarFromNameList(relname), lockmode);
+	reloid = RelationGetRelid(relation);
+
+	tupdesc = RelationGetDescr(relation);
+
+	/* Look up attribute number and scan pg_attrdef to find its tuple */
+	attnum = get_attnum(reloid, attname);
+	defoid = InvalidOid;
+	if (attnum != InvalidAttrNumber && tupdesc->constr != NULL)
+	{
+		Relation	attrdef;
+		ScanKeyData	keys[2];
+		SysScanDesc	scan;
+		HeapTuple	tup;
+
+		attrdef = relation_open(AttrDefaultRelationId, AccessShareLock);
+		ScanKeyInit(&keys[0],
+					Anum_pg_attrdef_adrelid,
+					BTEqualStrategyNumber,
+					F_OIDEQ,
+					ObjectIdGetDatum(reloid));
+		ScanKeyInit(&keys[1],
+					Anum_pg_attrdef_adnum,
+					BTEqualStrategyNumber,
+					F_INT2EQ,
+					Int16GetDatum(attnum));
+		scan = systable_beginscan(attrdef, AttrDefaultIndexId, true,
+								  NULL, 2, keys);
+		if (HeapTupleIsValid(tup = systable_getnext(scan)))
+			defoid = HeapTupleGetOid(tup);
+
+		systable_endscan(scan);
+		relation_close(attrdef, AccessShareLock);
+	}
+	if (!OidIsValid(defoid))
+	{
+		if (!missing_ok)
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_COLUMN),
+					 errmsg("default value for column \"%s\" of relation \"%s\" does not exist",
+							attname, NameListToString(relname))));
+
+		address.classId = AttrDefaultRelationId;
+		address.objectId = InvalidOid;
+		address.objectSubId = InvalidAttrNumber;
+		relation_close(relation, lockmode);
+		return address;
+	}
+
+	address.classId = AttrDefaultRelationId;
+	address.objectId = defoid;
+	address.objectSubId = 0;
+
+	*relp = relation;
+	return address;
+}
+
+/*
  * Find the ObjectAddress for a type or domain
  */
 static ObjectAddress
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 6bdb774..34dd3c0 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1055,6 +1055,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
 		case OBJECT_COLUMN:
 		case OBJECT_COLLATION:
 		case OBJECT_CONVERSION:
+		case OBJECT_DEFAULT:
 		case OBJECT_DOMAIN:
 		case OBJECT_DOMCONSTRAINT:
 		case OBJECT_EXTENSION:
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 64508f0..35e7b28 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1211,6 +1211,7 @@ typedef enum ObjectType
 	OBJECT_COLLATION,
 	OBJECT_CONVERSION,
 	OBJECT_DATABASE,
+	OBJECT_DEFAULT,
 	OBJECT_DOMAIN,
 	OBJECT_DOMCONSTRAINT,
 	OBJECT_EVENT_TRIGGER,
-- 
2.1.3

>From 44f8e1c391797edda47f64904b89e5f7a1cdb1b9 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvhe...@alvh.no-ip.org>
Date: Mon, 22 Dec 2014 18:32:28 -0300
Subject: [PATCH 3/5] add object type map stuff

---
 src/backend/catalog/objectaddress.c | 130 ++++++++++++++++++++++++++++++++++++
 src/backend/parser/parse_type.c     |  46 ++++++++-----
 src/include/catalog/objectaddress.h |   1 +
 src/include/parser/parse_type.h     |   1 +
 4 files changed, 162 insertions(+), 16 deletions(-)

diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 7a0b24b..59431a9 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -431,6 +431,106 @@ static const ObjectPropertyType ObjectProperty[] =
 	}
 };
 
+/*
+ * This struct maps the string object types as returned by
+ * getObjectTypeDescription into ObjType enum values.  Note that some enum
+ * values can be obtained by different names, and that some string object types
+ * do not have corresponding values in the output enum.  The user of this map
+ * must be careful to test for invalid values being returned.
+ *
+ * To ease maintenance, this follows the order of getObjectTypeDescription.
+ */
+static const struct object_type_map
+{
+	const char *tm_name;
+	ObjectType	tm_type;
+}
+ObjectTypeMap[] =
+{
+	/* OCLASS_CLASS, all kinds of relations */
+	{ "table", OBJECT_TABLE },
+	{ "index", OBJECT_INDEX },
+	{ "sequence", OBJECT_SEQUENCE },
+	{ "toast table", -1 },		/* unmapped */
+	{ "view", OBJECT_VIEW },
+	{ "materialized view", OBJECT_MATVIEW },
+	{ "composite type", -1 },	/* unmapped */
+	{ "foreign table", OBJECT_FOREIGN_TABLE },
+	{ "table column", OBJECT_COLUMN },
+	{ "index column", -1 },		/* unmapped */
+	{ "sequence column", -1 },	/* unmapped */
+	{ "toast table column", -1 },	/* unmapped */
+	{ "view column", -1 },		/* unmapped */
+	{ "materialized view column", -1 },	/* unmapped */
+	{ "composite type column", -1 },	/* unmapped */
+	{ "foreign table column", OBJECT_COLUMN },
+	/* OCLASS_PROC */
+	{ "aggregate", OBJECT_AGGREGATE },
+	{ "function", OBJECT_FUNCTION },
+	/* OCLASS_TYPE */
+	{ "type", OBJECT_TYPE },
+	/* OCLASS_CAST */
+	{ "cast", OBJECT_CAST },
+	/* OCLASS_COLLATION */
+	{ "collation", OBJECT_COLLATION },
+	/* OCLASS_CONSTRAINT */
+	{ "table constraint", OBJECT_TABCONSTRAINT },
+	{ "domain constraint", OBJECT_DOMCONSTRAINT },
+	/* OCLASS_CONVERSION */
+	{ "conversion", OBJECT_CONVERSION },
+	/* OCLASS_DEFAULT */
+	{ "default value", OBJECT_DEFAULT },
+	/* OCLASS_LANGUAGE */
+	{ "language", OBJECT_LANGUAGE },
+	/* OCLASS_LARGEOBJECT */
+	{ "large object", OBJECT_LARGEOBJECT },
+	/* OCLASS_OPERATOR */
+	{ "operator", OBJECT_OPERATOR },
+	/* OCLASS_OPCLASS */
+	{ "operator class", OBJECT_OPCLASS },
+	/* OCLASS_OPFAMILY */
+	{ "operator family", OBJECT_OPFAMILY },
+	/* OCLASS_AMOP */
+	{ "operator of access method", -1 },	/* unmapped */
+	/* OCLASS_AMPROC */
+	{ "function of access method", -1 },	/* unmapped */
+	/* OCLASS_REWRITE */
+	{ "rule", OBJECT_RULE },
+	/* OCLASS_TRIGGER */
+	{ "trigger", OBJECT_TRIGGER },
+	/* OCLASS_SCHEMA */
+	{ "schema", OBJECT_SCHEMA },
+	/* OCLASS_TSPARSER */
+	{ "text search parser", OBJECT_TSPARSER },
+	/* OCLASS_TSDICT */
+	{ "text search dictionary", OBJECT_TSDICTIONARY },
+	/* OCLASS_TSTEMPLATE */
+	{ "text search template", OBJECT_TSTEMPLATE },
+	/* OCLASS_TSCONFIG */
+	{ "text search configuration", OBJECT_TSCONFIGURATION },
+	/* OCLASS_ROLE */
+	{ "role", OBJECT_ROLE },
+	/* OCLASS_DATABASE */
+	{ "database", OBJECT_DATABASE },
+	/* OCLASS_TBLSPACE */
+	{ "tablespace", OBJECT_TABLESPACE },
+	/* OCLASS_FDW */
+	{ "foreign-data wrapper", OBJECT_FDW },
+	/* OCLASS_FOREIGN_SERVER */
+	{ "server", OBJECT_FOREIGN_SERVER },
+	/* OCLASS_USER_MAPPING */
+	{ "user mapping", -1 },		/* unmapped */
+	/* OCLASS_DEFACL */
+	{ "default acl", -1 },		/* unmapped */
+	/* OCLASS_EXTENSION */
+	{ "extension", OBJECT_EXTENSION },
+	/* OCLASS_EVENT_TRIGGER */
+	{ "event trigger", OBJECT_EVENT_TRIGGER },
+	/* OCLASS_POLICY */
+	{ "policy", OBJECT_POLICY }
+};
+
+
 static ObjectAddress get_object_address_unqualified(ObjectType objtype,
 							   List *qualname, bool missing_ok);
 static ObjectAddress get_relation_by_qualified_name(ObjectType objtype,
@@ -1479,6 +1579,34 @@ get_object_namespace(const ObjectAddress *address)
 }
 
 /*
+ * Return ObjectType for the given object type as given by
+ * getObjectTypeDescription; if no valid ObjectType code exists, but it's a
+ * possible output type from getObjectTypeDescription, return -1.
+ * Otherwise, an error is thrown.
+ */
+int
+unstringify_objtype(const char *objtype)
+{
+	ObjectType	type;
+	int			i;
+
+	for (i = 0; i < lengthof(ObjectTypeMap); i++)
+	{
+		if (strcmp(ObjectTypeMap[i].tm_name, objtype) == 0)
+		{
+			type = ObjectTypeMap[i].tm_type;
+			break;
+		}
+	}
+	if (i >= lengthof(ObjectTypeMap))
+		ereport(ERROR,
+			   (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				errmsg("unrecognized object type \"%s\"", objtype)));
+
+	return type;
+}
+
+/*
  * Interfaces to reference fields of ObjectPropertyType
  */
 Oid
@@ -2609,6 +2737,8 @@ pg_identify_object(PG_FUNCTION_ARGS)
 /*
  * Return a palloc'ed string that describes the type of object that the
  * passed address is for.
+ *
+ * Keep ObjectTypeMap in sync with this.
  */
 char *
 getObjectTypeDescription(const ObjectAddress *object)
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index d0803df..299cc53 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -705,13 +705,11 @@ pts_error_callback(void *arg)
 /*
  * Given a string that is supposed to be a SQL-compatible type declaration,
  * such as "int4" or "integer" or "character varying(32)", parse
- * the string and convert it to a type OID and type modifier.
- * If missing_ok is true, InvalidOid is returned rather than raising an error
- * when the type name is not found.
+ * the string and return the result as a TypeName.
+ * If the string cannot be parsed as a type, an error is raised.
  */
-void
-parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p,
-				bool missing_ok)
+TypeName *
+typeStringToTypeName(const char *str)
 {
 	StringInfoData buf;
 	List	   *raw_parsetree_list;
@@ -720,7 +718,6 @@ parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p,
 	TypeCast   *typecast;
 	TypeName   *typeName;
 	ErrorContextCallback ptserrcontext;
-	Type		tup;
 
 	/* make sure we give useful error for empty input */
 	if (strspn(str, " \t\n\r\f") == strlen(str))
@@ -779,6 +776,7 @@ parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p,
 		typecast->arg == NULL ||
 		!IsA(typecast->arg, A_Const))
 		goto fail;
+
 	typeName = typecast->typeName;
 	if (typeName == NULL ||
 		!IsA(typeName, TypeName))
@@ -786,6 +784,31 @@ parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p,
 	if (typeName->setof)
 		goto fail;
 
+	pfree(buf.data);
+
+	return typeName;
+
+fail:
+	ereport(ERROR,
+			(errcode(ERRCODE_SYNTAX_ERROR),
+			 errmsg("invalid type name \"%s\"", str)));
+}
+
+/*
+ * Given a string that is supposed to be a SQL-compatible type declaration,
+ * such as "int4" or "integer" or "character varying(32)", parse
+ * the string and convert it to a type OID and type modifier.
+ * If missing_ok is true, InvalidOid is returned rather than raising an error
+ * when the type name is not found.
+ */
+void
+parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p, bool missing_ok)
+{
+	TypeName   *typeName;
+	Type		tup;
+
+	typeName = typeStringToTypeName(str);
+
 	tup = LookupTypeName(NULL, typeName, typmod_p, missing_ok);
 	if (tup == NULL)
 	{
@@ -808,13 +831,4 @@ parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p,
 		*typeid_p = HeapTupleGetOid(tup);
 		ReleaseSysCache(tup);
 	}
-
-	pfree(buf.data);
-
-	return;
-
-fail:
-	ereport(ERROR,
-			(errcode(ERRCODE_SYNTAX_ERROR),
-			 errmsg("invalid type name \"%s\"", str)));
 }
diff --git a/src/include/catalog/objectaddress.h b/src/include/catalog/objectaddress.h
index 2a9431d..cab9bfe 100644
--- a/src/include/catalog/objectaddress.h
+++ b/src/include/catalog/objectaddress.h
@@ -55,6 +55,7 @@ extern HeapTuple get_catalog_object_by_oid(Relation catalog,
 extern char *getObjectDescription(const ObjectAddress *object);
 extern char *getObjectDescriptionOids(Oid classid, Oid objid);
 
+extern int unstringify_objtype(const char *objtype);
 extern char *getObjectTypeDescription(const ObjectAddress *object);
 extern char *getObjectIdentity(const ObjectAddress *address);
 
diff --git a/src/include/parser/parse_type.h b/src/include/parser/parse_type.h
index fa9cc59..152bdbb 100644
--- a/src/include/parser/parse_type.h
+++ b/src/include/parser/parse_type.h
@@ -47,6 +47,7 @@ extern Datum stringTypeDatum(Type tp, char *string, int32 atttypmod);
 
 extern Oid	typeidTypeRelid(Oid type_id);
 
+extern TypeName *typeStringToTypeName(const char *str);
 extern void parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p, bool missing_ok);
 
 #define ISCOMPLEX(typeid) (typeidTypeRelid(typeid) != InvalidOid)
-- 
2.1.3

>From 4d5f038a39b88b616f332df3ac52a22469f7203a Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvhe...@alvh.no-ip.org>
Date: Mon, 22 Dec 2014 14:50:52 -0300
Subject: [PATCH 4/5] Add sql-callable pg_get_object_address

---
 src/backend/catalog/objectaddress.c    | 180 +++++++++++++++++++++++++++++++++
 src/include/catalog/pg_proc.h          |   3 +
 src/include/utils/builtins.h           |   3 +
 src/test/regress/sql/alter_generic.sql |  68 +++++++++++++
 4 files changed, 254 insertions(+)

diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 59431a9..74dca73 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -1368,6 +1368,186 @@ get_object_address_opcf(ObjectType objtype,
 }
 
 /*
+ * 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.
+ */
+static List *
+textarray_to_strvaluelist(ArrayType *arr)
+{
+	Datum  *elems;
+	bool   *nulls;
+	int		nelems;
+	List   *list = NIL;
+	int		i;
+
+	deconstruct_array(arr, TEXTOID, -1, false, 'i',
+					  &elems, &nulls, &nelems);
+
+	for (i = 0; i < nelems; i++)
+	{
+		Assert(nulls[i] == false);
+		list = lappend(list, makeString(TextDatumGetCString(elems[i])));
+	}
+
+	return list;
+}
+
+/*
+ * SQL-callable version of get_object_address
+ *
+ * Note: this function is not strict to allow objargs to be NULL, which is the
+ * most common case.  Nulls in the other arguments cause an error to be raised.
+ */
+Datum
+pg_get_object_address(PG_FUNCTION_ARGS)
+{
+	char   *ttype;
+	ObjectType type;
+	ArrayType *namearr;
+	List   *name;
+	List   *args;
+	ObjectAddress addr;
+	TupleDesc tupdesc;
+	Datum	values[3];
+	bool	nulls[3];
+	HeapTuple htup;
+	Relation	relation;
+
+	if (PG_ARGISNULL(0))
+		ereport(ERROR,
+				(errmsg("object type must not be null")));
+	if (PG_ARGISNULL(1))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("object name array must not be null")));
+
+	/* Decode object type, raise error if unknown */
+	ttype = TextDatumGetCString(PG_GETARG_TEXT_P(0));
+	type = unstringify_objtype(ttype);
+	if (type < 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("unrecognized object type \"%s\"", ttype)));
+
+	/*
+	 * For some object types, the "name" array must be passed as a TypeName;
+	 * for all other cases, use a list of a string Values.
+	 */
+	namearr = PG_GETARG_ARRAYTYPE_P(1);
+	if (type == OBJECT_TYPE || type == OBJECT_DOMAIN)
+	{
+		Datum	*elems;
+		bool	*nulls;
+		int		nelems;
+		TypeName *type;
+
+		deconstruct_array(namearr, TEXTOID, -1, false, 'i',
+						  &elems, &nulls, &nelems);
+		if (nelems != 1)
+			ereport(ERROR,
+					(errmsg("must supply only one type name")));
+		if (nulls[0])
+			ereport(ERROR,
+					(errmsg("type name must not be null")));
+		type = typeStringToTypeName(TextDatumGetCString(elems[0]));
+		name = type->names;
+	}
+	else if (type == OBJECT_CAST)
+	{
+		Datum	*elems;
+		bool	*nulls;
+		int		nelems;
+
+		deconstruct_array(namearr, TEXTOID, -1, false, 'i',
+						  &elems, &nulls, &nelems);
+		if (nelems != 1)
+			ereport(ERROR,
+					(errmsg("must supply only one type name")));
+		if (nulls[0])
+			ereport(ERROR,
+					(errmsg("type name must not be null")));
+		name = list_make1(typeStringToTypeName(TextDatumGetCString(elems[0])));
+	}
+	else
+		name = textarray_to_strvaluelist(namearr);
+
+	/*
+	 * If args are given, decode them according to the object type.
+	 */
+	if (!PG_ARGISNULL(2))
+	{
+		ArrayType *arrargs = PG_GETARG_ARRAYTYPE_P(2);
+
+		if (type == OBJECT_AGGREGATE ||
+			type == OBJECT_FUNCTION ||
+			type == OBJECT_OPERATOR ||
+			type == OBJECT_CAST)
+		{
+			/* in these cases, the args list must be of TypeName */
+			Datum  *elems;
+			bool   *nulls;
+			int		nelems;
+			int		i;
+
+			deconstruct_array(arrargs, TEXTOID, -1, false, 'i',
+							  &elems, &nulls, &nelems);
+
+			args = NIL;
+			for (i = 0; i < nelems; i++)
+			{
+				Assert(nulls[i] == false);
+				args = lappend(args,
+							   typeStringToTypeName(TextDatumGetCString(elems[i])));
+			}
+		}
+		else
+		{
+			/* For all other object types, use string Values */
+			args = textarray_to_strvaluelist(arrargs);
+		}
+	}
+	else
+	{
+		/* args is NULL */
+		args = NIL;
+	}
+
+	if (type == OBJECT_OPCLASS ||
+		type == OBJECT_OPFAMILY)
+	{
+		if (list_length(args) != 1)
+			ereport(ERROR,
+				   (errmsg("length(args) must equal 1")));
+	}
+
+	addr = get_object_address(type, name, args,
+							  &relation, AccessShareLock, false);
+
+	if (relation)
+		relation_close(relation, AccessShareLock);
+
+	tupdesc = CreateTemplateTupleDesc(3, false);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "classid",
+					   OIDOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "objid",
+					   OIDOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "objsubid",
+					   INT4OID, -1, 0);
+	tupdesc = BlessTupleDesc(tupdesc);
+
+	values[0] = ObjectIdGetDatum(addr.classId);
+	values[1] = ObjectIdGetDatum(addr.objectId);
+	values[2] = Int32GetDatum(addr.objectSubId);
+	nulls[0] = false;
+	nulls[1] = false;
+	nulls[2] = false;
+
+	htup = heap_form_tuple(tupdesc, values, nulls);
+
+	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+}
+
+/*
  * Check ownership of an object previously identified by get_object_address.
  */
 void
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index f766ed7..932969a 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3036,6 +3036,9 @@ DESCR("get identification of SQL object");
 DATA(insert OID = 3839 (  pg_identify_object		PGNSP PGUID 12 1 0 0 0 f f f f t f s 3 0 2249 "26 26 23" "{26,26,23,25,25,25,25}" "{i,i,i,o,o,o,o}" "{classid,objid,subobjid,type,schema,name,identity}" _null_ pg_identify_object _null_ _null_ _null_ ));
 DESCR("get machine-parseable identification of SQL object");
 
+DATA(insert OID = 3954 (  pg_get_object_address    PGNSP PGUID 12 1 0 0 0 f f f f f f s 3 0 2249 "25 1009 1009" "{25,1009,1009,26,26,23}" "{i,i,i,o,o,o}" "{type,name,args,classid,objid,subobjid}" _null_ pg_get_object_address _null_ _null_ _null_ ));
+DESCR("get OID-based object address from name/args arrays");
+
 DATA(insert OID = 2079 (  pg_table_is_visible		PGNSP PGUID 12 10 0 0 0 f f f f t f s 1 0 16 "26" _null_ _null_ _null_ _null_ pg_table_is_visible _null_ _null_ _null_ ));
 DESCR("is table visible in search path?");
 DATA(insert OID = 2080 (  pg_type_is_visible		PGNSP PGUID 12 10 0 0 0 f f f f t f s 1 0 16 "26" _null_ _null_ _null_ _null_ pg_type_is_visible _null_ _null_ _null_ ));
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 2da3002..7c4d291 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -1195,6 +1195,9 @@ extern Datum pg_last_committed_xact(PG_FUNCTION_ARGS);
 extern Datum pg_describe_object(PG_FUNCTION_ARGS);
 extern Datum pg_identify_object(PG_FUNCTION_ARGS);
 
+/* catalog/objectaddress.c */
+extern Datum pg_get_object_address(PG_FUNCTION_ARGS);
+
 /* commands/constraint.c */
 extern Datum unique_key_recheck(PG_FUNCTION_ARGS);
 
diff --git a/src/test/regress/sql/alter_generic.sql b/src/test/regress/sql/alter_generic.sql
index f46cbc8..3171467 100644
--- a/src/test/regress/sql/alter_generic.sql
+++ b/src/test/regress/sql/alter_generic.sql
@@ -537,6 +537,74 @@ SELECT nspname, prsname
   WHERE t.prsnamespace = n.oid AND nspname like 'alt_nsp%'
   ORDER BY nspname, prsname;
 
+-- Test generic object addressing/identification functions
+CREATE TABLE alt_nsp1.gentable (
+	a serial primary key CONSTRAINT a_chk CHECK (a > 0),
+	b text DEFAULT 'hello');
+CREATE VIEW alt_nsp1.genview AS SELECT * from gentable;
+CREATE MATERIALIZED VIEW alt_nsp1.genmatview AS SELECT * FROM alt_nsp1.gentable;
+CREATE TYPE alt_nsp1.gencomptype AS (a int);
+CREATE TYPE alt_nsp1.genenum AS ENUM ('one', 'two');
+CREATE FOREIGN TABLE alt_nsp1.genftable (a int) SERVER alt_fserv2;
+CREATE AGGREGATE alt_nsp1.genaggr(int4) (sfunc = int4pl, stype = int4);
+CREATE DOMAIN alt_nsp1.gendomain AS int4 CONSTRAINT domconstr CHECK (value > 0);
+CREATE FUNCTION alt_nsp1.trig() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN END; $$;
+CREATE TRIGGER t BEFORE INSERT ON alt_nsp1.gentable FOR EACH ROW EXECUTE PROCEDURE alt_nsp1.trig();
+CREATE POLICY genpol ON alt_nsp1.gentable;
+
+CREATE FUNCTION alt_nsp1.etrig() RETURNS EVENT_TRIGGER LANGUAGE plpgsql AS $$ BEGIN END; $$;
+CREATE EVENT TRIGGER evttrig ON ddl_command_end EXECUTE PROCEDURE etrig();
+
+WITH objects (type, name, args) AS (VALUES
+				('table', '{alt_nsp1, gentable}'::text[], NULL::text[]),
+				('index', '{alt_nsp1, gentable_pkey}', NULL),
+				('sequence', '{alt_nsp1, gentable_a_seq}', NULL),
+				-- toast table
+				('view', '{alt_nsp1, genview}', NULL),
+				('materialized view', '{alt_nsp1, genmatview}', NULL),
+				('foreign table', '{alt_nsp1, genftable}', NULL),
+				('table column', '{alt_nsp1, gentable, b}', NULL),
+				('foreign table column', '{alt_nsp1, genftable, a}', NULL),
+				('aggregate', '{alt_nsp1, genaggr}', '{int4}'),
+				('function', '{pg_catalog, pg_identify_object}', '{pg_catalog.oid, pg_catalog.oid, int4}'),
+				('type', '{pg_catalog._int4}', NULL),
+				('type', '{alt_nsp1.gendomain}', NULL),
+				('type', '{alt_nsp1.gencomptype}', NULL),
+				('type', '{alt_nsp1.genenum}', NULL),
+				('cast', '{int8}', '{int4}'),
+				('collation', '{default}', NULL),
+				('table constraint', '{alt_nsp1, gentable, a_chk}', NULL),
+				('domain constraint', '{alt_nsp1, gendomain, domconstr}', NULL),
+				('conversion', '{pg_catalog, ascii_to_mic}', NULL),
+				('default value', '{alt_nsp1, gentable, b}', NULL),
+				('language', '{plpgsql}', NULL),
+				-- large object
+				('operator', '{+}', '{int4, int4}'),
+				('operator class', '{int4_ops}', '{btree}'),
+				('operator family', '{integer_ops}', '{btree}'),
+				-- operator of access method
+				-- function of access method
+				('rule', '{alt_nsp1, genview, _RETURN}', NULL),
+				('trigger', '{alt_nsp1, gentable, t}', NULL),
+				('schema', '{alt_nsp1}', NULL),
+				('text search parser', '{alt_ts_prs3}', NULL),
+				('text search dictionary', '{alt_ts_dict3}', NULL),
+				('text search template', '{alt_ts_temp3}', NULL),
+				('text search configuration', '{alt_ts_conf3}', NULL),
+				('role', '{regtest_alter_user2}', NULL),
+				-- database
+				-- tablespace
+				('foreign-data wrapper', '{alt_fdw3}', NULL),
+				('server', '{alt_fserv3}', NULL),
+				-- user mapping
+				-- extension
+				('event trigger', '{evttrig}', NULL),
+				('policy', '{alt_nsp1, gentable, genpol}', NULL)
+        )
+SELECT (pg_identify_object(classid, objid, subobjid)).*
+  FROM objects, pg_get_object_address(type, name, args)
+ORDER BY classid, objid;
+
 ---
 --- Cleanup resources
 ---
-- 
2.1.3

>From ae2fe1ecd804a279ceb34eb617a19ac768d8975a Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvhe...@alvh.no-ip.org>
Date: Mon, 22 Dec 2014 18:32:43 -0300
Subject: [PATCH 5/5] array objname/objargs stuff

---
 doc/src/sgml/func.sgml               |  16 ++++
 src/backend/catalog/objectaddress.c  | 172 +++++++++++++++++++++++++++++++----
 src/backend/commands/event_trigger.c |  52 ++++++++++-
 src/backend/utils/adt/regproc.c      |  60 ++++++++++++
 src/include/catalog/objectaddress.h  |   2 +
 src/include/catalog/pg_proc.h        |   3 +-
 src/include/utils/builtins.h         |   4 +
 7 files changed, 283 insertions(+), 26 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 24c64b7..bccb667 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17772,6 +17772,22 @@ FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger();
          identifier present in the identity is quoted if necessary.
         </entry>
        </row>
+       <row>
+        <entry><literal>address_names</literal></entry>
+        <entry><type>text[]</type></entry>
+        <entry>
+         An array that, together with <literal>address_args</literal>,
+         can be used by the C-language function getObjectAddress() to
+         recreate the object address in a remote server containing a similar object.
+        </entry>
+       </row>
+       <row>
+        <entry><literal>address_args</literal></entry>
+        <entry><type>text[]</type></entry>
+        <entry>
+         See <literal>address_names</literal> above.
+        </entry>
+       </row>
       </tbody>
      </tgroup>
     </informaltable>
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 74dca73..eb2ddff 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -556,8 +556,9 @@ static void getRelationTypeDescription(StringInfo buffer, Oid relid,
 						   int32 objectSubId);
 static void getProcedureTypeDescription(StringInfo buffer, Oid procid);
 static void getConstraintTypeDescription(StringInfo buffer, Oid constroid);
-static void getOpFamilyIdentity(StringInfo buffer, Oid opfid);
-static void getRelationIdentity(StringInfo buffer, Oid relid);
+static void getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **objname,
+					List **objargs);
+static void getRelationIdentity(StringInfo buffer, Oid relid, List **objname);
 
 /*
  * Translate an object name and arguments (as passed by the parser) to an
@@ -3170,7 +3171,7 @@ getProcedureTypeDescription(StringInfo buffer, Oid procid)
 }
 
 /*
- * Return a palloc'ed string that identifies an object.
+ * Obtain a given object's identity, as a palloc'ed string.
  *
  * This is for machine consumption, so it's not translated.  All elements are
  * schema-qualified when appropriate.
@@ -3178,14 +3179,42 @@ getProcedureTypeDescription(StringInfo buffer, Oid procid)
 char *
 getObjectIdentity(const ObjectAddress *object)
 {
+	return getObjectIdentityParts(object, NULL, NULL);
+}
+
+/*
+ * As above, but more detailed.
+ *
+ * There are two sets of return values: the identity itself as a palloc'd
+ * string is returned.  objname and objargs, if not NULL, are output parameters
+ * that receive lists of strings that are useful to give back to
+ * get_object_address() to reconstruct the ObjectAddress.
+ */
+char *
+getObjectIdentityParts(const ObjectAddress *object,
+					   List **objname, List **objargs)
+{
 	StringInfoData buffer;
 
 	initStringInfo(&buffer);
 
+	/*
+	 * Make sure that both objname and objargs were passed, or none was; and
+	 * initialize them to empty lists.  For objname this is useless because it
+	 * will be initialized in all cases inside the switch; but we do it anyway
+	 * so that we can Assert() below that no branch leaves it unset.
+	 */
+	Assert(PointerIsValid(objname) == PointerIsValid(objargs));
+	if (objname)
+	{
+		*objname = NIL;
+		*objargs = NIL;
+	}
+
 	switch (getObjectClass(object))
 	{
 		case OCLASS_CLASS:
-			getRelationIdentity(&buffer, object->objectId);
+			getRelationIdentity(&buffer, object->objectId, objname);
 			if (object->objectSubId != 0)
 			{
 				char	   *attr;
@@ -3193,17 +3222,27 @@ getObjectIdentity(const ObjectAddress *object)
 				attr = get_relid_attribute_name(object->objectId,
 												object->objectSubId);
 				appendStringInfo(&buffer, ".%s", quote_identifier(attr));
+				if (objname)
+					*objname = lappend(*objname, attr);
 			}
 			break;
 
 		case OCLASS_PROC:
 			appendStringInfoString(&buffer,
 							   format_procedure_qualified(object->objectId));
+			if (objname)
+				format_procedure_parts(object->objectId, objname, objargs);
 			break;
 
 		case OCLASS_TYPE:
-			appendStringInfoString(&buffer,
-								 format_type_be_qualified(object->objectId));
+			{
+				char *typeout;
+
+				typeout = format_type_be_qualified(object->objectId);
+				appendStringInfoString(&buffer, typeout);
+				if (objname)
+					*objname = list_make1(typeout);
+			}
 			break;
 
 		case OCLASS_CAST:
@@ -3226,6 +3265,10 @@ getObjectIdentity(const ObjectAddress *object)
 							  format_type_be_qualified(castForm->castsource),
 							 format_type_be_qualified(castForm->casttarget));
 
+				if (objname)
+					*objname = list_make2(format_type_be_qualified(castForm->castsource),
+										  format_type_be_qualified(castForm->casttarget));
+
 				heap_close(castRel, AccessShareLock);
 				break;
 			}
@@ -3246,6 +3289,8 @@ getObjectIdentity(const ObjectAddress *object)
 				appendStringInfoString(&buffer,
 									   quote_qualified_identifier(schema,
 												   NameStr(coll->collname)));
+				if (objname)
+					*objname = list_make2(schema, NameStr(coll->collname));
 				ReleaseSysCache(collTup);
 				break;
 			}
@@ -3266,7 +3311,9 @@ getObjectIdentity(const ObjectAddress *object)
 				{
 					appendStringInfo(&buffer, "%s on ",
 									 quote_identifier(NameStr(con->conname)));
-					getRelationIdentity(&buffer, con->conrelid);
+					getRelationIdentity(&buffer, con->conrelid, objname);
+					if (objname)
+						*objname = lappend(*objname, pstrdup(NameStr(con->conname)));
 				}
 				else
 				{
@@ -3278,7 +3325,10 @@ getObjectIdentity(const ObjectAddress *object)
 
 					appendStringInfo(&buffer, "%s on %s",
 									 quote_identifier(NameStr(con->conname)),
-									 getObjectIdentity(&domain));
+									 getObjectIdentityParts(&domain, objname,
+															objargs));
+					if (objname)
+						*objname = lappend(*objname, pstrdup(NameStr(con->conname)));
 				}
 
 				ReleaseSysCache(conTup);
@@ -3298,6 +3348,8 @@ getObjectIdentity(const ObjectAddress *object)
 				conForm = (Form_pg_conversion) GETSTRUCT(conTup);
 				appendStringInfoString(&buffer,
 								quote_identifier(NameStr(conForm->conname)));
+				if (objname)
+					*objname = list_make1(pstrdup(NameStr(conForm->conname)));
 				ReleaseSysCache(conTup);
 				break;
 			}
@@ -3335,7 +3387,8 @@ getObjectIdentity(const ObjectAddress *object)
 				colobject.objectSubId = attrdef->adnum;
 
 				appendStringInfo(&buffer, "for %s",
-								 getObjectIdentity(&colobject));
+								 getObjectIdentityParts(&colobject,
+														objname, objargs));
 
 				systable_endscan(adscan);
 				heap_close(attrdefDesc, AccessShareLock);
@@ -3355,17 +3408,23 @@ getObjectIdentity(const ObjectAddress *object)
 				langForm = (Form_pg_language) GETSTRUCT(langTup);
 				appendStringInfoString(&buffer,
 							   quote_identifier(NameStr(langForm->lanname)));
+				if (objname)
+					*objname = list_make1(pstrdup(NameStr(langForm->lanname)));
 				ReleaseSysCache(langTup);
 				break;
 			}
 		case OCLASS_LARGEOBJECT:
 			appendStringInfo(&buffer, "%u",
 							 object->objectId);
+			if (objname)
+				*objname = list_make1(psprintf("%u", object->objectId));
 			break;
 
 		case OCLASS_OPERATOR:
 			appendStringInfoString(&buffer,
 								format_operator_qualified(object->objectId));
+			if (objname)
+				format_operator_parts(object->objectId, objname, objargs);
 			break;
 
 		case OCLASS_OPCLASS:
@@ -3396,14 +3455,19 @@ getObjectIdentity(const ObjectAddress *object)
 												 NameStr(opcForm->opcname)));
 				appendStringInfo(&buffer, " for %s",
 								 quote_identifier(NameStr(amForm->amname)));
-
+				if (objname)
+				{
+					*objname = list_make2(pstrdup(schema),
+										  pstrdup(NameStr(opcForm->opcname)));
+					*objargs = list_make1(pstrdup(NameStr(amForm->amname)));
+				}
 				ReleaseSysCache(amTup);
 				ReleaseSysCache(opcTup);
 				break;
 			}
 
 		case OCLASS_OPFAMILY:
-			getOpFamilyIdentity(&buffer, object->objectId);
+			getOpFamilyIdentity(&buffer, object->objectId, objname, objargs);
 			break;
 
 		case OCLASS_AMOP:
@@ -3415,6 +3479,10 @@ getObjectIdentity(const ObjectAddress *object)
 				Form_pg_amop amopForm;
 				StringInfoData opfam;
 
+				/* no objname support here */
+				if (objname)
+					*objname = NIL;
+
 				amopDesc = heap_open(AccessMethodOperatorRelationId,
 									 AccessShareLock);
 
@@ -3435,7 +3503,7 @@ getObjectIdentity(const ObjectAddress *object)
 				amopForm = (Form_pg_amop) GETSTRUCT(tup);
 
 				initStringInfo(&opfam);
-				getOpFamilyIdentity(&opfam, amopForm->amopfamily);
+				getOpFamilyIdentity(&opfam, amopForm->amopfamily, NULL, NULL);
 
 				appendStringInfo(&buffer, "operator %d (%s, %s) of %s",
 								 amopForm->amopstrategy,
@@ -3459,6 +3527,10 @@ getObjectIdentity(const ObjectAddress *object)
 				Form_pg_amproc amprocForm;
 				StringInfoData opfam;
 
+				/* no objname support here */
+				if (objname)
+					*objname = NIL;
+
 				amprocDesc = heap_open(AccessMethodProcedureRelationId,
 									   AccessShareLock);
 
@@ -3479,7 +3551,7 @@ getObjectIdentity(const ObjectAddress *object)
 				amprocForm = (Form_pg_amproc) GETSTRUCT(tup);
 
 				initStringInfo(&opfam);
-				getOpFamilyIdentity(&opfam, amprocForm->amprocfamily);
+				getOpFamilyIdentity(&opfam, amprocForm->amprocfamily, NULL, NULL);
 
 				appendStringInfo(&buffer, "function %d (%s, %s) of %s",
 								 amprocForm->amprocnum,
@@ -3512,7 +3584,9 @@ getObjectIdentity(const ObjectAddress *object)
 
 				appendStringInfo(&buffer, "%s on ",
 								 quote_identifier(NameStr(rule->rulename)));
-				getRelationIdentity(&buffer, rule->ev_class);
+				getRelationIdentity(&buffer, rule->ev_class, objname);
+				if (objname)
+					*objname = lappend(*objname, NameStr(rule->rulename));
 
 				heap_close(ruleDesc, AccessShareLock);
 				break;
@@ -3536,7 +3610,9 @@ getObjectIdentity(const ObjectAddress *object)
 
 				appendStringInfo(&buffer, "%s on ",
 								 quote_identifier(NameStr(trig->tgname)));
-				getRelationIdentity(&buffer, trig->tgrelid);
+				getRelationIdentity(&buffer, trig->tgrelid, objname);
+				if (objname)
+					*objname = lappend(*objname, NameStr(trig->tgname));
 
 				heap_close(trigDesc, AccessShareLock);
 				break;
@@ -3560,7 +3636,9 @@ getObjectIdentity(const ObjectAddress *object)
 
 				appendStringInfo(&buffer, "%s on ",
 								 quote_identifier(NameStr(policy->polname)));
-				getRelationIdentity(&buffer, policy->polrelid);
+				getRelationIdentity(&buffer, policy->polrelid, objname);
+				if (objname)
+					*objname = lappend(*objname, NameStr(policy->polname));
 
 				heap_close(polDesc, AccessShareLock);
 				break;
@@ -3576,6 +3654,8 @@ getObjectIdentity(const ObjectAddress *object)
 						 object->objectId);
 				appendStringInfoString(&buffer,
 									   quote_identifier(nspname));
+				if (objname)
+					*objname = list_make1(nspname);
 				break;
 			}
 
@@ -3595,6 +3675,9 @@ getObjectIdentity(const ObjectAddress *object)
 				appendStringInfoString(&buffer,
 									   quote_qualified_identifier(schema,
 											  NameStr(formParser->prsname)));
+				if (objname)
+					*objname = list_make2(schema,
+										  pstrdup(NameStr(formParser->prsname)));
 				ReleaseSysCache(tup);
 				break;
 			}
@@ -3615,6 +3698,9 @@ getObjectIdentity(const ObjectAddress *object)
 				appendStringInfoString(&buffer,
 									   quote_qualified_identifier(schema,
 											   NameStr(formDict->dictname)));
+				if (objname)
+					*objname = list_make2(schema,
+										  pstrdup(NameStr(formDict->dictname)));
 				ReleaseSysCache(tup);
 				break;
 			}
@@ -3635,7 +3721,9 @@ getObjectIdentity(const ObjectAddress *object)
 				appendStringInfoString(&buffer,
 									   quote_qualified_identifier(schema,
 											   NameStr(formTmpl->tmplname)));
-				pfree(schema);
+				if (objname)
+					*objname = list_make2(schema,
+										  pstrdup(NameStr(formTmpl->tmplname)));
 				ReleaseSysCache(tup);
 				break;
 			}
@@ -3656,6 +3744,9 @@ getObjectIdentity(const ObjectAddress *object)
 				appendStringInfoString(&buffer,
 									   quote_qualified_identifier(schema,
 												 NameStr(formCfg->cfgname)));
+				if (objname)
+					*objname = list_make2(schema,
+										  pstrdup(NameStr(formCfg->cfgname)));
 				ReleaseSysCache(tup);
 				break;
 			}
@@ -3664,6 +3755,9 @@ getObjectIdentity(const ObjectAddress *object)
 			{
 				char	   *username;
 
+				/* no objname support here */
+				Assert(objname == NULL);
+
 				username = GetUserNameFromId(object->objectId);
 				appendStringInfoString(&buffer,
 									   quote_identifier(username));
@@ -3674,6 +3768,9 @@ getObjectIdentity(const ObjectAddress *object)
 			{
 				char	   *datname;
 
+				/* no objname support here */
+				Assert(objname == NULL);
+
 				datname = get_database_name(object->objectId);
 				if (!datname)
 					elog(ERROR, "cache lookup failed for database %u",
@@ -3687,6 +3784,9 @@ getObjectIdentity(const ObjectAddress *object)
 			{
 				char	   *tblspace;
 
+				/* no objname support here */
+				Assert(objname == NULL);
+
 				tblspace = get_tablespace_name(object->objectId);
 				if (!tblspace)
 					elog(ERROR, "cache lookup failed for tablespace %u",
@@ -3702,6 +3802,8 @@ getObjectIdentity(const ObjectAddress *object)
 
 				fdw = GetForeignDataWrapper(object->objectId);
 				appendStringInfoString(&buffer, quote_identifier(fdw->fdwname));
+				if (objname)
+					*objname = list_make1(pstrdup(fdw->fdwname));
 				break;
 			}
 
@@ -3712,6 +3814,8 @@ getObjectIdentity(const ObjectAddress *object)
 				srv = GetForeignServer(object->objectId);
 				appendStringInfoString(&buffer,
 									   quote_identifier(srv->servername));
+				if (objname)
+					*objname = list_make1(pstrdup(srv->servername));
 				break;
 			}
 
@@ -3721,6 +3825,8 @@ getObjectIdentity(const ObjectAddress *object)
 				Oid			useid;
 				const char *usename;
 
+				/* XXX get_object_address doesn't seem to support this */
+
 				tup = SearchSysCache1(USERMAPPINGOID,
 									  ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(tup))
@@ -3745,10 +3851,15 @@ getObjectIdentity(const ObjectAddress *object)
 				Relation	defaclrel;
 				ScanKeyData skey[1];
 				SysScanDesc rcscan;
-
 				HeapTuple	tup;
 				Form_pg_default_acl defacl;
 
+				/*
+				 * There is no valid representation for default ACL objects for
+				 * get_object_address; disallow callers from asking for it.
+				 */
+				Assert(!objname);
+
 				defaclrel = heap_open(DefaultAclRelationId, AccessShareLock);
 
 				ScanKeyInit(&skey[0],
@@ -3815,6 +3926,8 @@ getObjectIdentity(const ObjectAddress *object)
 					elog(ERROR, "cache lookup failed for extension %u",
 						 object->objectId);
 				appendStringInfoString(&buffer, quote_identifier(extname));
+				if (objname)
+					*objname = list_make1(extname);
 				break;
 			}
 
@@ -3823,6 +3936,9 @@ getObjectIdentity(const ObjectAddress *object)
 				HeapTuple	tup;
 				Form_pg_event_trigger trigForm;
 
+				/* no objname support here */
+				Assert(objname == NULL);
+
 				tup = SearchSysCache1(EVENTTRIGGEROID,
 									  ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(tup))
@@ -3843,11 +3959,18 @@ getObjectIdentity(const ObjectAddress *object)
 			break;
 	}
 
+	/*
+	 * If a get_object_address representation was requested, make sure we are
+	 * providing one.  We don't check for objargs, because many of the cases
+	 * above leave it as NIL.
+	 */
+	Assert(!objname || *objname != NIL);
+
 	return buffer.data;
 }
 
 static void
-getOpFamilyIdentity(StringInfo buffer, Oid opfid)
+getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **objname, List **objargs)
 {
 	HeapTuple	opfTup;
 	Form_pg_opfamily opfForm;
@@ -3872,6 +3995,13 @@ getOpFamilyIdentity(StringInfo buffer, Oid opfid)
 												NameStr(opfForm->opfname)),
 					 NameStr(amForm->amname));
 
+	if (objname)
+	{
+		*objname = list_make2(pstrdup(schema),
+							  pstrdup(NameStr(opfForm->opfname)));
+		*objargs = list_make1(pstrdup(NameStr(amForm->amname)));
+	}
+
 	ReleaseSysCache(amTup);
 	ReleaseSysCache(opfTup);
 }
@@ -3881,7 +4011,7 @@ getOpFamilyIdentity(StringInfo buffer, Oid opfid)
  * StringInfo.
  */
 static void
-getRelationIdentity(StringInfo buffer, Oid relid)
+getRelationIdentity(StringInfo buffer, Oid relid, List **objname)
 {
 	HeapTuple	relTup;
 	Form_pg_class relForm;
@@ -3897,6 +4027,8 @@ getRelationIdentity(StringInfo buffer, Oid relid)
 	appendStringInfoString(buffer,
 						   quote_qualified_identifier(schema,
 												 NameStr(relForm->relname)));
+	if (objname)
+		*objname = list_make2(schema, pstrdup(NameStr(relForm->relname)));
 
 	ReleaseSysCache(relTup);
 }
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 34dd3c0..9ea20a7 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -117,6 +117,8 @@ typedef struct SQLDropObject
 	const char *objname;
 	const char *objidentity;
 	const char *objecttype;
+	List	   *addrnames;
+	List	   *addrargs;
 	bool		original;
 	bool		normal;
 	slist_node	next;
@@ -1324,10 +1326,11 @@ EventTriggerSQLDropAddObject(const ObjectAddress *object, bool original, bool no
 		heap_close(catalog, AccessShareLock);
 	}
 
-	/* object identity */
-	obj->objidentity = getObjectIdentity(&obj->address);
+	/* object identity, objname and objargs */
+	obj->objidentity =
+		getObjectIdentityParts(&obj->address, &obj->addrnames, &obj->addrargs);
 
-	/* and object type, too */
+	/* object type */
 	obj->objecttype = getObjectTypeDescription(&obj->address);
 
 	slist_push_head(&(currentEventTriggerState->SQLDropList), &obj->next);
@@ -1336,6 +1339,33 @@ EventTriggerSQLDropAddObject(const ObjectAddress *object, bool original, bool no
 }
 
 /*
+ * helper for pg_event_trigger_dropped_object
+ *
+ * Make an array of text Datum out of a list of C strings.
+ */
+static Datum
+strlist_to_textarray(List *list)
+{
+	ArrayType *arr;
+	Datum	*datums;
+	int		j = 0;
+	ListCell *cell;
+
+	datums = palloc(sizeof(text *) * list_length(list));
+	foreach(cell, list)
+	{
+		char   *name = lfirst(cell);
+
+		datums[j++] = CStringGetTextDatum(name);
+	}
+
+	arr = construct_array(datums, list_length(list),
+						  TEXTOID, -1, false, 'i');
+
+	return PointerGetDatum(arr);
+}
+
+/*
  * pg_event_trigger_dropped_objects
  *
  * Make the list of dropped objects available to the user function run by the
@@ -1390,8 +1420,8 @@ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
 	{
 		SQLDropObject *obj;
 		int			i = 0;
-		Datum		values[9];
-		bool		nulls[9];
+		Datum		values[11];
+		bool		nulls[11];
 
 		obj = slist_container(SQLDropObject, next, iter.cur);
 
@@ -1434,6 +1464,18 @@ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
 		else
 			nulls[i++] = true;
 
+		/* address_names */
+		if (obj->addrnames)
+			values[i++] = strlist_to_textarray(obj->addrnames);
+		else
+			nulls[i++] = true;
+
+		/* address_args */
+		if (obj->addrargs)
+			values[i++] = strlist_to_textarray(obj->addrargs);
+		else
+			nulls[i++] = true;
+
 		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
 	}
 
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index c0314ee..8cda52b 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -439,6 +439,41 @@ format_procedure_internal(Oid procedure_oid, bool force_qualify)
 }
 
 /*
+ * Output a objname/objargs representation for the procedure with the
+ * given OID.  If it doesn't exist, an error is thrown.
+ *
+ * This can be used to feed get_object_address.
+ */
+void
+format_procedure_parts(Oid procedure_oid, List **objnames, List **objargs)
+{
+	HeapTuple	proctup;
+	Form_pg_proc procform;
+	int			nargs;
+	int			i;
+
+	proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procedure_oid));
+
+	if (!HeapTupleIsValid(proctup))
+		elog(ERROR, "cache lookup failed for procedure with OID %u", procedure_oid);
+
+	procform = (Form_pg_proc) GETSTRUCT(proctup);
+	nargs = procform->pronargs;
+
+	*objnames = list_make2(get_namespace_name(procform->pronamespace),
+						   pstrdup(NameStr(procform->proname)));
+	*objargs = NIL;
+	for (i = 0; i < nargs; i++)
+	{
+		Oid		thisargtype = procform->proargtypes.values[i];
+
+		*objargs = lappend(*objargs, format_type_be_qualified(thisargtype));
+	}
+
+	ReleaseSysCache(proctup);
+}
+
+/*
  * regprocedureout		- converts proc OID to "pro_name(args)"
  */
 Datum
@@ -875,6 +910,31 @@ format_operator_qualified(Oid operator_oid)
 	return format_operator_internal(operator_oid, true);
 }
 
+void
+format_operator_parts(Oid operator_oid, List **objnames, List **objargs)
+{
+	HeapTuple	opertup;
+	Form_pg_operator oprForm;
+
+	opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operator_oid));
+	if (!HeapTupleIsValid(opertup))
+		elog(ERROR, "cache lookup failed for operator with OID %u",
+			 operator_oid);
+
+	oprForm = (Form_pg_operator) GETSTRUCT(opertup);
+	*objnames = list_make2(get_namespace_name(oprForm->oprnamespace),
+						   pstrdup(NameStr(oprForm->oprname)));
+	*objargs = NIL;
+	if (oprForm->oprleft)
+		*objargs = lappend(*objargs,
+						   format_type_be_qualified(oprForm->oprleft));
+	if (oprForm->oprright)
+		*objargs = lappend(*objargs,
+						   format_type_be_qualified(oprForm->oprright));
+
+	ReleaseSysCache(opertup);
+}
+
 /*
  * regoperatorout		- converts operator OID to "opr_name(args)"
  */
diff --git a/src/include/catalog/objectaddress.h b/src/include/catalog/objectaddress.h
index cab9bfe..997a156 100644
--- a/src/include/catalog/objectaddress.h
+++ b/src/include/catalog/objectaddress.h
@@ -58,5 +58,7 @@ extern char *getObjectDescriptionOids(Oid classid, Oid objid);
 extern int unstringify_objtype(const char *objtype);
 extern char *getObjectTypeDescription(const ObjectAddress *object);
 extern char *getObjectIdentity(const ObjectAddress *address);
+extern char *getObjectIdentityParts(const ObjectAddress *address,
+					   List **objname, List **objargs);
 
 #endif   /* OBJECTADDRESS_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 932969a..a93788a 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -5078,7 +5078,8 @@ DATA(insert OID = 3785 (  pg_logical_slot_peek_binary_changes PGNSP PGUID 12 100
 DESCR("peek at binary changes from replication slot");
 
 /* event triggers */
-DATA(insert OID = 3566 (  pg_event_trigger_dropped_objects		PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{26,26,23,16,16,25,25,25,25}" "{o,o,o,o,o,o,o,o,o}" "{classid, objid, objsubid, original, normal, object_type, schema_name, object_name, object_identity}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
+DATA(insert OID = 3566 (  pg_event_trigger_dropped_objects		PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{26,26,23,16,16,25,25,25,25,1009,1009}" "{o,o,o,o,o,o,o,o,o,o,o}" "{classid, objid, objsubid, original, normal, object_type, schema_name, object_name, object_identity, address_names, address_args}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
+
 DESCR("list objects dropped by the current command");
 DATA(insert OID = 4566 (  pg_event_trigger_table_rewrite_oid	PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 26 "" "{26}" "{o}" "{oid}" _null_ pg_event_trigger_table_rewrite_oid _null_ _null_ _null_ ));
 DESCR("return Oid of the table getting rewritten");
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 7c4d291..fbc71be 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -642,8 +642,12 @@ extern Datum text_regclass(PG_FUNCTION_ARGS);
 extern List *stringToQualifiedNameList(const char *string);
 extern char *format_procedure(Oid procedure_oid);
 extern char *format_procedure_qualified(Oid procedure_oid);
+extern void format_procedure_parts(Oid operator_oid, List **objnames,
+					  List **objargs);
 extern char *format_operator(Oid operator_oid);
 extern char *format_operator_qualified(Oid operator_oid);
+extern void format_operator_parts(Oid operator_oid, List **objnames,
+					  List **objargs);
 
 /* rowtypes.c */
 extern Datum record_in(PG_FUNCTION_ARGS);
-- 
2.1.3

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

Reply via email to