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