replicating DROP commands across servers
As you probably know, I've studying how to implement a system to allow
replicating DDL commands to remote servers. I have a fairly good grasp
of handling CREATE commands; we have another thread discussing that
involving a JSON output for such commands. That hasn't been carried to
completion yet; I will finish that up for 9.5. My intention is to have
that code handle ALTER commands as well: I have tried some experiments
and it turns out that it's a bit more complex than I would like, but it
seems doable, now that I've figured out exactly how. (Of course, the
ALTER bits can also be used for auditing and business rule enforcing,
which are the other use-cases for this feature.)
In this thread I focus on DROP. We already added support for dropped
objects in 9.3 in the form of the pg_event_trigger_dropped_objects()
function, which returns the set of objects dropped by any command.
That's quite handy for the auditing purpose it was supposed to serve,
but it's insufficient for replication because it's pretty hard to craft
a command for object deletion on the remote node.
So my current plan involves receiving the set of objects in the remote
node, then adding them to an ObjectAddresses array, then call
performMultipleDeletions() on that. As it turns out, this works quite
well -- except when it doesn't.
The problem is this: at the local node we have the object type and
identity. We can transmit this quite fine from this node to the other
node, and then in the other node parse this back into a objname list and
pass that to get_object_address(); but only for objects that have a
simple representation, such as schemas and relations. For procedures,
constraints, triggers and other types, it's a lot of work to parse the
string into the objname/objargs lists (the identities for such objects
as "constraint_name on schema.tablename" or "schema.funcname(arg1, arg2)"
and so on. Of course, it is *possible* to parse this, but it seems the
wrong thing to do when we could just obtain the right input in the first
place.
My proposal therefore is to add some more columns to
pg_event_trigger_dropped_objects(): more precisely, objname and objargs,
which would carry exactly what get_object_address() would require to
re-construct an ObjectAddress for the object being dropped at the remote
end.
Thoughts, objections?
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Alvaro Herrera <alvherre@2ndquadrant.com> writes:
My proposal therefore is to add some more columns to
pg_event_trigger_dropped_objects(): more precisely, objname and objargs,
which would carry exactly what get_object_address() would require to
re-construct an ObjectAddress for the object being dropped at the remote
end.
Those aren't strings or indeed flat objects at all, but structures, so it
seems like this is still rather underspecified. How will you represent
something like a List of TypeName at the SQL level?
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Tom Lane wrote:
Alvaro Herrera <alvherre@2ndquadrant.com> writes:
My proposal therefore is to add some more columns to
pg_event_trigger_dropped_objects(): more precisely, objname and objargs,
which would carry exactly what get_object_address() would require to
re-construct an ObjectAddress for the object being dropped at the remote
end.Those aren't strings or indeed flat objects at all, but structures, so it
seems like this is still rather underspecified. How will you represent
something like a List of TypeName at the SQL level?
Yeah, that's an ugly case. I'm thinking that I could print those like
regtype output would, and then read them back in using (something
similar to) parseTypeString(). A bit convoluted perhaps, but I think it
should work. For things such as function and cast identities, typmod
shouldn't matter AFAIK, so that loss is not significant.
Another thing this will need is a table such as
static const struct
{
const char *tm_name;
ObjectType tm_type;
}
ObjectTypeMap[] =
{
/* relation types */
{ "table", OBJECT_TABLE },
{ "index", OBJECT_INDEX },
{ "sequence", OBJECT_SEQUENCE },
...
so that we can translate object types back into the ObjectType enum.
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Here's a patch implementing the proposed idea. This is used in the
Bidirectional Replication stuff by Simon/Andres; it works well.
One thing of note is that I added output flags for "normal" and
"original", which mostly come from performDeletion flags. This let one
select only such objects when trying to replicate a drop; otherwise,
we'd add RI triggers to the set to drop remotely, which doesn't work
because their names have OIDs embedded, and in the remote system those
are different.
One curious thing is that I had to add a hack that if an object has a
"reverse" flag in the ObjectAddresses array, also set the "normal"
output flag. (Another possibility would have been to add a "reverse"
output flag, but there doesn't seem to be a use for that --- it seems to
expose internals unnecessarily.)
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Attachments:
improve-drop-info-1.patchtext/x-diff; charset=us-asciiDownload
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 17538,17543 **** FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger();
--- 17538,17556 ----
<entry>Object sub-id (e.g. attribute number for columns)</entry>
</row>
<row>
+ <entry><literal>original</literal></entry>
+ <entry><type>bool</type></entry>
+ <entry>Flag used to identify the root object of the deletion</entry>
+ </row>
+ <row>
+ <entry></literal>normal</literal></entry>
+ <entry><type>bool</type></entry>
+ <entry>
+ Flag indicating that there's a normal dependency relationship
+ in the dependency graph leading to this object
+ </entry>
+ </row>
+ <row>
<entry><literal>object_type</literal></entry>
<entry><type>text</type></entry>
<entry>Type of the object</entry>
***************
*** 17567,17572 **** FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger();
--- 17580,17601 ----
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>
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 203,218 **** deleteObjectsInList(ObjectAddresses *targetObjects, Relation *depRel,
/*
* Keep track of objects for event triggers, if necessary.
*/
! if (trackDroppedObjectsNeeded())
{
for (i = 0; i < targetObjects->numrefs; i++)
{
! ObjectAddress *thisobj = targetObjects->refs + i;
!
! if ((!(flags & PERFORM_DELETION_INTERNAL)) &&
! EventTriggerSupportsObjectClass(getObjectClass(thisobj)))
{
! EventTriggerSQLDropAddObject(thisobj);
}
}
}
--- 203,227 ----
/*
* Keep track of objects for event triggers, if necessary.
*/
! if (trackDroppedObjectsNeeded() && !(flags & PERFORM_DELETION_INTERNAL))
{
for (i = 0; i < targetObjects->numrefs; i++)
{
! const ObjectAddress *thisobj = &targetObjects->refs[i];
! const ObjectAddressExtra *extra = &targetObjects->extras[i];
! bool original = false;
! bool normal = false;
!
! if (extra->flags & DEPFLAG_ORIGINAL)
! original = true;
! if (extra->flags & DEPFLAG_NORMAL)
! normal = true;
! if (extra->flags & DEPFLAG_REVERSE)
! normal = true;
!
! if (EventTriggerSupportsObjectClass(getObjectClass(thisobj)))
{
! EventTriggerSQLDropAddObject(thisobj, original, normal);
}
}
}
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
***************
*** 417,422 **** static const ObjectPropertyType ObjectProperty[] =
--- 417,513 ----
}
};
+ /*
+ * This struct maps the 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 enum. The user of this map must be careful to test for
+ * invalid values being returned.
+ *
+ * This follows the order of getObjectTypeDescription.
+ */
+ static const struct object_type_map
+ {
+ const char *tm_name;
+ ObjectType tm_type;
+ }
+ ObjectTypeMap[] =
+ {
+ /* OCLASS_CLASS */
+ { "table", OBJECT_TABLE },
+ { "index", OBJECT_INDEX },
+ { "sequence", OBJECT_SEQUENCE },
+ { "toast table", -1 }, /* unmapped */
+ { "view", OBJECT_VIEW },
+ { "materialized view", OBJECT_MATVIEW },
+ { "composite type", OBJECT_COMPOSITE },
+ { "foreign table", OBJECT_FOREIGN_TABLE },
+ { "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_CONSTRAINT },
+ { "domain constraint", OBJECT_CONSTRAINT },
+ /* 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", OBJECT_USER_MAPPING },
+ /* OCLASS_DEFACL */
+ { "default acl", -1 }, /* FIXME */
+ /* OCLASS_EXTENSION */
+ { "extension", OBJECT_EXTENSION },
+ /* OCLASS_EVENT_TRIGGER */
+ { "event trigger", OBJECT_EVENT_TRIGGER }
+ };
+
+
static ObjectAddress get_object_address_unqualified(ObjectType objtype,
List *qualname, bool missing_ok);
static ObjectAddress get_relation_by_qualified_name(ObjectType objtype,
***************
*** 439,446 **** 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);
/*
* Translate an object name and arguments (as passed by the parser) to an
--- 530,538 ----
int32 objectSubId);
static void getProcedureTypeDescription(StringInfo buffer, Oid procid);
static void getConstraintTypeDescription(StringInfo buffer, Oid constroid);
! static void getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **objname,
! List **objargs);
! static void getRelationIdentity(StringInfo buffer, Oid relid, List **objname);
/*
* Translate an object name and arguments (as passed by the parser) to an
***************
*** 1347,1352 **** get_object_namespace(const ObjectAddress *address)
--- 1439,1472 ----
}
/*
+ * 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
***************
*** 2442,2447 **** pg_identify_object(PG_FUNCTION_ARGS)
--- 2562,2569 ----
/*
* 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)
***************
*** 2689,2695 **** getProcedureTypeDescription(StringInfo buffer, Oid procid)
}
/*
! * Return a palloc'ed string that identifies an object.
*
* This is for machine consumption, so it's not translated. All elements are
* schema-qualified when appropriate.
--- 2811,2817 ----
}
/*
! * 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.
***************
*** 2697,2710 **** getProcedureTypeDescription(StringInfo buffer, Oid procid)
char *
getObjectIdentity(const ObjectAddress *object)
{
StringInfoData buffer;
initStringInfo(&buffer);
switch (getObjectClass(object))
{
case OCLASS_CLASS:
! getRelationIdentity(&buffer, object->objectId);
if (object->objectSubId != 0)
{
char *attr;
--- 2819,2855 ----
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.
+ * Initialize objargs to empty list, which is the most common case.
+ */
+ Assert(PointerIsValid(objname) == PointerIsValid(objargs));
+ if (objargs)
+ *objargs = NIL;
+
switch (getObjectClass(object))
{
case OCLASS_CLASS:
! getRelationIdentity(&buffer, object->objectId, objname);
if (object->objectSubId != 0)
{
char *attr;
***************
*** 2712,2728 **** getObjectIdentity(const ObjectAddress *object)
attr = get_relid_attribute_name(object->objectId,
object->objectSubId);
appendStringInfo(&buffer, ".%s", quote_identifier(attr));
}
break;
case OCLASS_PROC:
appendStringInfoString(&buffer,
format_procedure_qualified(object->objectId));
break;
case OCLASS_TYPE:
! appendStringInfoString(&buffer,
! format_type_be_qualified(object->objectId));
break;
case OCLASS_CAST:
--- 2857,2883 ----
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:
! {
! char *typeout;
!
! typeout = format_type_be_qualified(object->objectId);
! appendStringInfoString(&buffer, typeout);
! if (objname)
! *objname = list_make1(typeout);
! }
break;
case OCLASS_CAST:
***************
*** 2745,2750 **** getObjectIdentity(const ObjectAddress *object)
--- 2900,2909 ----
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;
}
***************
*** 2765,2770 **** getObjectIdentity(const ObjectAddress *object)
--- 2924,2931 ----
appendStringInfoString(&buffer,
quote_qualified_identifier(schema,
NameStr(coll->collname)));
+ if (objname)
+ *objname = list_make2(schema, NameStr(coll->collname));
ReleaseSysCache(collTup);
break;
}
***************
*** 2785,2791 **** getObjectIdentity(const ObjectAddress *object)
{
appendStringInfo(&buffer, "%s on ",
quote_identifier(NameStr(con->conname)));
! getRelationIdentity(&buffer, con->conrelid);
}
else
{
--- 2946,2954 ----
{
appendStringInfo(&buffer, "%s on ",
quote_identifier(NameStr(con->conname)));
! getRelationIdentity(&buffer, con->conrelid, objname);
! if (objname)
! *objname = lappend(*objname, pstrdup(NameStr(con->conname)));
}
else
{
***************
*** 2798,2803 **** getObjectIdentity(const ObjectAddress *object)
--- 2961,2968 ----
appendStringInfo(&buffer, "%s on %s",
quote_identifier(NameStr(con->conname)),
getObjectIdentity(&domain));
+
+ /* FIXME missing objname/objargs */
}
ReleaseSysCache(conTup);
***************
*** 2817,2822 **** getObjectIdentity(const ObjectAddress *object)
--- 2982,2989 ----
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;
}
***************
*** 2856,2861 **** getObjectIdentity(const ObjectAddress *object)
--- 3023,3030 ----
appendStringInfo(&buffer, "for %s",
getObjectIdentity(&colobject));
+ /* XXX no objname/objargs here */
+
systable_endscan(adscan);
heap_close(attrdefDesc, AccessShareLock);
break;
***************
*** 2874,2890 **** getObjectIdentity(const ObjectAddress *object)
--- 3043,3065 ----
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:
***************
*** 2915,2928 **** getObjectIdentity(const ObjectAddress *object)
NameStr(opcForm->opcname)));
appendStringInfo(&buffer, " for %s",
quote_identifier(NameStr(amForm->amname)));
!
ReleaseSysCache(amTup);
ReleaseSysCache(opcTup);
break;
}
case OCLASS_OPFAMILY:
! getOpFamilyIdentity(&buffer, object->objectId);
break;
case OCLASS_AMOP:
--- 3090,3108 ----
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, objname, objargs);
break;
case OCLASS_AMOP:
***************
*** 2934,2939 **** getObjectIdentity(const ObjectAddress *object)
--- 3114,3123 ----
Form_pg_amop amopForm;
StringInfoData opfam;
+ /* no objname support here */
+ if (objname)
+ *objname = NIL;
+
amopDesc = heap_open(AccessMethodOperatorRelationId,
AccessShareLock);
***************
*** 2954,2960 **** getObjectIdentity(const ObjectAddress *object)
amopForm = (Form_pg_amop) GETSTRUCT(tup);
initStringInfo(&opfam);
! getOpFamilyIdentity(&opfam, amopForm->amopfamily);
appendStringInfo(&buffer, "operator %d (%s, %s) of %s",
amopForm->amopstrategy,
--- 3138,3144 ----
amopForm = (Form_pg_amop) GETSTRUCT(tup);
initStringInfo(&opfam);
! getOpFamilyIdentity(&opfam, amopForm->amopfamily, NULL, NULL);
appendStringInfo(&buffer, "operator %d (%s, %s) of %s",
amopForm->amopstrategy,
***************
*** 2978,2983 **** getObjectIdentity(const ObjectAddress *object)
--- 3162,3171 ----
Form_pg_amproc amprocForm;
StringInfoData opfam;
+ /* no objname support here */
+ if (objname)
+ *objname = NIL;
+
amprocDesc = heap_open(AccessMethodProcedureRelationId,
AccessShareLock);
***************
*** 2998,3004 **** getObjectIdentity(const ObjectAddress *object)
amprocForm = (Form_pg_amproc) GETSTRUCT(tup);
initStringInfo(&opfam);
! getOpFamilyIdentity(&opfam, amprocForm->amprocfamily);
appendStringInfo(&buffer, "function %d (%s, %s) of %s",
amprocForm->amprocnum,
--- 3186,3192 ----
amprocForm = (Form_pg_amproc) GETSTRUCT(tup);
initStringInfo(&opfam);
! getOpFamilyIdentity(&opfam, amprocForm->amprocfamily, NULL, NULL);
appendStringInfo(&buffer, "function %d (%s, %s) of %s",
amprocForm->amprocnum,
***************
*** 3031,3037 **** getObjectIdentity(const ObjectAddress *object)
appendStringInfo(&buffer, "%s on ",
quote_identifier(NameStr(rule->rulename)));
! getRelationIdentity(&buffer, rule->ev_class);
heap_close(ruleDesc, AccessShareLock);
break;
--- 3219,3227 ----
appendStringInfo(&buffer, "%s on ",
quote_identifier(NameStr(rule->rulename)));
! getRelationIdentity(&buffer, rule->ev_class, objname);
! if (objname)
! *objname = lappend(*objname, NameStr(rule->rulename));
heap_close(ruleDesc, AccessShareLock);
break;
***************
*** 3055,3061 **** getObjectIdentity(const ObjectAddress *object)
appendStringInfo(&buffer, "%s on ",
quote_identifier(NameStr(trig->tgname)));
! getRelationIdentity(&buffer, trig->tgrelid);
heap_close(trigDesc, AccessShareLock);
break;
--- 3245,3253 ----
appendStringInfo(&buffer, "%s on ",
quote_identifier(NameStr(trig->tgname)));
! getRelationIdentity(&buffer, trig->tgrelid, objname);
! if (objname)
! *objname = lappend(*objname, NameStr(trig->tgname));
heap_close(trigDesc, AccessShareLock);
break;
***************
*** 3071,3076 **** getObjectIdentity(const ObjectAddress *object)
--- 3263,3270 ----
object->objectId);
appendStringInfoString(&buffer,
quote_identifier(nspname));
+ if (objname)
+ *objname = list_make1(nspname);
break;
}
***************
*** 3090,3095 **** getObjectIdentity(const ObjectAddress *object)
--- 3284,3292 ----
appendStringInfoString(&buffer,
quote_qualified_identifier(schema,
NameStr(formParser->prsname)));
+ if (objname)
+ *objname = list_make2(schema,
+ pstrdup(NameStr(formParser->prsname)));
ReleaseSysCache(tup);
break;
}
***************
*** 3110,3115 **** getObjectIdentity(const ObjectAddress *object)
--- 3307,3315 ----
appendStringInfoString(&buffer,
quote_qualified_identifier(schema,
NameStr(formDict->dictname)));
+ if (objname)
+ *objname = list_make2(schema,
+ pstrdup(NameStr(formDict->dictname)));
ReleaseSysCache(tup);
break;
}
***************
*** 3130,3136 **** getObjectIdentity(const ObjectAddress *object)
appendStringInfoString(&buffer,
quote_qualified_identifier(schema,
NameStr(formTmpl->tmplname)));
! pfree(schema);
ReleaseSysCache(tup);
break;
}
--- 3330,3338 ----
appendStringInfoString(&buffer,
quote_qualified_identifier(schema,
NameStr(formTmpl->tmplname)));
! if (objname)
! *objname = list_make2(schema,
! pstrdup(NameStr(formTmpl->tmplname)));
ReleaseSysCache(tup);
break;
}
***************
*** 3151,3156 **** getObjectIdentity(const ObjectAddress *object)
--- 3353,3361 ----
appendStringInfoString(&buffer,
quote_qualified_identifier(schema,
NameStr(formCfg->cfgname)));
+ if (objname)
+ *objname = list_make2(schema,
+ pstrdup(NameStr(formCfg->cfgname)));
ReleaseSysCache(tup);
break;
}
***************
*** 3159,3164 **** getObjectIdentity(const ObjectAddress *object)
--- 3364,3372 ----
{
char *username;
+ /* no objname support here */
+ Assert(objname == NULL);
+
username = GetUserNameFromId(object->objectId);
appendStringInfoString(&buffer,
quote_identifier(username));
***************
*** 3169,3174 **** getObjectIdentity(const ObjectAddress *object)
--- 3377,3385 ----
{
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",
***************
*** 3182,3187 **** getObjectIdentity(const ObjectAddress *object)
--- 3393,3401 ----
{
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",
***************
*** 3197,3202 **** getObjectIdentity(const ObjectAddress *object)
--- 3411,3418 ----
fdw = GetForeignDataWrapper(object->objectId);
appendStringInfoString(&buffer, quote_identifier(fdw->fdwname));
+ if (objname)
+ *objname = list_make1(pstrdup(fdw->fdwname));
break;
}
***************
*** 3207,3212 **** getObjectIdentity(const ObjectAddress *object)
--- 3423,3430 ----
srv = GetForeignServer(object->objectId);
appendStringInfoString(&buffer,
quote_identifier(srv->servername));
+ if (objname)
+ *objname = list_make1(pstrdup(srv->servername));
break;
}
***************
*** 3216,3221 **** getObjectIdentity(const ObjectAddress *object)
--- 3434,3441 ----
Oid useid;
const char *usename;
+ /* XXX get_object_address doesn't seem to support this */
+
tup = SearchSysCache1(USERMAPPINGOID,
ObjectIdGetDatum(object->objectId));
if (!HeapTupleIsValid(tup))
***************
*** 3240,3249 **** getObjectIdentity(const ObjectAddress *object)
Relation defaclrel;
ScanKeyData skey[1];
SysScanDesc rcscan;
-
HeapTuple tup;
Form_pg_default_acl defacl;
defaclrel = heap_open(DefaultAclRelationId, AccessShareLock);
ScanKeyInit(&skey[0],
--- 3460,3470 ----
Relation defaclrel;
ScanKeyData skey[1];
SysScanDesc rcscan;
HeapTuple tup;
Form_pg_default_acl defacl;
+ /* XXX get_object_address doesn't seem to support this */
+
defaclrel = heap_open(DefaultAclRelationId, AccessShareLock);
ScanKeyInit(&skey[0],
***************
*** 3310,3315 **** getObjectIdentity(const ObjectAddress *object)
--- 3531,3538 ----
elog(ERROR, "cache lookup failed for extension %u",
object->objectId);
appendStringInfoString(&buffer, quote_identifier(extname));
+ if (objname)
+ *objname = list_make1(extname);
break;
}
***************
*** 3318,3323 **** getObjectIdentity(const ObjectAddress *object)
--- 3541,3549 ----
HeapTuple tup;
Form_pg_event_trigger trigForm;
+ /* no objname support here */
+ Assert(objname == NULL);
+
tup = SearchSysCache1(EVENTTRIGGEROID,
ObjectIdGetDatum(object->objectId));
if (!HeapTupleIsValid(tup))
***************
*** 3342,3348 **** getObjectIdentity(const ObjectAddress *object)
}
static void
! getOpFamilyIdentity(StringInfo buffer, Oid opfid)
{
HeapTuple opfTup;
Form_pg_opfamily opfForm;
--- 3568,3574 ----
}
static void
! getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **objname, List **objargs)
{
HeapTuple opfTup;
Form_pg_opfamily opfForm;
***************
*** 3367,3372 **** getOpFamilyIdentity(StringInfo buffer, Oid opfid)
--- 3593,3605 ----
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);
}
***************
*** 3376,3382 **** getOpFamilyIdentity(StringInfo buffer, Oid opfid)
* StringInfo.
*/
static void
! getRelationIdentity(StringInfo buffer, Oid relid)
{
HeapTuple relTup;
Form_pg_class relForm;
--- 3609,3615 ----
* StringInfo.
*/
static void
! getRelationIdentity(StringInfo buffer, Oid relid, List **objname)
{
HeapTuple relTup;
Form_pg_class relForm;
***************
*** 3392,3397 **** getRelationIdentity(StringInfo buffer, Oid relid)
--- 3625,3632 ----
appendStringInfoString(buffer,
quote_qualified_identifier(schema,
NameStr(relForm->relname)));
+ if (objname)
+ *objname = list_make2(schema, pstrdup(NameStr(relForm->relname)));
ReleaseSysCache(relTup);
}
*** a/src/backend/commands/event_trigger.c
--- b/src/backend/commands/event_trigger.c
***************
*** 111,116 **** typedef struct SQLDropObject
--- 111,121 ----
const char *objname;
const char *objidentity;
const char *objecttype;
+ List *addrnames;
+ List *addrargs;
+ ObjectAddress dependee;
+ bool original;
+ bool normal;
slist_node next;
} SQLDropObject;
***************
*** 920,928 **** EventTriggerSupportsObjectType(ObjectType obtype)
--- 925,935 ----
case OBJECT_ATTRIBUTE:
case OBJECT_CAST:
case OBJECT_COLUMN:
+ case OBJECT_COMPOSITE:
case OBJECT_CONSTRAINT:
case OBJECT_COLLATION:
case OBJECT_CONVERSION:
+ case OBJECT_DEFAULT:
case OBJECT_DOMAIN:
case OBJECT_EXTENSION:
case OBJECT_FDW:
***************
*** 946,951 **** EventTriggerSupportsObjectType(ObjectType obtype)
--- 953,959 ----
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
case OBJECT_TYPE:
+ case OBJECT_USER_MAPPING:
case OBJECT_VIEW:
return true;
}
***************
*** 1102,1108 **** trackDroppedObjectsNeeded(void)
* Register one object as being dropped by the current command.
*/
void
! EventTriggerSQLDropAddObject(ObjectAddress *object)
{
SQLDropObject *obj;
MemoryContext oldcxt;
--- 1110,1116 ----
* Register one object as being dropped by the current command.
*/
void
! EventTriggerSQLDropAddObject(const ObjectAddress *object, bool original, bool normal)
{
SQLDropObject *obj;
MemoryContext oldcxt;
***************
*** 1121,1126 **** EventTriggerSQLDropAddObject(ObjectAddress *object)
--- 1129,1136 ----
obj = palloc0(sizeof(SQLDropObject));
obj->address = *object;
+ obj->original = original;
+ obj->normal = normal;
/*
* Obtain schema names from the object's catalog tuple, if one exists;
***************
*** 1182,1191 **** EventTriggerSQLDropAddObject(ObjectAddress *object)
heap_close(catalog, AccessShareLock);
}
! /* object identity */
! obj->objidentity = getObjectIdentity(&obj->address);
! /* and object type, too */
obj->objecttype = getObjectTypeDescription(&obj->address);
slist_push_head(&(currentEventTriggerState->SQLDropList), &obj->next);
--- 1192,1202 ----
heap_close(catalog, AccessShareLock);
}
! /* object identity, objname and objargs */
! obj->objidentity =
! getObjectIdentityParts(&obj->address, &obj->addrnames, &obj->addrargs);
! /* object type */
obj->objecttype = getObjectTypeDescription(&obj->address);
slist_push_head(&(currentEventTriggerState->SQLDropList), &obj->next);
***************
*** 1248,1255 **** pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
{
SQLDropObject *obj;
int i = 0;
! Datum values[7];
! bool nulls[7];
obj = slist_container(SQLDropObject, next, iter.cur);
--- 1259,1266 ----
{
SQLDropObject *obj;
int i = 0;
! Datum values[11];
! bool nulls[11];
obj = slist_container(SQLDropObject, next, iter.cur);
***************
*** 1265,1270 **** pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
--- 1276,1287 ----
/* objsubid */
values[i++] = Int32GetDatum(obj->address.objectSubId);
+ /* original */
+ values[i++] = BoolGetDatum(obj->original);
+
+ /* normal */
+ values[i++] = BoolGetDatum(obj->normal);
+
/* object_type */
values[i++] = CStringGetTextDatum(obj->objecttype);
***************
*** 1286,1291 **** pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
--- 1303,1356 ----
else
nulls[i++] = true;
+ /* address_names */
+ if (obj->addrnames)
+ {
+ ArrayType *arr;
+ Datum *datums;
+ int j = 0;
+ ListCell *cell;
+
+ datums = palloc(sizeof(text *) * list_length(obj->addrnames));
+ foreach(cell, obj->addrnames)
+ {
+ char *name = lfirst(cell);
+
+ datums[j++] = CStringGetTextDatum(name);
+ }
+
+ arr = construct_array(datums, list_length(obj->addrnames),
+ TEXTOID, -1, false, 'i');
+
+ values[i++] = PointerGetDatum(arr);
+ }
+ else
+ nulls[i++] = true;
+
+ /* address_args */
+ /* FIXME duplicated code block ... */
+ if (obj->addrargs)
+ {
+ ArrayType *arr;
+ Datum *datums;
+ int j = 0;
+ ListCell *cell;
+
+ datums = palloc(sizeof(text *) * list_length(obj->addrargs));
+ foreach(cell, obj->addrargs)
+ {
+ char *arg = lfirst(cell);
+
+ datums[j++] = CStringGetTextDatum(arg);
+ }
+
+ arr = construct_array(datums, list_length(obj->addrargs),
+ TEXTOID, -1, false, 'i');
+ values[i++] = PointerGetDatum(arr);
+ }
+ else
+ nulls[i++] = true;
+
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
}
*** a/src/backend/parser/parse_type.c
--- b/src/backend/parser/parse_type.c
***************
*** 705,717 **** 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.
*/
! void
! parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p,
! bool missing_ok)
{
StringInfoData buf;
List *raw_parsetree_list;
--- 705,715 ----
/*
* 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 return the result as a TypeName.
! * If the string cannot be parsed as a type, an error is raised.
*/
! TypeName *
! typeStringToTypeName(const char *str)
{
StringInfoData buf;
List *raw_parsetree_list;
***************
*** 720,726 **** 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))
--- 718,723 ----
***************
*** 779,784 **** parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p,
--- 776,782 ----
typecast->arg == NULL ||
!IsA(typecast->arg, A_Const))
goto fail;
+
typeName = typecast->typeName;
if (typeName == NULL ||
!IsA(typeName, TypeName))
***************
*** 786,791 **** parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p,
--- 784,814 ----
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,820 **** 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)));
}
--- 831,834 ----
*** a/src/backend/utils/adt/regproc.c
--- b/src/backend/utils/adt/regproc.c
***************
*** 439,444 **** format_procedure_internal(Oid procedure_oid, bool force_qualify)
--- 439,479 ----
}
/*
+ * 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,880 **** format_operator_qualified(Oid operator_oid)
--- 910,940 ----
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)"
*/
*** a/src/include/catalog/objectaddress.h
--- b/src/include/catalog/objectaddress.h
***************
*** 55,61 **** extern HeapTuple get_catalog_object_by_oid(Relation catalog,
--- 55,64 ----
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);
+ extern char *getObjectIdentityParts(const ObjectAddress *address,
+ List **objname, List **objargs);
#endif /* OBJECTADDRESS_H */
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 4978,4984 **** 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,25,25,25,25}" "{o,o,o,o,o,o,o}" "{classid, objid, objsubid, object_type, schema_name, object_name, object_identity}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
DESCR("list objects dropped by the current command");
/* generic transition functions for ordered-set aggregates */
--- 4978,4984 ----
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,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");
/* generic transition functions for ordered-set aggregates */
*** a/src/include/commands/event_trigger.h
--- b/src/include/commands/event_trigger.h
***************
*** 50,55 **** extern void EventTriggerSQLDrop(Node *parsetree);
extern bool EventTriggerBeginCompleteQuery(void);
extern void EventTriggerEndCompleteQuery(void);
extern bool trackDroppedObjectsNeeded(void);
! extern void EventTriggerSQLDropAddObject(ObjectAddress *object);
#endif /* EVENT_TRIGGER_H */
--- 50,56 ----
extern bool EventTriggerBeginCompleteQuery(void);
extern void EventTriggerEndCompleteQuery(void);
extern bool trackDroppedObjectsNeeded(void);
! extern void EventTriggerSQLDropAddObject(const ObjectAddress *object,
! bool original, bool normal);
#endif /* EVENT_TRIGGER_H */
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 1192,1201 **** typedef enum ObjectType
--- 1192,1203 ----
OBJECT_ATTRIBUTE, /* type's attribute, when distinct from column */
OBJECT_CAST,
OBJECT_COLUMN,
+ OBJECT_COMPOSITE,
OBJECT_CONSTRAINT,
OBJECT_COLLATION,
OBJECT_CONVERSION,
OBJECT_DATABASE,
+ OBJECT_DEFAULT,
OBJECT_DOMAIN,
OBJECT_EVENT_TRIGGER,
OBJECT_EXTENSION,
***************
*** 1222,1227 **** typedef enum ObjectType
--- 1224,1230 ----
OBJECT_TSPARSER,
OBJECT_TSTEMPLATE,
OBJECT_TYPE,
+ OBJECT_USER_MAPPING,
OBJECT_VIEW
} ObjectType;
*** a/src/include/parser/parse_type.h
--- b/src/include/parser/parse_type.h
***************
*** 47,52 **** extern Datum stringTypeDatum(Type tp, char *string, int32 atttypmod);
--- 47,53 ----
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)
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 641,648 **** extern Datum text_regclass(PG_FUNCTION_ARGS);
--- 641,652 ----
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);
Hi.
I thought I'd review this patch, since pgaudit uses the
pg_event_trigger_dropped_objects function.
I went through the patch line by line, and I don't really have anything
to say about it. I notice that there are some XXX/FIXME comments in the
code, but it's not clear if those need to (or can be) fixed before the
code is committed.
Everything else looks good. I think this is ready for committer.
-- Abhijit
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 2014-06-13 15:50:50 -0400, Alvaro Herrera wrote:
Here's a patch implementing the proposed idea. This is used in the
Bidirectional Replication stuff by Simon/Andres; it works well.
I think there's been some changes to this patch since july, care to
resend a new version?
I think it's appropriate to mark the patch as "Waiting for Author"
instead of "Ready for Committer" till then.
Greetings,
Andres Freund
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Andres Freund wrote:
On 2014-06-13 15:50:50 -0400, Alvaro Herrera wrote:
Here's a patch implementing the proposed idea. This is used in the
Bidirectional Replication stuff by Simon/Andres; it works well.I think there's been some changes to this patch since july, care to
resend a new version?
Sure, here it is.
The only difference with the previous version is that it now also
supports column defaults. This was found to be a problem when you drop
a sequence that some column default depends on -- for example a column
declared SERIAL, or a sequence marked with ALTER SEQUENCE OWNED BY. The
new code is able to drop both the sequence and the default value
(leaving, of course, the rest of the column intact.) This required
adding support for such objects in get_object_address.
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Attachments:
improve-drop-info-2.patchtext/x-diff; charset=us-asciiDownload
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 17564,17569 **** FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger();
--- 17564,17582 ----
<entry>Object sub-id (e.g. attribute number for columns)</entry>
</row>
<row>
+ <entry><literal>original</literal></entry>
+ <entry><type>bool</type></entry>
+ <entry>Flag used to identify the root object of the deletion</entry>
+ </row>
+ <row>
+ <entry><literal>normal</literal></entry>
+ <entry><type>bool</type></entry>
+ <entry>
+ Flag indicating that there's a normal dependency relationship
+ in the dependency graph leading to this object
+ </entry>
+ </row>
+ <row>
<entry><literal>object_type</literal></entry>
<entry><type>text</type></entry>
<entry>Type of the object</entry>
***************
*** 17593,17598 **** FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger();
--- 17606,17627 ----
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>
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 203,218 **** deleteObjectsInList(ObjectAddresses *targetObjects, Relation *depRel,
/*
* Keep track of objects for event triggers, if necessary.
*/
! if (trackDroppedObjectsNeeded())
{
for (i = 0; i < targetObjects->numrefs; i++)
{
! ObjectAddress *thisobj = targetObjects->refs + i;
!
! if ((!(flags & PERFORM_DELETION_INTERNAL)) &&
! EventTriggerSupportsObjectClass(getObjectClass(thisobj)))
{
! EventTriggerSQLDropAddObject(thisobj);
}
}
}
--- 203,227 ----
/*
* Keep track of objects for event triggers, if necessary.
*/
! if (trackDroppedObjectsNeeded() && !(flags & PERFORM_DELETION_INTERNAL))
{
for (i = 0; i < targetObjects->numrefs; i++)
{
! const ObjectAddress *thisobj = &targetObjects->refs[i];
! const ObjectAddressExtra *extra = &targetObjects->extras[i];
! bool original = false;
! bool normal = false;
!
! if (extra->flags & DEPFLAG_ORIGINAL)
! original = true;
! if (extra->flags & DEPFLAG_NORMAL)
! normal = true;
! if (extra->flags & DEPFLAG_REVERSE)
! normal = true;
!
! if (EventTriggerSupportsObjectClass(getObjectClass(thisobj)))
{
! EventTriggerSQLDropAddObject(thisobj, original, normal);
}
}
}
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
***************
*** 417,422 **** static const ObjectPropertyType ObjectProperty[] =
--- 417,513 ----
}
};
+ /*
+ * This struct maps the 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 enum. The user of this map must be careful to test for
+ * invalid values being returned.
+ *
+ * This follows the order of getObjectTypeDescription.
+ */
+ static const struct object_type_map
+ {
+ const char *tm_name;
+ ObjectType tm_type;
+ }
+ ObjectTypeMap[] =
+ {
+ /* OCLASS_CLASS */
+ { "table", OBJECT_TABLE },
+ { "index", OBJECT_INDEX },
+ { "sequence", OBJECT_SEQUENCE },
+ { "toast table", -1 }, /* unmapped */
+ { "view", OBJECT_VIEW },
+ { "materialized view", OBJECT_MATVIEW },
+ { "composite type", OBJECT_COMPOSITE },
+ { "foreign table", OBJECT_FOREIGN_TABLE },
+ { "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_CONSTRAINT },
+ { "domain constraint", OBJECT_CONSTRAINT },
+ /* 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", OBJECT_USER_MAPPING },
+ /* OCLASS_DEFACL */
+ { "default acl", -1 }, /* FIXME */
+ /* OCLASS_EXTENSION */
+ { "extension", OBJECT_EXTENSION },
+ /* OCLASS_EVENT_TRIGGER */
+ { "event trigger", OBJECT_EVENT_TRIGGER }
+ };
+
+
static ObjectAddress get_object_address_unqualified(ObjectType objtype,
List *qualname, bool missing_ok);
static ObjectAddress get_relation_by_qualified_name(ObjectType objtype,
***************
*** 427,432 **** static ObjectAddress get_object_address_relobject(ObjectType objtype,
--- 518,526 ----
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,
***************
*** 439,446 **** 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);
/*
* Translate an object name and arguments (as passed by the parser) to an
--- 533,541 ----
int32 objectSubId);
static void getProcedureTypeDescription(StringInfo buffer, Oid procid);
static void getConstraintTypeDescription(StringInfo buffer, Oid constroid);
! static void getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **objname,
! List **objargs);
! static void getRelationIdentity(StringInfo buffer, Oid relid, List **objname);
/*
* Translate an object name and arguments (as passed by the parser) to an
***************
*** 514,519 **** get_object_address(ObjectType objtype, List *objname, List *objargs,
--- 609,620 ----
&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_CONSTRAINT:
***************
*** 1058,1063 **** get_object_address_attribute(ObjectType objtype, List *objname,
--- 1159,1246 ----
}
/*
+ * 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(lfirst(list_tail(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
***************
*** 1347,1352 **** get_object_namespace(const ObjectAddress *address)
--- 1530,1563 ----
}
/*
+ * 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
***************
*** 2442,2447 **** pg_identify_object(PG_FUNCTION_ARGS)
--- 2653,2660 ----
/*
* 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)
***************
*** 2689,2695 **** getProcedureTypeDescription(StringInfo buffer, Oid procid)
}
/*
! * Return a palloc'ed string that identifies an object.
*
* This is for machine consumption, so it's not translated. All elements are
* schema-qualified when appropriate.
--- 2902,2908 ----
}
/*
! * 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.
***************
*** 2697,2710 **** getProcedureTypeDescription(StringInfo buffer, Oid procid)
char *
getObjectIdentity(const ObjectAddress *object)
{
StringInfoData buffer;
initStringInfo(&buffer);
switch (getObjectClass(object))
{
case OCLASS_CLASS:
! getRelationIdentity(&buffer, object->objectId);
if (object->objectSubId != 0)
{
char *attr;
--- 2910,2946 ----
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.
+ * Initialize objargs to empty list, which is the most common case.
+ */
+ Assert(PointerIsValid(objname) == PointerIsValid(objargs));
+ if (objargs)
+ *objargs = NIL;
+
switch (getObjectClass(object))
{
case OCLASS_CLASS:
! getRelationIdentity(&buffer, object->objectId, objname);
if (object->objectSubId != 0)
{
char *attr;
***************
*** 2712,2728 **** getObjectIdentity(const ObjectAddress *object)
attr = get_relid_attribute_name(object->objectId,
object->objectSubId);
appendStringInfo(&buffer, ".%s", quote_identifier(attr));
}
break;
case OCLASS_PROC:
appendStringInfoString(&buffer,
format_procedure_qualified(object->objectId));
break;
case OCLASS_TYPE:
! appendStringInfoString(&buffer,
! format_type_be_qualified(object->objectId));
break;
case OCLASS_CAST:
--- 2948,2974 ----
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:
! {
! char *typeout;
!
! typeout = format_type_be_qualified(object->objectId);
! appendStringInfoString(&buffer, typeout);
! if (objname)
! *objname = list_make1(typeout);
! }
break;
case OCLASS_CAST:
***************
*** 2745,2750 **** getObjectIdentity(const ObjectAddress *object)
--- 2991,3000 ----
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;
}
***************
*** 2765,2770 **** getObjectIdentity(const ObjectAddress *object)
--- 3015,3022 ----
appendStringInfoString(&buffer,
quote_qualified_identifier(schema,
NameStr(coll->collname)));
+ if (objname)
+ *objname = list_make2(schema, NameStr(coll->collname));
ReleaseSysCache(collTup);
break;
}
***************
*** 2785,2791 **** getObjectIdentity(const ObjectAddress *object)
{
appendStringInfo(&buffer, "%s on ",
quote_identifier(NameStr(con->conname)));
! getRelationIdentity(&buffer, con->conrelid);
}
else
{
--- 3037,3045 ----
{
appendStringInfo(&buffer, "%s on ",
quote_identifier(NameStr(con->conname)));
! getRelationIdentity(&buffer, con->conrelid, objname);
! if (objname)
! *objname = lappend(*objname, pstrdup(NameStr(con->conname)));
}
else
{
***************
*** 2798,2803 **** getObjectIdentity(const ObjectAddress *object)
--- 3052,3059 ----
appendStringInfo(&buffer, "%s on %s",
quote_identifier(NameStr(con->conname)),
getObjectIdentity(&domain));
+
+ /* FIXME missing objname/objargs */
}
ReleaseSysCache(conTup);
***************
*** 2817,2822 **** getObjectIdentity(const ObjectAddress *object)
--- 3073,3080 ----
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;
}
***************
*** 2854,2860 **** getObjectIdentity(const ObjectAddress *object)
colobject.objectSubId = attrdef->adnum;
appendStringInfo(&buffer, "for %s",
! getObjectIdentity(&colobject));
systable_endscan(adscan);
heap_close(attrdefDesc, AccessShareLock);
--- 3112,3119 ----
colobject.objectSubId = attrdef->adnum;
appendStringInfo(&buffer, "for %s",
! getObjectIdentityParts(&colobject,
! objname, objargs));
systable_endscan(adscan);
heap_close(attrdefDesc, AccessShareLock);
***************
*** 2874,2890 **** getObjectIdentity(const ObjectAddress *object)
--- 3133,3155 ----
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:
***************
*** 2915,2928 **** getObjectIdentity(const ObjectAddress *object)
NameStr(opcForm->opcname)));
appendStringInfo(&buffer, " for %s",
quote_identifier(NameStr(amForm->amname)));
!
ReleaseSysCache(amTup);
ReleaseSysCache(opcTup);
break;
}
case OCLASS_OPFAMILY:
! getOpFamilyIdentity(&buffer, object->objectId);
break;
case OCLASS_AMOP:
--- 3180,3198 ----
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, objname, objargs);
break;
case OCLASS_AMOP:
***************
*** 2934,2939 **** getObjectIdentity(const ObjectAddress *object)
--- 3204,3213 ----
Form_pg_amop amopForm;
StringInfoData opfam;
+ /* no objname support here */
+ if (objname)
+ *objname = NIL;
+
amopDesc = heap_open(AccessMethodOperatorRelationId,
AccessShareLock);
***************
*** 2954,2960 **** getObjectIdentity(const ObjectAddress *object)
amopForm = (Form_pg_amop) GETSTRUCT(tup);
initStringInfo(&opfam);
! getOpFamilyIdentity(&opfam, amopForm->amopfamily);
appendStringInfo(&buffer, "operator %d (%s, %s) of %s",
amopForm->amopstrategy,
--- 3228,3234 ----
amopForm = (Form_pg_amop) GETSTRUCT(tup);
initStringInfo(&opfam);
! getOpFamilyIdentity(&opfam, amopForm->amopfamily, NULL, NULL);
appendStringInfo(&buffer, "operator %d (%s, %s) of %s",
amopForm->amopstrategy,
***************
*** 2978,2983 **** getObjectIdentity(const ObjectAddress *object)
--- 3252,3261 ----
Form_pg_amproc amprocForm;
StringInfoData opfam;
+ /* no objname support here */
+ if (objname)
+ *objname = NIL;
+
amprocDesc = heap_open(AccessMethodProcedureRelationId,
AccessShareLock);
***************
*** 2998,3004 **** getObjectIdentity(const ObjectAddress *object)
amprocForm = (Form_pg_amproc) GETSTRUCT(tup);
initStringInfo(&opfam);
! getOpFamilyIdentity(&opfam, amprocForm->amprocfamily);
appendStringInfo(&buffer, "function %d (%s, %s) of %s",
amprocForm->amprocnum,
--- 3276,3282 ----
amprocForm = (Form_pg_amproc) GETSTRUCT(tup);
initStringInfo(&opfam);
! getOpFamilyIdentity(&opfam, amprocForm->amprocfamily, NULL, NULL);
appendStringInfo(&buffer, "function %d (%s, %s) of %s",
amprocForm->amprocnum,
***************
*** 3031,3037 **** getObjectIdentity(const ObjectAddress *object)
appendStringInfo(&buffer, "%s on ",
quote_identifier(NameStr(rule->rulename)));
! getRelationIdentity(&buffer, rule->ev_class);
heap_close(ruleDesc, AccessShareLock);
break;
--- 3309,3317 ----
appendStringInfo(&buffer, "%s on ",
quote_identifier(NameStr(rule->rulename)));
! getRelationIdentity(&buffer, rule->ev_class, objname);
! if (objname)
! *objname = lappend(*objname, NameStr(rule->rulename));
heap_close(ruleDesc, AccessShareLock);
break;
***************
*** 3055,3061 **** getObjectIdentity(const ObjectAddress *object)
appendStringInfo(&buffer, "%s on ",
quote_identifier(NameStr(trig->tgname)));
! getRelationIdentity(&buffer, trig->tgrelid);
heap_close(trigDesc, AccessShareLock);
break;
--- 3335,3343 ----
appendStringInfo(&buffer, "%s on ",
quote_identifier(NameStr(trig->tgname)));
! getRelationIdentity(&buffer, trig->tgrelid, objname);
! if (objname)
! *objname = lappend(*objname, NameStr(trig->tgname));
heap_close(trigDesc, AccessShareLock);
break;
***************
*** 3071,3076 **** getObjectIdentity(const ObjectAddress *object)
--- 3353,3360 ----
object->objectId);
appendStringInfoString(&buffer,
quote_identifier(nspname));
+ if (objname)
+ *objname = list_make1(nspname);
break;
}
***************
*** 3090,3095 **** getObjectIdentity(const ObjectAddress *object)
--- 3374,3382 ----
appendStringInfoString(&buffer,
quote_qualified_identifier(schema,
NameStr(formParser->prsname)));
+ if (objname)
+ *objname = list_make2(schema,
+ pstrdup(NameStr(formParser->prsname)));
ReleaseSysCache(tup);
break;
}
***************
*** 3110,3115 **** getObjectIdentity(const ObjectAddress *object)
--- 3397,3405 ----
appendStringInfoString(&buffer,
quote_qualified_identifier(schema,
NameStr(formDict->dictname)));
+ if (objname)
+ *objname = list_make2(schema,
+ pstrdup(NameStr(formDict->dictname)));
ReleaseSysCache(tup);
break;
}
***************
*** 3130,3136 **** getObjectIdentity(const ObjectAddress *object)
appendStringInfoString(&buffer,
quote_qualified_identifier(schema,
NameStr(formTmpl->tmplname)));
! pfree(schema);
ReleaseSysCache(tup);
break;
}
--- 3420,3428 ----
appendStringInfoString(&buffer,
quote_qualified_identifier(schema,
NameStr(formTmpl->tmplname)));
! if (objname)
! *objname = list_make2(schema,
! pstrdup(NameStr(formTmpl->tmplname)));
ReleaseSysCache(tup);
break;
}
***************
*** 3151,3156 **** getObjectIdentity(const ObjectAddress *object)
--- 3443,3451 ----
appendStringInfoString(&buffer,
quote_qualified_identifier(schema,
NameStr(formCfg->cfgname)));
+ if (objname)
+ *objname = list_make2(schema,
+ pstrdup(NameStr(formCfg->cfgname)));
ReleaseSysCache(tup);
break;
}
***************
*** 3159,3164 **** getObjectIdentity(const ObjectAddress *object)
--- 3454,3462 ----
{
char *username;
+ /* no objname support here */
+ Assert(objname == NULL);
+
username = GetUserNameFromId(object->objectId);
appendStringInfoString(&buffer,
quote_identifier(username));
***************
*** 3169,3174 **** getObjectIdentity(const ObjectAddress *object)
--- 3467,3475 ----
{
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",
***************
*** 3182,3187 **** getObjectIdentity(const ObjectAddress *object)
--- 3483,3491 ----
{
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",
***************
*** 3197,3202 **** getObjectIdentity(const ObjectAddress *object)
--- 3501,3508 ----
fdw = GetForeignDataWrapper(object->objectId);
appendStringInfoString(&buffer, quote_identifier(fdw->fdwname));
+ if (objname)
+ *objname = list_make1(pstrdup(fdw->fdwname));
break;
}
***************
*** 3207,3212 **** getObjectIdentity(const ObjectAddress *object)
--- 3513,3520 ----
srv = GetForeignServer(object->objectId);
appendStringInfoString(&buffer,
quote_identifier(srv->servername));
+ if (objname)
+ *objname = list_make1(pstrdup(srv->servername));
break;
}
***************
*** 3216,3221 **** getObjectIdentity(const ObjectAddress *object)
--- 3524,3531 ----
Oid useid;
const char *usename;
+ /* XXX get_object_address doesn't seem to support this */
+
tup = SearchSysCache1(USERMAPPINGOID,
ObjectIdGetDatum(object->objectId));
if (!HeapTupleIsValid(tup))
***************
*** 3240,3249 **** getObjectIdentity(const ObjectAddress *object)
Relation defaclrel;
ScanKeyData skey[1];
SysScanDesc rcscan;
-
HeapTuple tup;
Form_pg_default_acl defacl;
defaclrel = heap_open(DefaultAclRelationId, AccessShareLock);
ScanKeyInit(&skey[0],
--- 3550,3560 ----
Relation defaclrel;
ScanKeyData skey[1];
SysScanDesc rcscan;
HeapTuple tup;
Form_pg_default_acl defacl;
+ /* XXX get_object_address doesn't seem to support this */
+
defaclrel = heap_open(DefaultAclRelationId, AccessShareLock);
ScanKeyInit(&skey[0],
***************
*** 3310,3315 **** getObjectIdentity(const ObjectAddress *object)
--- 3621,3628 ----
elog(ERROR, "cache lookup failed for extension %u",
object->objectId);
appendStringInfoString(&buffer, quote_identifier(extname));
+ if (objname)
+ *objname = list_make1(extname);
break;
}
***************
*** 3318,3323 **** getObjectIdentity(const ObjectAddress *object)
--- 3631,3639 ----
HeapTuple tup;
Form_pg_event_trigger trigForm;
+ /* no objname support here */
+ Assert(objname == NULL);
+
tup = SearchSysCache1(EVENTTRIGGEROID,
ObjectIdGetDatum(object->objectId));
if (!HeapTupleIsValid(tup))
***************
*** 3342,3348 **** getObjectIdentity(const ObjectAddress *object)
}
static void
! getOpFamilyIdentity(StringInfo buffer, Oid opfid)
{
HeapTuple opfTup;
Form_pg_opfamily opfForm;
--- 3658,3664 ----
}
static void
! getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **objname, List **objargs)
{
HeapTuple opfTup;
Form_pg_opfamily opfForm;
***************
*** 3367,3372 **** getOpFamilyIdentity(StringInfo buffer, Oid opfid)
--- 3683,3695 ----
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);
}
***************
*** 3376,3382 **** getOpFamilyIdentity(StringInfo buffer, Oid opfid)
* StringInfo.
*/
static void
! getRelationIdentity(StringInfo buffer, Oid relid)
{
HeapTuple relTup;
Form_pg_class relForm;
--- 3699,3705 ----
* StringInfo.
*/
static void
! getRelationIdentity(StringInfo buffer, Oid relid, List **objname)
{
HeapTuple relTup;
Form_pg_class relForm;
***************
*** 3392,3397 **** getRelationIdentity(StringInfo buffer, Oid relid)
--- 3715,3722 ----
appendStringInfoString(buffer,
quote_qualified_identifier(schema,
NameStr(relForm->relname)));
+ if (objname)
+ *objname = list_make2(schema, pstrdup(NameStr(relForm->relname)));
ReleaseSysCache(relTup);
}
*** a/src/backend/commands/event_trigger.c
--- b/src/backend/commands/event_trigger.c
***************
*** 111,116 **** typedef struct SQLDropObject
--- 111,121 ----
const char *objname;
const char *objidentity;
const char *objecttype;
+ List *addrnames;
+ List *addrargs;
+ ObjectAddress dependee;
+ bool original;
+ bool normal;
slist_node next;
} SQLDropObject;
***************
*** 920,928 **** EventTriggerSupportsObjectType(ObjectType obtype)
--- 925,935 ----
case OBJECT_ATTRIBUTE:
case OBJECT_CAST:
case OBJECT_COLUMN:
+ case OBJECT_COMPOSITE:
case OBJECT_CONSTRAINT:
case OBJECT_COLLATION:
case OBJECT_CONVERSION:
+ case OBJECT_DEFAULT:
case OBJECT_DOMAIN:
case OBJECT_EXTENSION:
case OBJECT_FDW:
***************
*** 946,951 **** EventTriggerSupportsObjectType(ObjectType obtype)
--- 953,959 ----
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
case OBJECT_TYPE:
+ case OBJECT_USER_MAPPING:
case OBJECT_VIEW:
return true;
}
***************
*** 1102,1108 **** trackDroppedObjectsNeeded(void)
* Register one object as being dropped by the current command.
*/
void
! EventTriggerSQLDropAddObject(ObjectAddress *object)
{
SQLDropObject *obj;
MemoryContext oldcxt;
--- 1110,1116 ----
* Register one object as being dropped by the current command.
*/
void
! EventTriggerSQLDropAddObject(const ObjectAddress *object, bool original, bool normal)
{
SQLDropObject *obj;
MemoryContext oldcxt;
***************
*** 1121,1126 **** EventTriggerSQLDropAddObject(ObjectAddress *object)
--- 1129,1136 ----
obj = palloc0(sizeof(SQLDropObject));
obj->address = *object;
+ obj->original = original;
+ obj->normal = normal;
/*
* Obtain schema names from the object's catalog tuple, if one exists;
***************
*** 1182,1191 **** EventTriggerSQLDropAddObject(ObjectAddress *object)
heap_close(catalog, AccessShareLock);
}
! /* object identity */
! obj->objidentity = getObjectIdentity(&obj->address);
! /* and object type, too */
obj->objecttype = getObjectTypeDescription(&obj->address);
slist_push_head(&(currentEventTriggerState->SQLDropList), &obj->next);
--- 1192,1202 ----
heap_close(catalog, AccessShareLock);
}
! /* object identity, objname and objargs */
! obj->objidentity =
! getObjectIdentityParts(&obj->address, &obj->addrnames, &obj->addrargs);
! /* object type */
obj->objecttype = getObjectTypeDescription(&obj->address);
slist_push_head(&(currentEventTriggerState->SQLDropList), &obj->next);
***************
*** 1248,1255 **** pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
{
SQLDropObject *obj;
int i = 0;
! Datum values[7];
! bool nulls[7];
obj = slist_container(SQLDropObject, next, iter.cur);
--- 1259,1266 ----
{
SQLDropObject *obj;
int i = 0;
! Datum values[11];
! bool nulls[11];
obj = slist_container(SQLDropObject, next, iter.cur);
***************
*** 1265,1270 **** pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
--- 1276,1287 ----
/* objsubid */
values[i++] = Int32GetDatum(obj->address.objectSubId);
+ /* original */
+ values[i++] = BoolGetDatum(obj->original);
+
+ /* normal */
+ values[i++] = BoolGetDatum(obj->normal);
+
/* object_type */
values[i++] = CStringGetTextDatum(obj->objecttype);
***************
*** 1286,1291 **** pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
--- 1303,1356 ----
else
nulls[i++] = true;
+ /* address_names */
+ if (obj->addrnames)
+ {
+ ArrayType *arr;
+ Datum *datums;
+ int j = 0;
+ ListCell *cell;
+
+ datums = palloc(sizeof(text *) * list_length(obj->addrnames));
+ foreach(cell, obj->addrnames)
+ {
+ char *name = lfirst(cell);
+
+ datums[j++] = CStringGetTextDatum(name);
+ }
+
+ arr = construct_array(datums, list_length(obj->addrnames),
+ TEXTOID, -1, false, 'i');
+
+ values[i++] = PointerGetDatum(arr);
+ }
+ else
+ nulls[i++] = true;
+
+ /* address_args */
+ /* FIXME duplicated code block ... */
+ if (obj->addrargs)
+ {
+ ArrayType *arr;
+ Datum *datums;
+ int j = 0;
+ ListCell *cell;
+
+ datums = palloc(sizeof(text *) * list_length(obj->addrargs));
+ foreach(cell, obj->addrargs)
+ {
+ char *arg = lfirst(cell);
+
+ datums[j++] = CStringGetTextDatum(arg);
+ }
+
+ arr = construct_array(datums, list_length(obj->addrargs),
+ TEXTOID, -1, false, 'i');
+ values[i++] = PointerGetDatum(arr);
+ }
+ else
+ nulls[i++] = true;
+
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
}
*** a/src/backend/parser/parse_type.c
--- b/src/backend/parser/parse_type.c
***************
*** 705,717 **** 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.
*/
! void
! parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p,
! bool missing_ok)
{
StringInfoData buf;
List *raw_parsetree_list;
--- 705,715 ----
/*
* 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 return the result as a TypeName.
! * If the string cannot be parsed as a type, an error is raised.
*/
! TypeName *
! typeStringToTypeName(const char *str)
{
StringInfoData buf;
List *raw_parsetree_list;
***************
*** 720,726 **** 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))
--- 718,723 ----
***************
*** 779,784 **** parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p,
--- 776,782 ----
typecast->arg == NULL ||
!IsA(typecast->arg, A_Const))
goto fail;
+
typeName = typecast->typeName;
if (typeName == NULL ||
!IsA(typeName, TypeName))
***************
*** 786,791 **** parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p,
--- 784,814 ----
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,820 **** 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)));
}
--- 831,834 ----
*** a/src/backend/utils/adt/regproc.c
--- b/src/backend/utils/adt/regproc.c
***************
*** 439,444 **** format_procedure_internal(Oid procedure_oid, bool force_qualify)
--- 439,479 ----
}
/*
+ * 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,880 **** format_operator_qualified(Oid operator_oid)
--- 910,940 ----
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)"
*/
*** a/src/include/catalog/objectaddress.h
--- b/src/include/catalog/objectaddress.h
***************
*** 55,61 **** extern HeapTuple get_catalog_object_by_oid(Relation catalog,
--- 55,64 ----
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);
+ extern char *getObjectIdentityParts(const ObjectAddress *address,
+ List **objname, List **objargs);
#endif /* OBJECTADDRESS_H */
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 4976,4982 **** 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,25,25,25,25}" "{o,o,o,o,o,o,o}" "{classid, objid, objsubid, object_type, schema_name, object_name, object_identity}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
DESCR("list objects dropped by the current command");
/* generic transition functions for ordered-set aggregates */
--- 4976,4982 ----
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,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");
/* generic transition functions for ordered-set aggregates */
*** a/src/include/commands/event_trigger.h
--- b/src/include/commands/event_trigger.h
***************
*** 50,55 **** extern void EventTriggerSQLDrop(Node *parsetree);
extern bool EventTriggerBeginCompleteQuery(void);
extern void EventTriggerEndCompleteQuery(void);
extern bool trackDroppedObjectsNeeded(void);
! extern void EventTriggerSQLDropAddObject(ObjectAddress *object);
#endif /* EVENT_TRIGGER_H */
--- 50,56 ----
extern bool EventTriggerBeginCompleteQuery(void);
extern void EventTriggerEndCompleteQuery(void);
extern bool trackDroppedObjectsNeeded(void);
! extern void EventTriggerSQLDropAddObject(const ObjectAddress *object,
! bool original, bool normal);
#endif /* EVENT_TRIGGER_H */
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 1206,1215 **** typedef enum ObjectType
--- 1206,1217 ----
OBJECT_ATTRIBUTE, /* type's attribute, when distinct from column */
OBJECT_CAST,
OBJECT_COLUMN,
+ OBJECT_COMPOSITE,
OBJECT_CONSTRAINT,
OBJECT_COLLATION,
OBJECT_CONVERSION,
OBJECT_DATABASE,
+ OBJECT_DEFAULT,
OBJECT_DOMAIN,
OBJECT_EVENT_TRIGGER,
OBJECT_EXTENSION,
***************
*** 1236,1241 **** typedef enum ObjectType
--- 1238,1244 ----
OBJECT_TSPARSER,
OBJECT_TSTEMPLATE,
OBJECT_TYPE,
+ OBJECT_USER_MAPPING,
OBJECT_VIEW
} ObjectType;
*** a/src/include/parser/parse_type.h
--- b/src/include/parser/parse_type.h
***************
*** 47,52 **** extern Datum stringTypeDatum(Type tp, char *string, int32 atttypmod);
--- 47,53 ----
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)
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 642,649 **** extern Datum text_regclass(PG_FUNCTION_ARGS);
--- 642,653 ----
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);
I think there's been some changes to this patch since july, care to
resend a new version?Sure, here it is.
The only difference with the previous version is that it now also
supports column defaults. This was found to be a problem when you drop
a sequence that some column default depends on -- for example a column
declared SERIAL, or a sequence marked with ALTER SEQUENCE OWNED BY. The
new code is able to drop both the sequence and the default value
(leaving, of course, the rest of the column intact.) This required
adding support for such objects in get_object_address.
I have given this patch the following review:
- Apply to current master (77e65bf). -- success
- check-world. --success
- multiple FIXME statements still exist -- are there plans to fix these
items? Can the duplicated code be extracted to a static function?
-Adam
--
Adam Brightwell - adam.brightwell@crunchydatasolutions.com
Database Engineer - www.crunchydatasolutions.com
On 09/16/2014 09:09 PM, Brightwell, Adam wrote:
I think there's been some changes to this patch since july, care to
resend a new version?Sure, here it is.
The only difference with the previous version is that it now also
supports column defaults. This was found to be a problem when you drop
a sequence that some column default depends on -- for example a column
declared SERIAL, or a sequence marked with ALTER SEQUENCE OWNED BY. The
new code is able to drop both the sequence and the default value
(leaving, of course, the rest of the column intact.) This required
adding support for such objects in get_object_address.I have given this patch the following review:
- Apply to current master (77e65bf). -- success
- check-world. --success
- multiple FIXME statements still exist -- are there plans to fix these
items? Can the duplicated code be extracted to a static function?
Nothing seems to be happening to this, so I'm marking this as returned
with feedback.
- Heikki
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Heikki Linnakangas wrote:
On 09/16/2014 09:09 PM, Brightwell, Adam wrote:
I have given this patch the following review:
- Apply to current master (77e65bf). -- success
- check-world. --success
- multiple FIXME statements still exist -- are there plans to fix these
items? Can the duplicated code be extracted to a static function?Nothing seems to be happening to this, so I'm marking this as
returned with feedback.
Meh.
There are three fixmes in the code. One can be handled by just removing
the line; we don't really care about duplicating 10 lines of boilerplate
code. The other two mean missing support for domain constraints and for
default ACLs. Is there absolutely no feedback to be had on the
mechanism used by the patch?
Since the patch has had good feedback and no further comments arise, I
can just implement support for those two missing object types and push,
and everybody will be happy. Right?
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Alvaro,
* Alvaro Herrera (alvherre@2ndquadrant.com) wrote:
There are three fixmes in the code. One can be handled by just removing
the line; we don't really care about duplicating 10 lines of boilerplate
code. The other two mean missing support for domain constraints and for
default ACLs. Is there absolutely no feedback to be had on the
mechanism used by the patch?Since the patch has had good feedback and no further comments arise, I
can just implement support for those two missing object types and push,
and everybody will be happy. Right?
In general, I'd say yes, but I'll take a look at the patch now and
provide feedback in a couple hours anyway.
Thanks,
Stephen
On 10/03/2014 08:08 PM, Stephen Frost wrote:
Alvaro,
* Alvaro Herrera (alvherre@2ndquadrant.com) wrote:
There are three fixmes in the code. One can be handled by just removing
the line; we don't really care about duplicating 10 lines of boilerplate
code. The other two mean missing support for domain constraints and for
default ACLs. Is there absolutely no feedback to be had on the
mechanism used by the patch?Since the patch has had good feedback and no further comments arise, I
can just implement support for those two missing object types and push,
and everybody will be happy. Right?In general, I'd say yes, but I'll take a look at the patch now and
provide feedback in a couple hours anyway.
Thanks Stephen!
I had a very brief look at the docs, and these extra outputs from
pg_event_trigger_dropped_objects caught my eye:
+ <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>
I couldn't find a function called getObjectAddress anywhere. Typo?
Also, is providing a C-language function the best we can do? The rest of
the information returned by pg_event_trigger_dropped_objects is usable
from any language.
- Heikki
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Heikki Linnakangas wrote:
I had a very brief look at the docs, and these extra outputs from
pg_event_trigger_dropped_objects caught my eye:+ <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>I couldn't find a function called getObjectAddress anywhere. Typo?
Ah, yeah, it's get_object_address actually.
Also, is providing a C-language function the best we can do? The
rest of the information returned by pg_event_trigger_dropped_objects
is usable from any language.
Well, the return value from get_object_address is an ObjectAddress.
It's simple enough to create an SQL wrapper that takes the
address_names/address_args arrays and return an ObjectAddress; would
this be useful?
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 10/03/2014 09:06 PM, Alvaro Herrera wrote:
Heikki Linnakangas wrote:
I had a very brief look at the docs, and these extra outputs from
pg_event_trigger_dropped_objects caught my eye:+ <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>I couldn't find a function called getObjectAddress anywhere. Typo?
Ah, yeah, it's get_object_address actually.
Also, is providing a C-language function the best we can do? The
rest of the information returned by pg_event_trigger_dropped_objects
is usable from any language.Well, the return value from get_object_address is an ObjectAddress.
It's simple enough to create an SQL wrapper that takes the
address_names/address_args arrays and return an ObjectAddress; would
this be useful?
An ObjectAddress consists of a classid, objid, and objsubid.
pg_event_trigger_dropped_objects already returns all of those as
separate fields. What am I missing?
- Heikki
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Heikki Linnakangas wrote:
On 10/03/2014 09:06 PM, Alvaro Herrera wrote:
Well, the return value from get_object_address is an ObjectAddress.
It's simple enough to create an SQL wrapper that takes the
address_names/address_args arrays and return an ObjectAddress; would
this be useful?An ObjectAddress consists of a classid, objid, and objsubid.
pg_event_trigger_dropped_objects already returns all of those as
separate fields. What am I missing?
Precisely the point is not returning those values, because they are
useless to identify the equivalent object in a remote database. What we
need is the object names and other stuff used to uniquely identify it
"by user-visible name". We transmit those name arrays to a remote
server, then on the remote server we can run get_object_address and get
the ObjectAddress, which has the classid,objid,objsubid values you cite ...
but for the remote server. The object can then be dropped there.
Initially we thought that we would use the object_identity object for
this (which is why we invented that functionality and added the column
in 9.3), but this turned out not to work very well for unusual object
types; hence this patch.
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Alvaro Herrera wrote:
Precisely the point is not returning those values, because they are
useless to identify the equivalent object in a remote database. What we
need is the object names and other stuff used to uniquely identify it
"by user-visible name". We transmit those name arrays to a remote
server, then on the remote server we can run get_object_address and get
the ObjectAddress, which has the classid,objid,objsubid values you cite ...
but for the remote server. The object can then be dropped there.Initially we thought that we would use the object_identity object for
this (which is why we invented that functionality and added the column
in 9.3), but this turned out not to work very well for unusual object
types; hence this patch.
The other thing to keep in mind is that with all those ObjectAddress
thingies you got, you cannot simply construct a DROP <obj> command:
1. The objects in the list might be of different types; say a table and
a view that are dropped by the same command because of CASCADE. (You
could just pass the CASCADE to the other side and hope that it happens
to do the same thing; but if the schemas are slightly different, it
might not.)
2. DROP OWNED or other commands might have dropped several objects,
again of varying types.
So what we do in BDR is stuff all those ObjectAddress in an array of
them, and then call performMultipleDeletions. There is no way to get
this functionality in non-C code; so it's hard to see that it's very
useful to have a non-C way to use the original objnames/objargs arrays.
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Alvaro,
* Alvaro Herrera (alvherre@2ndquadrant.com) wrote:
+ /* + * Make sure that both objname and objargs were passed, or none was. + * Initialize objargs to empty list, which is the most common case. + */ + Assert(PointerIsValid(objname) == PointerIsValid(objargs)); + if (objargs) + *objargs = NIL; +
I feel like I must be missing something, but you only explicitly reset
objargs, not objnames, and then below you sometimes add to objname and
other times throw away anything which might be there:
--- 2948,2974 ---- attr = get_relid_attribute_name(object->objectId, object->objectSubId); appendStringInfo(&buffer, ".%s", quote_identifier(attr)); + if (objname) + *objname = lappend(*objname, attr); } break;
Here's an "add to objname" case, and then:
case OCLASS_PROC: appendStringInfoString(&buffer, format_procedure_qualified(object->objectId)); + if (objname) + format_procedure_parts(object->objectId, objname, objargs); break;case OCLASS_TYPE:
! {
! char *typeout;
!
! typeout = format_type_be_qualified(object->objectId);
! appendStringInfoString(&buffer, typeout);
! if (objname)
! *objname = list_make1(typeout);
! }
break;
here's a "throw away whatever was in objname" case.
*************** *** 2745,2750 **** getObjectIdentity(const ObjectAddress *object) --- 2991,3000 ---- 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; }
And another "throw away" case.
--- 3037,3045 ---- { appendStringInfo(&buffer, "%s on ", quote_identifier(NameStr(con->conname))); ! getRelationIdentity(&buffer, con->conrelid, objname); ! if (objname) ! *objname = lappend(*objname, pstrdup(NameStr(con->conname))); } else {
And another "add to existing" case.
Guess I have a bit of a hard time with an API that's "we might add to
this list, or we might replace whatever is there". I think it would be
best to just initialize both (or assert that they are) and have any
callers who need to merge the list(s) coming back into an existing list
handle that themselves.
I'm also not a huge fan of the big object_type_map, but I also don't
have a better solution.
Thanks,
Stephen
Stephen Frost wrote:
Alvaro,
* Alvaro Herrera (alvherre@2ndquadrant.com) wrote:
+ /* + * Make sure that both objname and objargs were passed, or none was. + * Initialize objargs to empty list, which is the most common case. + */ + Assert(PointerIsValid(objname) == PointerIsValid(objargs)); + if (objargs) + *objargs = NIL; +I feel like I must be missing something, but you only explicitly reset
objargs, not objnames, and then below you sometimes add to objname and
other times throw away anything which might be there:--- 2948,2974 ---- attr = get_relid_attribute_name(object->objectId, object->objectSubId); appendStringInfo(&buffer, ".%s", quote_identifier(attr)); + if (objname) + *objname = lappend(*objname, attr); } break;Here's an "add to objname" case, and then:
Right. In the "add to objname" cases, there is already some other
routine that initialized it previously by filling in some stuff; in the
case above, this happens in the getRelationIdentity() immediately
preceding this.
In the other cases we initialize on that spot.
--- 3037,3045 ---- { appendStringInfo(&buffer, "%s on ", quote_identifier(NameStr(con->conname))); ! getRelationIdentity(&buffer, con->conrelid, objname); ! if (objname) ! *objname = lappend(*objname, pstrdup(NameStr(con->conname))); } else {And another "add to existing" case.
Note how this one has a getRelationIdentity, just like the first one.
Guess I have a bit of a hard time with an API that's "we might add to
this list, or we might replace whatever is there". I think it would be
best to just initialize both (or assert that they are) and have any
callers who need to merge the list(s) coming back into an existing list
handle that themselves.
The thing is, the list is already initialized in all cases to a valid
list in this routine; there is no case that appends to whatever junk
might have been there before this routine started.
I'm also not a huge fan of the big object_type_map, but I also don't
have a better solution.
Agreed. We have the ObjectProperty array also in the same file; it
kinda looks like there is duplication here, but actually there isn't.
This whole issue is just fallout from the fact that we have three
different ways to identify object classes: the ObjectClass enum, the
ObjectType enum, and the relation OIDs of each catalog (actually a
fourth one, see below). I don't see any other nice way around this; I
guess we could try to auto-generate these tables somehow from a master
text file, or something. Not material for this patch, I think.
Note my DDL deparse patch adds a comment:
+/* XXX merge this with ObjectTypeMap? */
static event_trigger_support_data event_trigger_support[] = {
and a late revision to that patch added a new function in
event_triggers.c (not yet posted I think) due to GRANT having its own
enum of object types, AclObjectKind.
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
* Alvaro Herrera (alvherre@2ndquadrant.com) wrote:
Right. In the "add to objname" cases, there is already some other
routine that initialized it previously by filling in some stuff; in the
case above, this happens in the getRelationIdentity() immediately
preceding this.In the other cases we initialize on that spot.
ahh, ok, that makes a bit more sense, sorry for missing it. Still makes
me wonder why objargs gets special treatment at the top of the function
and objnames doesn't- seems like both should be initialized either
before being passed in (and perhaps an Assert to verify that they are),
or they should both be initialized, but I tend to prefer just Assert'ing
that they are correct on entry- either both are valid pointers to empty
lists, or both NULL.
I'm also not a huge fan of the big object_type_map, but I also don't
have a better solution.Agreed. We have the ObjectProperty array also in the same file; it
kinda looks like there is duplication here, but actually there isn't.
Yeah, I did notice that, and noticed that it's not duplication.
This whole issue is just fallout from the fact that we have three
different ways to identify object classes: the ObjectClass enum, the
ObjectType enum, and the relation OIDs of each catalog (actually a
fourth one, see below). I don't see any other nice way around this; I
guess we could try to auto-generate these tables somehow from a master
text file, or something. Not material for this patch, I think.
Agreed that this patch doesn't need to address it and not sure that a
master text file would actually be an improvement.. I had been thinking
if there was some way to have a single mapping which could be used in
either direction, but I didn't see any sensible way to make that work
given that it's not *quite* the same backwards and forewards.
Note my DDL deparse patch adds a comment:
+/* XXX merge this with ObjectTypeMap? */
static event_trigger_support_data event_trigger_support[] = {
Yes, I saw that, and that you added a comment that the new map needs to
be updated when changes are made, which is also good.
and a late revision to that patch added a new function in
event_triggers.c (not yet posted I think) due to GRANT having its own
enum of object types, AclObjectKind.
Yeah. Perhaps one day we'll unify all of these, though I'm not 100%
sure it'd be possible...
Thanks!
Stephen
Stephen Frost wrote:
* Alvaro Herrera (alvherre@2ndquadrant.com) wrote:
Right. In the "add to objname" cases, there is already some other
routine that initialized it previously by filling in some stuff; in the
case above, this happens in the getRelationIdentity() immediately
preceding this.In the other cases we initialize on that spot.
ahh, ok, that makes a bit more sense, sorry for missing it. Still makes
me wonder why objargs gets special treatment at the top of the function
and objnames doesn't- seems like both should be initialized either
before being passed in (and perhaps an Assert to verify that they are),
or they should both be initialized, but I tend to prefer just Assert'ing
that they are correct on entry- either both are valid pointers to empty
lists, or both NULL.
I guess I could initialize objnames to NIL also. I initialize objargs
because that one is unused for a lot of object types (so I would have to
set it to NIL in cases where it's not used), whereas objnames is always
used and thus we know it's always initialized later.
Maybe what I need here is just a longer comment explaining this ...
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
* Alvaro Herrera (alvherre@2ndquadrant.com) wrote:
Stephen Frost wrote:
ahh, ok, that makes a bit more sense, sorry for missing it. Still makes
me wonder why objargs gets special treatment at the top of the function
and objnames doesn't- seems like both should be initialized either
before being passed in (and perhaps an Assert to verify that they are),
or they should both be initialized, but I tend to prefer just Assert'ing
that they are correct on entry- either both are valid pointers to empty
lists, or both NULL.I guess I could initialize objnames to NIL also. I initialize objargs
because that one is unused for a lot of object types (so I would have to
set it to NIL in cases where it's not used), whereas objnames is always
used and thus we know it's always initialized later.Maybe what I need here is just a longer comment explaining this ...
A one-line comment that it's always reset below would be sufficient for me.
Thanks for explaining it :),
Stephen
On Fri, Oct 3, 2014 at 2:33 PM, Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
Heikki Linnakangas wrote:
On 10/03/2014 09:06 PM, Alvaro Herrera wrote:
Well, the return value from get_object_address is an ObjectAddress.
It's simple enough to create an SQL wrapper that takes the
address_names/address_args arrays and return an ObjectAddress; would
this be useful?An ObjectAddress consists of a classid, objid, and objsubid.
pg_event_trigger_dropped_objects already returns all of those as
separate fields. What am I missing?Precisely the point is not returning those values, because they are
useless to identify the equivalent object in a remote database. What we
need is the object names and other stuff used to uniquely identify it
"by user-visible name". We transmit those name arrays to a remote
server, then on the remote server we can run get_object_address and get
the ObjectAddress, which has the classid,objid,objsubid values you cite ...
but for the remote server. The object can then be dropped there.Initially we thought that we would use the object_identity object for
this (which is why we invented that functionality and added the column
in 9.3), but this turned out not to work very well for unusual object
types; hence this patch.
I'm not really very convinced that it's a good idea to expose this
instead of just figuring out a way to parse the object identity.
But I expect to lose that argument.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Robert Haas wrote:
I'm not really very convinced that it's a good idea to expose this
instead of just figuring out a way to parse the object identity.
That's the first thing I tried. But it's not pretty: you have to
extract schema names by splitting at a period (and what if a schema name
contains a period?), split out on ON for certain object types, figure
out parens and argument types and names for functions and aggregates,
etc. It's just not sane to try to parse such text strings.
But I expect to lose that argument.
Good :-)
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 2014-10-03 14:02:09 -0300, Alvaro Herrera wrote:
Since the patch has had good feedback and no further comments arise, I
can just implement support for those two missing object types and push,
and everybody will be happy. Right?
I'd like to see a new version before that out here... I don't think
there's fundamental issues, but it's complicated enough to warrant
that. And I don't think it has gotten enough detailed review. I hope to
be able to give a bit more of that...
Greetings,
Andres Freund
--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Fri, Oct 3, 2014 at 4:58 PM, Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
Robert Haas wrote:
I'm not really very convinced that it's a good idea to expose this
instead of just figuring out a way to parse the object identity.That's the first thing I tried. But it's not pretty: you have to
extract schema names by splitting at a period (and what if a schema name
contains a period?),
Please tell me that the literals are escaped if necessary. If so,
this is pretty easy. quote_literal() is not a hard transformation to
reverse, and splitting on a unquoted period is not hard...
split out on ON for certain object types,
...nor is splitting on any other fixed text string, such as " ON ".
figure
out parens and argument types and names for functions and aggregates,
etc.
I certainly agree that parsing out parens and argument types and names
for functions and aggregates is the hardest part of this, mostly
because you can't count a comma to mark the end of one argument and
the beginning of the next - you have to account for quoted
identifiers, and you might be inside a numeric typemod or similar.
It's just not sane to try to parse such text strings.
But this is a pretty ridiculous argument. We have an existing parser
that does it just fine, and a special-purpose parser that does just
that (and not all of the other stuff that the main parser does) would
be a great deal simpler. Maybe there are examples other than the ones
you listed here that demonstrate that this is actually a hard problem,
but the fact that you might need to undo quote_literal() or search for
and split on fixed strings does not.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 10/4/14, 8:12 PM, Robert Haas wrote:
It's just not sane to try to parse such text strings.
But this is a pretty ridiculous argument. We have an existing parser
that does it just fine, and a special-purpose parser that does just
that (and not all of the other stuff that the main parser does) would
be a great deal simpler. Maybe there are examples other than the ones
you listed here that demonstrate that this is actually a hard problem,
but the fact that you might need to undo quote_literal() or search for
and split on fixed strings does not.
FWIW, I've run into situations more than once in userspace where I need a way to properly separate schema and object name. Generally I can make do using reg* casts and then hitting catalog tables, but it'd be nice if there was an easier way.
--
Jim Nasby, Data Architect, Blue Treble Consulting
Data in Trouble? Get it in Treble! http://BlueTreble.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Mon, Oct 6, 2014 at 7:03 PM, Jim Nasby <Jim.Nasby@bluetreble.com> wrote:
On 10/4/14, 8:12 PM, Robert Haas wrote:
It's just not sane to try to parse such text strings.
But this is a pretty ridiculous argument. We have an existing parser
that does it just fine, and a special-purpose parser that does just
that (and not all of the other stuff that the main parser does) would
be a great deal simpler. Maybe there are examples other than the ones
you listed here that demonstrate that this is actually a hard problem,
but the fact that you might need to undo quote_literal() or search for
and split on fixed strings does not.FWIW, I've run into situations more than once in userspace where I need a
way to properly separate schema and object name. Generally I can make do
using reg* casts and then hitting catalog tables, but it'd be nice if there
was an easier way.
Sure, although I think that's a bit of a separate problem. It's hard
to iterate through a string a character at a time from the SQL level
so that you can handle stuff like the quote_literal() rules. If we
want people to be able to do that easily we need to provide tools to
handle it. But C is actually quite well-suited to such tasks.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 10/6/14, 11:24 PM, Robert Haas wrote:
Offlist.
FWIW, I've run into situations more than once in userspace where I need a
way to properly separate schema and object name. Generally I can make do
using reg* casts and then hitting catalog tables, but it'd be nice if there
was an easier way.Sure, although I think that's a bit of a separate problem. It's hard
to iterate through a string a character at a time from the SQL level
so that you can handle stuff like the quote_literal() rules. If we
want people to be able to do that easily we need to provide tools to
handle it. But C is actually quite well-suited to such tasks.
Yeah, I wouldn't want to attempt this in SQL; I was saying that a built-in function to do this would be broadly useful, not just for replicating DROPs.
--
Jim Nasby, Data Architect, Blue Treble Consulting
Data in Trouble? Get it in Treble! http://BlueTreble.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Jim Nasby wrote:
On 10/6/14, 11:24 PM, Robert Haas wrote:
Offlist.
FWIW, I've run into situations more than once in userspace where I need a
way to properly separate schema and object name. Generally I can make do
using reg* casts and then hitting catalog tables, but it'd be nice if there
was an easier way.Sure, although I think that's a bit of a separate problem. It's hard
to iterate through a string a character at a time from the SQL level
so that you can handle stuff like the quote_literal() rules. If we
want people to be able to do that easily we need to provide tools to
handle it. But C is actually quite well-suited to such tasks.Yeah, I wouldn't want to attempt this in SQL; I was saying that a
built-in function to do this would be broadly useful, not just for
replicating DROPs.
Well, most of what you need is served by pg_identify_object, I think:
just grab the OID from the appropriate catalog, and the OID of the
catalog itself; that function will give you schema and name, which is
what you need. Probably the most difficult part is figuring out which
reg* cast you want .. and of course there are also cases where there is
no such datatype to cast to in the first place.
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Robert Haas wrote:
On Fri, Oct 3, 2014 at 4:58 PM, Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
Robert Haas wrote:
I'm not really very convinced that it's a good idea to expose this
instead of just figuring out a way to parse the object identity.That's the first thing I tried. But it's not pretty: you have to
extract schema names by splitting at a period (and what if a schema name
contains a period?),Please tell me that the literals are escaped if necessary. If so,
this is pretty easy. quote_literal() is not a hard transformation to
reverse, and splitting on a unquoted period is not hard...
I don't think it is necessary to force parsing strings for something
that can be more conveniently obtained from the get go, just because
we're too lazy to change the existing definition of the function. I'm
not saying it is impossible or extremely hard to parse the strings, but
since we can emit the right format with no extra effort, there doesn't
seem to be any point on doing it that way. It's not like this patch
adds excessive extra complexity to this code, either.
I'd say that the most complex part of this patch is the addition of the
two flag ("normal" and "original") columns, which we would need
regardless of the rest of the patch; these are used to tell whether
there are routes to the given object that have the eponymous flags set
in the dependency graph.
It's just not sane to try to parse such text strings.
But this is a pretty ridiculous argument. We have an existing parser
that does it just fine, and a special-purpose parser that does just
that (and not all of the other stuff that the main parser does) would
be a great deal simpler.
We have a main parser because we have no other option --- it's the way
stuff gets into the system in the first place. But in this case, it's
not about accepting communication from the outside world, but emitting
state that is already in the database, in a different format.
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 2014-10-04 21:12:24 -0400, Robert Haas wrote:
On Fri, Oct 3, 2014 at 4:58 PM, Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
Robert Haas wrote:
I'm not really very convinced that it's a good idea to expose this
instead of just figuring out a way to parse the object identity.That's the first thing I tried. But it's not pretty: you have to
extract schema names by splitting at a period (and what if a schema name
contains a period?),Please tell me that the literals are escaped if necessary. If so,
this is pretty easy. quote_literal() is not a hard transformation to
reverse, and splitting on a unquoted period is not hard...
split out on ON for certain object types,
...nor is splitting on any other fixed text string, such as " ON ".
I'm not following here. Maybe just because I'm misunderstanding your
position.
The patch imo consists out of the following parts:
1) Addition of dependency information to the dropped object list
2) Actual get_object_address() handling for default values - the current
behaviour looks pretty borked to me.
3) The reverse of getObjectTypeDescription()
4) getObjectIdentityParts() - a slightly more detailed version of
getObjectIdentity() that requires less string parsing
5) drop even trigger support for a few types.
Are you saying you want to add a function to do 4) via parsing inside
postgres or are you suggesting to do that in every user of this
facility?
If the former, why would it be preferrable to do string parsing if we
have access to the source data? That part of the patch looks trivial to
me?
If the latter, I don't see the advantage either - this is complicated
enough, why should different users repeat the work?
Am I misunderstanding you here?
Having reread the patch just now I basically see two things to
criticize:
a) why isn't this accessible at SQL level? That seems easy to address.
b) Arguably some of this could well be done in separate commits.
It's just not sane to try to parse such text strings.
But this is a pretty ridiculous argument. We have an existing parser
that does it just fine
Which happens to be the part of postgres that's copied most often. So
it's certainly not something appearing to be trivial.
,and a special-purpose parser that does just
that (and not all of the other stuff that the main parser does) would
be a great deal simpler.
That parser also happens to be far from trivial (if we're talking about
parseNameAndArgTypes() - which just solves one of the cases.
Greetings,
Andres Freund
--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Andres Freund wrote:
Having reread the patch just now I basically see two things to
criticize:
a) why isn't this accessible at SQL level? That seems easy to address.
b) Arguably some of this could well be done in separate commits.
Fair comments. I will split it up.
FWIW, I spent some time today fighting with this stuff but the
OBJECT_POLICY stuff has been causing me some trouble. I'm not sure that
what's there currently in get_object_address is completely sane -- for
one thing I'm unsure that tables are the only object kind in the system
that are subject to policies. In that case, we have a shortage of
abstraction here, it seems, which will need some additional fixes.
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Alvaro Herrera wrote:
Andres Freund wrote:
Having reread the patch just now I basically see two things to
criticize:
a) why isn't this accessible at SQL level? That seems easy to address.
b) Arguably some of this could well be done in separate commits.Fair comments. I will split it up.
Here's a split version. The last part is still missing some polish --
in particular handling for OBJECT_POLICY, and the SQL interface which
would also allow us to get something in the regression tests.
Note: in this patch series you can find the ObjectTypeMap thing that you
thought was obsolete in the DDL deparse patch ...
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Attachments:
0001-add-normal-original-flags-to-pg_event_trigger_droppe.patchtext/x-diff; charset=us-asciiDownload
>From 92816868c1c717519a76a83e65cd0b48e3726fbf Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Fri, 4 Apr 2014 16:05:48 -0300
Subject: [PATCH 1/3] add normal/original flags to
pg_event_trigger_dropped_objects
---
doc/src/sgml/func.sgml | 13 ++++++++++
src/backend/catalog/dependency.c | 21 ++++++++++-----
src/backend/commands/event_trigger.c | 16 +++++++++---
src/include/catalog/pg_proc.h | 2 +-
src/include/commands/event_trigger.h | 3 ++-
src/test/regress/expected/event_trigger.out | 40 +++++++++++++++++++++++++++++
src/test/regress/sql/event_trigger.sql | 30 ++++++++++++++++++++++
7 files changed, 114 insertions(+), 11 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 7e5bcd9..45f3efa 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17583,6 +17583,19 @@ FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger();
<entry>Object sub-id (e.g. attribute number for columns)</entry>
</row>
<row>
+ <entry><literal>original</literal></entry>
+ <entry><type>bool</type></entry>
+ <entry>Flag used to identify the root object of the deletion</entry>
+ </row>
+ <row>
+ <entry><literal>normal</literal></entry>
+ <entry><type>bool</type></entry>
+ <entry>
+ Flag indicating that there's a normal dependency relationship
+ in the dependency graph leading to this object
+ </entry>
+ </row>
+ <row>
<entry><literal>object_type</literal></entry>
<entry><type>text</type></entry>
<entry>Type of the object</entry>
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 256486c..6485e3d 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -205,16 +205,25 @@ deleteObjectsInList(ObjectAddresses *targetObjects, Relation *depRel,
/*
* Keep track of objects for event triggers, if necessary.
*/
- if (trackDroppedObjectsNeeded())
+ if (trackDroppedObjectsNeeded() && !(flags & PERFORM_DELETION_INTERNAL))
{
for (i = 0; i < targetObjects->numrefs; i++)
{
- ObjectAddress *thisobj = targetObjects->refs + i;
-
- if ((!(flags & PERFORM_DELETION_INTERNAL)) &&
- EventTriggerSupportsObjectClass(getObjectClass(thisobj)))
+ const ObjectAddress *thisobj = &targetObjects->refs[i];
+ const ObjectAddressExtra *extra = &targetObjects->extras[i];
+ bool original = false;
+ bool normal = false;
+
+ if (extra->flags & DEPFLAG_ORIGINAL)
+ original = true;
+ if (extra->flags & DEPFLAG_NORMAL)
+ normal = true;
+ if (extra->flags & DEPFLAG_REVERSE)
+ normal = true;
+
+ if (EventTriggerSupportsObjectClass(getObjectClass(thisobj)))
{
- EventTriggerSQLDropAddObject(thisobj);
+ EventTriggerSQLDropAddObject(thisobj, original, normal);
}
}
}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 1b8c94b..0bab971 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -112,6 +112,8 @@ typedef struct SQLDropObject
const char *objname;
const char *objidentity;
const char *objecttype;
+ bool original;
+ bool normal;
slist_node next;
} SQLDropObject;
@@ -1105,7 +1107,7 @@ trackDroppedObjectsNeeded(void)
* Register one object as being dropped by the current command.
*/
void
-EventTriggerSQLDropAddObject(ObjectAddress *object)
+EventTriggerSQLDropAddObject(const ObjectAddress *object, bool original, bool normal)
{
SQLDropObject *obj;
MemoryContext oldcxt;
@@ -1124,6 +1126,8 @@ EventTriggerSQLDropAddObject(ObjectAddress *object)
obj = palloc0(sizeof(SQLDropObject));
obj->address = *object;
+ obj->original = original;
+ obj->normal = normal;
/*
* Obtain schema names from the object's catalog tuple, if one exists;
@@ -1251,8 +1255,8 @@ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
{
SQLDropObject *obj;
int i = 0;
- Datum values[7];
- bool nulls[7];
+ Datum values[9];
+ bool nulls[9];
obj = slist_container(SQLDropObject, next, iter.cur);
@@ -1268,6 +1272,12 @@ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
/* objsubid */
values[i++] = Int32GetDatum(obj->address.objectSubId);
+ /* original */
+ values[i++] = BoolGetDatum(obj->original);
+
+ /* normal */
+ values[i++] = BoolGetDatum(obj->normal);
+
/* object_type */
values[i++] = CStringGetTextDatum(obj->objecttype);
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 4736532..c4e932f 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4988,7 +4988,7 @@ 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,25,25,25,25}" "{o,o,o,o,o,o,o}" "{classid, objid, objsubid, 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}" "{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_ ));
DESCR("list objects dropped by the current command");
/* generic transition functions for ordered-set aggregates */
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index 0233f4c..7ef01cb 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -50,6 +50,7 @@ extern void EventTriggerSQLDrop(Node *parsetree);
extern bool EventTriggerBeginCompleteQuery(void);
extern void EventTriggerEndCompleteQuery(void);
extern bool trackDroppedObjectsNeeded(void);
-extern void EventTriggerSQLDropAddObject(ObjectAddress *object);
+extern void EventTriggerSQLDropAddObject(const ObjectAddress *object,
+ bool original, bool normal);
#endif /* EVENT_TRIGGER_H */
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index d4723b2..cb21792 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -294,3 +294,43 @@ SELECT * FROM dropped_objects WHERE type = 'schema';
DROP ROLE regression_bob;
DROP EVENT TRIGGER regress_event_trigger_drop_objects;
DROP EVENT TRIGGER undroppable;
+CREATE OR REPLACE FUNCTION event_trigger_report_dropped()
+ RETURNS event_trigger
+ LANGUAGE plpgsql
+AS $$
+DECLARE r record;
+BEGIN
+ FOR r IN SELECT * from pg_event_trigger_dropped_objects()
+ LOOP
+ IF NOT r.normal AND NOT r.original THEN
+ CONTINUE;
+ END IF;
+ RAISE NOTICE 'NORMAL: orig=% normal=% type=% identity=%',
+ r.original, r.normal, r.object_type, r.object_identity;
+ END LOOP;
+END; $$;
+CREATE EVENT TRIGGER regress_event_trigger_report_dropped ON sql_drop
+ EXECUTE PROCEDURE event_trigger_report_dropped();
+CREATE SCHEMA evttrig
+ CREATE TABLE one (col_a SERIAL PRIMARY KEY, col_b text DEFAULT 'forty two')
+ CREATE INDEX one_idx ON one (col_b)
+ CREATE TABLE two (col_c INTEGER CHECK (col_c > 0) REFERENCES one DEFAULT 42);
+ALTER TABLE evttrig.two DROP COLUMN col_c;
+NOTICE: NORMAL: orig=t normal=f type=table column identity=evttrig.two.col_c
+NOTICE: NORMAL: orig=f normal=t type=table constraint identity=two_col_c_check on evttrig.two
+ALTER TABLE evttrig.one ALTER COLUMN col_b DROP DEFAULT;
+NOTICE: NORMAL: orig=t normal=f type=default value identity=for evttrig.one.col_b
+ALTER TABLE evttrig.one DROP CONSTRAINT one_pkey;
+NOTICE: NORMAL: orig=t normal=f type=table constraint identity=one_pkey on evttrig.one
+DROP INDEX evttrig.one_idx;
+NOTICE: NORMAL: orig=t normal=f type=index identity=evttrig.one_idx
+DROP SCHEMA evttrig CASCADE;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table evttrig.one
+drop cascades to table evttrig.two
+NOTICE: NORMAL: orig=t normal=f type=schema identity=evttrig
+NOTICE: NORMAL: orig=f normal=t type=table identity=evttrig.one
+NOTICE: NORMAL: orig=f normal=t type=sequence identity=evttrig.one_col_a_seq
+NOTICE: NORMAL: orig=f normal=t type=default value identity=for evttrig.one.col_a
+NOTICE: NORMAL: orig=f normal=t type=table identity=evttrig.two
+DROP EVENT TRIGGER regress_event_trigger_report_dropped;
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 11d2ce5..c82380b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -206,3 +206,33 @@ DROP ROLE regression_bob;
DROP EVENT TRIGGER regress_event_trigger_drop_objects;
DROP EVENT TRIGGER undroppable;
+
+CREATE OR REPLACE FUNCTION event_trigger_report_dropped()
+ RETURNS event_trigger
+ LANGUAGE plpgsql
+AS $$
+DECLARE r record;
+BEGIN
+ FOR r IN SELECT * from pg_event_trigger_dropped_objects()
+ LOOP
+ IF NOT r.normal AND NOT r.original THEN
+ CONTINUE;
+ END IF;
+ RAISE NOTICE 'NORMAL: orig=% normal=% type=% identity=%',
+ r.original, r.normal, r.object_type, r.object_identity;
+ END LOOP;
+END; $$;
+CREATE EVENT TRIGGER regress_event_trigger_report_dropped ON sql_drop
+ EXECUTE PROCEDURE event_trigger_report_dropped();
+CREATE SCHEMA evttrig
+ CREATE TABLE one (col_a SERIAL PRIMARY KEY, col_b text DEFAULT 'forty two')
+ CREATE INDEX one_idx ON one (col_b)
+ CREATE TABLE two (col_c INTEGER CHECK (col_c > 0) REFERENCES one DEFAULT 42);
+
+ALTER TABLE evttrig.two DROP COLUMN col_c;
+ALTER TABLE evttrig.one ALTER COLUMN col_b DROP DEFAULT;
+ALTER TABLE evttrig.one DROP CONSTRAINT one_pkey;
+DROP INDEX evttrig.one_idx;
+DROP SCHEMA evttrig CASCADE;
+
+DROP EVENT TRIGGER regress_event_trigger_report_dropped;
--
1.9.1
0002-add-support-for-OBJECT_DEFAULT-in-get_object_address.patchtext/x-diff; charset=us-asciiDownload
>From a424f151c888aa6e94867da2edfe84f251f2798a Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Wed, 15 Oct 2014 18:02:45 -0300
Subject: [PATCH 2/3] 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 b69b75b..a443e84 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_CONSTRAINT:
@@ -1080,6 +1089,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 0bab971..6cb067e 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -926,6 +926,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_CONSTRAINT:
case OBJECT_COLLATION:
case OBJECT_CONVERSION:
+ case OBJECT_DEFAULT:
case OBJECT_DOMAIN:
case OBJECT_EXTENSION:
case OBJECT_FDW:
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index cef9544..5a092d8 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1212,6 +1212,7 @@ typedef enum ObjectType
OBJECT_COLLATION,
OBJECT_CONVERSION,
OBJECT_DATABASE,
+ OBJECT_DEFAULT,
OBJECT_DOMAIN,
OBJECT_EVENT_TRIGGER,
OBJECT_EXTENSION,
--
1.9.1
0003-add-objname-objargs-support.patchtext/x-diff; charset=us-asciiDownload
>From 5525571e41b91ff70c03b08ac914c020dc489e87 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Wed, 15 Oct 2014 18:03:22 -0300
Subject: [PATCH 3/3] add objname/objargs support
---
doc/src/sgml/func.sgml | 16 ++
src/backend/catalog/objectaddress.c | 315 ++++++++++++++++++++++++++++++++---
src/backend/commands/alter.c | 3 +-
src/backend/commands/event_trigger.c | 57 ++++++-
src/backend/commands/tablecmds.c | 2 +-
src/backend/parser/gram.y | 16 +-
src/backend/parser/parse_type.c | 46 +++--
src/backend/parser/parse_utilcmd.c | 2 +-
src/backend/tcop/utility.c | 5 +-
src/backend/utils/adt/regproc.c | 60 +++++++
src/include/catalog/objectaddress.h | 3 +
src/include/catalog/pg_proc.h | 3 +-
src/include/nodes/parsenodes.h | 5 +-
src/include/parser/parse_type.h | 1 +
src/include/utils/builtins.h | 4 +
15 files changed, 481 insertions(+), 57 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 45f3efa..dec2a67 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17625,6 +17625,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 a443e84..4d0726c 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -431,6 +431,97 @@ static const ObjectPropertyType ObjectProperty[] =
}
};
+/*
+ * This struct maps the 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 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 */
+ { "table", OBJECT_TABLE },
+ { "index", OBJECT_INDEX },
+ { "sequence", OBJECT_SEQUENCE },
+ { "toast table", -1 }, /* unmapped */
+ { "view", OBJECT_VIEW },
+ { "materialized view", OBJECT_MATVIEW },
+ { "composite type", OBJECT_COMPOSITE },
+ { "foreign table", OBJECT_FOREIGN_TABLE },
+ { "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", OBJECT_USER_MAPPING },
+ /* OCLASS_DEFACL */
+ { "default acl", -1 }, /* unmapped */
+ /* OCLASS_EXTENSION */
+ { "extension", OBJECT_EXTENSION },
+ /* OCLASS_EVENT_TRIGGER */
+ { "event trigger", OBJECT_EVENT_TRIGGER }
+};
+
+
static ObjectAddress get_object_address_unqualified(ObjectType objtype,
List *qualname, bool missing_ok);
static ObjectAddress get_relation_by_qualified_name(ObjectType objtype,
@@ -456,8 +547,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
@@ -539,11 +631,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:
@@ -943,7 +1052,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);
@@ -999,7 +1108,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) :
@@ -1269,7 +1378,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));
@@ -1282,6 +1391,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;
@@ -1461,6 +1571,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
@@ -2591,6 +2729,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)
@@ -2838,7 +2978,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.
@@ -2846,14 +2986,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;
@@ -2861,17 +3029,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:
@@ -2894,6 +3072,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;
}
@@ -2914,6 +3096,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;
}
@@ -2934,7 +3118,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
{
@@ -2946,7 +3132,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);
@@ -2966,6 +3155,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;
}
@@ -3003,7 +3194,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);
@@ -3023,17 +3215,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:
@@ -3064,14 +3262,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:
@@ -3083,6 +3286,10 @@ getObjectIdentity(const ObjectAddress *object)
Form_pg_amop amopForm;
StringInfoData opfam;
+ /* no objname support here */
+ if (objname)
+ *objname = NIL;
+
amopDesc = heap_open(AccessMethodOperatorRelationId,
AccessShareLock);
@@ -3103,7 +3310,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,
@@ -3127,6 +3334,10 @@ getObjectIdentity(const ObjectAddress *object)
Form_pg_amproc amprocForm;
StringInfoData opfam;
+ /* no objname support here */
+ if (objname)
+ *objname = NIL;
+
amprocDesc = heap_open(AccessMethodProcedureRelationId,
AccessShareLock);
@@ -3147,7 +3358,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,
@@ -3180,7 +3391,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;
@@ -3204,7 +3417,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;
@@ -3220,6 +3435,8 @@ getObjectIdentity(const ObjectAddress *object)
object->objectId);
appendStringInfoString(&buffer,
quote_identifier(nspname));
+ if (objname)
+ *objname = list_make1(nspname);
break;
}
@@ -3239,6 +3456,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;
}
@@ -3259,6 +3479,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;
}
@@ -3279,7 +3502,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;
}
@@ -3300,6 +3525,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;
}
@@ -3308,6 +3536,9 @@ getObjectIdentity(const ObjectAddress *object)
{
char *username;
+ /* no objname support here */
+ Assert(objname == NULL);
+
username = GetUserNameFromId(object->objectId);
appendStringInfoString(&buffer,
quote_identifier(username));
@@ -3318,6 +3549,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",
@@ -3331,6 +3565,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",
@@ -3346,6 +3583,8 @@ getObjectIdentity(const ObjectAddress *object)
fdw = GetForeignDataWrapper(object->objectId);
appendStringInfoString(&buffer, quote_identifier(fdw->fdwname));
+ if (objname)
+ *objname = list_make1(pstrdup(fdw->fdwname));
break;
}
@@ -3356,6 +3595,8 @@ getObjectIdentity(const ObjectAddress *object)
srv = GetForeignServer(object->objectId);
appendStringInfoString(&buffer,
quote_identifier(srv->servername));
+ if (objname)
+ *objname = list_make1(pstrdup(srv->servername));
break;
}
@@ -3365,6 +3606,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))
@@ -3389,10 +3632,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],
@@ -3459,6 +3707,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;
}
@@ -3467,6 +3717,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))
@@ -3487,11 +3740,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;
@@ -3516,6 +3776,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);
}
@@ -3525,7 +3792,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;
@@ -3541,6 +3808,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/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 6cb067e..bb55bbc 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -112,6 +112,8 @@ typedef struct SQLDropObject
const char *objname;
const char *objidentity;
const char *objecttype;
+ List *addrnames;
+ List *addrargs;
bool original;
bool normal;
slist_node next;
@@ -923,11 +925,12 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_ATTRIBUTE:
case OBJECT_CAST:
case OBJECT_COLUMN:
- case OBJECT_CONSTRAINT:
+ case OBJECT_COMPOSITE:
case OBJECT_COLLATION:
case OBJECT_CONVERSION:
case OBJECT_DEFAULT:
case OBJECT_DOMAIN:
+ case OBJECT_DOMCONSTRAINT:
case OBJECT_EXTENSION:
case OBJECT_FDW:
case OBJECT_FOREIGN_SERVER:
@@ -944,6 +947,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:
@@ -951,6 +955,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
case OBJECT_TYPE:
+ case OBJECT_USER_MAPPING:
case OBJECT_VIEW:
return true;
}
@@ -1190,10 +1195,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);
@@ -1202,6 +1208,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
@@ -1256,8 +1289,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);
@@ -1300,6 +1333,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/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index ecdff1e..c6e06f0 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 c98c27a..8ea581e 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -5611,12 +5611,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 RULE name ON any_name IS comment_text
{
CommentStmt *n = makeNode(CommentStmt);
@@ -7301,8 +7310,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;
@@ -7570,7 +7578,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->renameType = OBJECT_TABCONSTRAINT;
n->relationType = OBJECT_TABLE;
n->relation = $3;
n->subname = $6;
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/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 7c1939f..04c8595 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -871,7 +871,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 4a2a339..2b93015 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1587,9 +1587,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;
@@ -1597,6 +1594,7 @@ AlterObjectTypeCommandTag(ObjectType objtype)
tag = "ALTER DATABASE";
break;
case OBJECT_DOMAIN:
+ case OBJECT_DOMCONSTRAINT:
tag = "ALTER DOMAIN";
break;
case OBJECT_EXTENSION:
@@ -1648,6 +1646,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/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 2a9431d..997a156 100644
--- a/src/include/catalog/objectaddress.h
+++ b/src/include/catalog/objectaddress.h
@@ -55,7 +55,10 @@ 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);
+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 c4e932f..a5c8b05 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4988,7 +4988,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");
/* generic transition functions for ordered-set aggregates */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 5a092d8..54f6082 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1208,12 +1208,13 @@ typedef enum ObjectType
OBJECT_ATTRIBUTE, /* type's attribute, when distinct from column */
OBJECT_CAST,
OBJECT_COLUMN,
- OBJECT_CONSTRAINT,
+ OBJECT_COMPOSITE,
OBJECT_COLLATION,
OBJECT_CONVERSION,
OBJECT_DATABASE,
OBJECT_DEFAULT,
OBJECT_DOMAIN,
+ OBJECT_DOMCONSTRAINT,
OBJECT_EVENT_TRIGGER,
OBJECT_EXTENSION,
OBJECT_FDW,
@@ -1232,6 +1233,7 @@ typedef enum ObjectType
OBJECT_RULE,
OBJECT_SCHEMA,
OBJECT_SEQUENCE,
+ OBJECT_TABCONSTRAINT,
OBJECT_TABLE,
OBJECT_TABLESPACE,
OBJECT_TRIGGER,
@@ -1240,6 +1242,7 @@ typedef enum ObjectType
OBJECT_TSPARSER,
OBJECT_TSTEMPLATE,
OBJECT_TYPE,
+ OBJECT_USER_MAPPING,
OBJECT_VIEW
} ObjectType;
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)
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index fb1b4a4..c4c8473 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);
--
1.9.1
Alvaro,
On Wednesday, October 15, 2014, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:
Alvaro Herrera wrote:
Andres Freund wrote:
Having reread the patch just now I basically see two things to
criticize:
a) why isn't this accessible at SQL level? That seems easy to address.
b) Arguably some of this could well be done in separate commits.Fair comments. I will split it up.
Here's a split version. The last part is still missing some polish --
in particular handling for OBJECT_POLICY, and the SQL interface which
would also allow us to get something in the regression tests.
The OBJECT_POLICY bit is on me to clean up and I'm planning to do so
shortly. I agree that we likely want policies for other objects also as a
couple people have brought up that idea now and will investigate it.
I'm planning to address the copy.c comments first and should have a patch
later tonight.
Thanks!
Stephen
On Thu, Oct 16, 2014 at 7:01 AM, Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:
Alvaro Herrera wrote:
Andres Freund wrote:
Having reread the patch just now I basically see two things to
criticize:
a) why isn't this accessible at SQL level? That seems easy to address.
b) Arguably some of this could well be done in separate commits.Fair comments. I will split it up.
Here's a split version. The last part is still missing some polish --
in particular handling for OBJECT_POLICY, and the SQL interface which
would also allow us to get something in the regression tests.Note: in this patch series you can find the ObjectTypeMap thing that you
thought was obsolete in the DDL deparse patch ...
This patch has had no activity for the last two months, is in "Needs
Review" state and has marked as reviewer Dimitri. As there is no
activity from the reviewer, I am moving that to CF 2014-12 and
removing Dimitri as reviewer. If someone wants to have a look at this
patch, feel free to do so. Dimitri, if you are still planning to look
at it, please re-add your name.
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Michael Paquier wrote:
This patch has had no activity for the last two months, is in "Needs
Review" state and has marked as reviewer Dimitri. As there is no
activity from the reviewer, I am moving that to CF 2014-12 and
removing Dimitri as reviewer. If someone wants to have a look at this
patch, feel free to do so. Dimitri, if you are still planning to look
at it, please re-add your name.
FWIW I intend to commit the first patch more or less as-is, and then add
a SQL function accessor to get_object_address to the second part and
commit that one also.
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Alvaro Herrera wrote:
Alvaro Herrera wrote:
Andres Freund wrote:
Having reread the patch just now I basically see two things to
criticize:
a) why isn't this accessible at SQL level? That seems easy to address.
b) Arguably some of this could well be done in separate commits.Fair comments. I will split it up.
Here's a split version. The last part is still missing some polish --
in particular handling for OBJECT_POLICY, and the SQL interface which
would also allow us to get something in the regression tests.
Pushed patch 1.
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Here's a five-part split of the remaining pieces of this patch.
Patch 0001 is the one I posted in
/messages/by-id/20141220022308.GY1768@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
Attachments:
0001-Distinguish-domain-constraint-from-table-constraints.patchtext/x-diff; charset=us-asciiDownload
>From f5a324e864baf60df989e313740744732c04404d Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@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
0002-add-support-for-OBJECT_DEFAULT-in-get_object_address.patchtext/x-diff; charset=us-asciiDownload
>From 92562fa5cd116e1a11cc93d275ff6079639d4cf4 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@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
0003-add-object-type-map-stuff.patchtext/x-diff; charset=us-asciiDownload
>From 44f8e1c391797edda47f64904b89e5f7a1cdb1b9 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@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
0004-Add-sql-callable-pg_get_object_address.patchtext/x-diff; charset=us-asciiDownload
>From 4d5f038a39b88b616f332df3ac52a22469f7203a Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@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
0005-array-objname-objargs-stuff.patchtext/x-diff; charset=us-asciiDownload
>From ae2fe1ecd804a279ceb34eb617a19ac768d8975a Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@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
I have pushed patches 0001 through 0004, with some revisions. Only the
final part is missing.
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 24 December 2014 at 07:41, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:
I have pushed patches 0001 through 0004, with some revisions. Only the
final part is missing.
Hi Alvaro,
Would you be able to commit the attached? It just fixes a new compiler
warning that I'm seeing on MSVC.
src\backend\parser\parse_type.c(795): warning C4715: 'typeStringToTypeName'
: not all control paths return a value [D:\Postgres\a\postgres.vcxproj]
Kind Regards
David Rowley
Attachments:
parse_type_warning_fix.difftext/plain; charset=US-ASCII; name=parse_type_warning_fix.diffDownload
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 299cc53..4ab7fe5 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -792,6 +792,7 @@ fail:
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("invalid type name \"%s\"", str)));
+ return NULL; /* keep compiler quiet */
}
/*
On 2014-12-24 21:54:20 +1300, David Rowley wrote:
On 24 December 2014 at 07:41, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:I have pushed patches 0001 through 0004, with some revisions. Only the
final part is missing.Hi Alvaro,
Would you be able to commit the attached? It just fixes a new compiler
warning that I'm seeing on MSVC.src\backend\parser\parse_type.c(795): warning C4715: 'typeStringToTypeName'
: not all control paths return a value [D:\Postgres\a\postgres.vcxproj]
Pushed.
I really wonder if we can't make msvc reliably recognize this kind of
scenario - especially this case is pretty trivial?
Which of:
#if defined(HAVE__BUILTIN_UNREACHABLE) && !defined(USE_ASSERT_CHECKING)
#define pg_unreachable() __builtin_unreachable()
#elif defined(_MSC_VER) && !defined(USE_ASSERT_CHECKING)
#define pg_unreachable() __assume(0)
#else
#define pg_unreachable() abort()
#endif
does your build end up using? Does changing things around make it
recognize this and similar cases?
Greetings,
Andres Freund
--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 25 December 2014 at 00:34, Andres Freund <andres@2ndquadrant.com> wrote:
On 2014-12-24 21:54:20 +1300, David Rowley wrote:
On 24 December 2014 at 07:41, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:I have pushed patches 0001 through 0004, with some revisions. Only the
final part is missing.Hi Alvaro,
Would you be able to commit the attached? It just fixes a new compiler
warning that I'm seeing on MSVC.src\backend\parser\parse_type.c(795): warning C4715:
'typeStringToTypeName'
: not all control paths return a value [D:\Postgres\a\postgres.vcxproj]
Pushed.
Thanks
I really wonder if we can't make msvc reliably recognize this kind of
scenario - especially this case is pretty trivial?Which of:
#if defined(HAVE__BUILTIN_UNREACHABLE) && !defined(USE_ASSERT_CHECKING)
#define pg_unreachable() __builtin_unreachable()
#elif defined(_MSC_VER) && !defined(USE_ASSERT_CHECKING)
#define pg_unreachable() __assume(0)
#else
#define pg_unreachable() abort()
#endif
I don't think the problem is here. The problem is the the elevel being set
to a variable in the elog macro to prevent the multiple evaluation problem,
then since it does int elevel_ = elevel ... if (elevel_ >= ERROR) that's
not constant, or at least the microsoft compiler is not smart enough to see
that it is.
The attached patch removes the warning, but likely can't be used in case
someone somewhere is doing elog(var++, "my error");
Compiling with the attached shaves almost 1% off the size of postgres.exe:
before; 5,882,368 bytes
after: 5,830,656 bytes
I've been trawling around to try to see if anything
like __builtin_constant_p() exists for MSVC, but so far I've not found
anything useful.
Kind Regards
David Rowley
Attachments:
elog_hacks.difftext/plain; charset=US-ASCII; name=elog_hacks.diffDownload
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 87438b8..8be68dd 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -121,10 +121,9 @@
#else /* !HAVE__BUILTIN_CONSTANT_P */
#define ereport_domain(elevel, domain, rest) \
do { \
- const int elevel_ = (elevel); \
- if (errstart(elevel_, __FILE__, __LINE__, PG_FUNCNAME_MACRO, domain)) \
+ if (errstart(elevel, __FILE__, __LINE__, PG_FUNCNAME_MACRO, domain)) \
errfinish rest; \
- if (elevel_ >= ERROR) \
+ if (elevel >= ERROR) \
pg_unreachable(); \
} while(0)
#endif /* HAVE__BUILTIN_CONSTANT_P */
@@ -258,12 +257,10 @@ extern int getinternalerrposition(void);
#else /* !HAVE__BUILTIN_CONSTANT_P */
#define elog(elevel, ...) \
do { \
- int elevel_; \
- elog_start(__FILE__, __LINE__, PG_FUNCNAME_MACRO); \
- elevel_ = (elevel); \
- elog_finish(elevel_, __VA_ARGS__); \
- if (elevel_ >= ERROR) \
- pg_unreachable(); \
+ elog_start(__FILE__, __LINE__, PG_FUNCNAME_MACRO); \
+ elog_finish(elevel, __VA_ARGS__); \
+ if (elevel >= ERROR) \
+ pg_unreachable(); \
} while(0)
#endif /* HAVE__BUILTIN_CONSTANT_P */
#else /* !HAVE__VA_ARGS */
Andres Freund <andres@2ndquadrant.com> writes:
I really wonder if we can't make msvc reliably recognize this kind of
scenario - especially this case is pretty trivial?
Even if MSVC did understand pg_unreachable(), there would be other
compilers that didn't, so we'd need to worry about suppressing such
warnings anyhow. Personally I'm just as happy that we have instances
of this case in the buildfarm where we can check it easily.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
David Rowley <dgrowley@gmail.com> writes:
On 25 December 2014 at 00:34, Andres Freund <andres@2ndquadrant.com> wrote:
I really wonder if we can't make msvc reliably recognize this kind of
scenario - especially this case is pretty trivial?
The attached patch removes the warning, but likely can't be used in case
someone somewhere is doing elog(var++, "my error");
Yeah, we're *not* doing that. There are definitely places where
ereport/elog are used with nonconstant elevel.
It's curious though that MSVC fails to notice that the variable never
changes. I wonder whether we could get away with changing the elog
macro to do
const int elevel_ = (elevel);
as ereport does, and whether it would help if so.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 25 December 2014 at 04:47, Tom Lane <tgl@sss.pgh.pa.us> wrote:
David Rowley <dgrowley@gmail.com> writes:
On 25 December 2014 at 00:34, Andres Freund <andres@2ndquadrant.com>
wrote:
I really wonder if we can't make msvc reliably recognize this kind of
scenario - especially this case is pretty trivial?The attached patch removes the warning, but likely can't be used in case
someone somewhere is doing elog(var++, "my error");Yeah, we're *not* doing that. There are definitely places where
ereport/elog are used with nonconstant elevel.
Agreed. The patch was intended as a demo of where the problem is. Although
I don't see why non-const elevel matters. Non-stable expressions are what
actually matter.
It's curious though that MSVC fails to notice that the variable never
changes. I wonder whether we could get away with changing the elog
macro to do
const int elevel_ = (elevel);
as ereport does, and whether it would help if so.
Unlikely, as the one that was just fixed above is an ereport.
I'll dig around a little more and see if there's some way to get MSVC to
optimise this somehow. The 1% reduction in the postgres.exe seems worth a
little bit of investigation time.
Regards
David Rowley
Alvaro Herrera wrote:
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.
Here's a fresh version of this patch. I chose to add a SQL-accessible
version, pg_identify_object_as_address, to make it easier to test. In
doing so I noticed a couple of bugs, and most interestingly I noticed
that it was essentially impossible to cleanly address an array type;
doing a roundtrip through the new functions would get me the base type
when I used "integer[]" but the array type when I used "_int4". This
looked like a problem, so I traced through it and noticed that we're
using the type name *list* as a list, rather than as a TypeName, to
refer to OBJECT_TYPE and OBJECT_DOMAIN; I hadn't understood the
significance of this until I realized that domains would be represented
with arrayBounds set to a non-empty list for the integer[] syntax, but
the name list would have "pg_catalog integer" only; when the rest of
TypeName was discarded, the fact that we were talking about an array was
completely forgotten. Before the dawn of time we had this:
-static void
-CommentType(List *typename, char *comment)
-{
- TypeName *tname;
- Oid oid;
-
- /* XXX a bit of a crock; should accept TypeName in COMMENT syntax */
- tname = makeTypeNameFromNameList(typename);
where the XXX comment was removed by commit c10575ff005c330d047534562
without a corresponding comment in the new function.
I'm going to see about changing the grammar to get this fixed; this
patch is important because it will enable us to complete^Wcontinue
working on the DDL deparse testing framework.
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
(The changes in the regression test are bogus, BTW; I didn't care enough
to get them fixed before sorting out the rest of the mess.)
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
objaddr.patchtext/x-diff; charset=us-asciiDownload
commit 89c8cbed0072ad4d921128b834fcb4f9e2eb4c33
Author: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Mon Dec 22 18:32:43 2014 -0300
array objname/objargs stuff
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 24c64b7..112b6a0 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17772,6 +17772,23 @@ 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 <function>pg_get_object_address()</function> to
+ recreate the object address in a remote server containing an
+ identically named object of the same kind.
+ </entry>
+ </row>
+ <row>
+ <entry><literal>address_args</literal></entry>
+ <entry><type>text[]</type></entry>
+ <entry>
+ Complement for <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 85079d6..789af5f 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -74,6 +74,7 @@
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
+#include "utils/memutils.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
@@ -556,8 +557,9 @@ static void getRelationTypeDescription(StringInfo buffer, Oid relid,
int32 objectSubId);
static void getProcedureTypeDescription(StringInfo buffer, Oid procid);
static void getConstraintTypeDescription(StringInfo buffer, Oid constroid);
-static void getOpFamilyIdentity(StringInfo buffer, Oid opfid);
-static void getRelationIdentity(StringInfo buffer, Oid relid);
+static void getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **objname,
+ List **objargs);
+static void getRelationIdentity(StringInfo buffer, Oid relid, List **objname);
/*
* Translate an object name and arguments (as passed by the parser) to an
@@ -2960,6 +2962,66 @@ pg_identify_object(PG_FUNCTION_ARGS)
}
/*
+ * SQL-level callable function to obtain object type + identity
+ */
+Datum
+pg_identify_object_as_address(PG_FUNCTION_ARGS)
+{
+ Oid classid = PG_GETARG_OID(0);
+ Oid objid = PG_GETARG_OID(1);
+ int32 subobjid = PG_GETARG_INT32(2);
+ ObjectAddress address;
+ char *identity;
+ List *names;
+ List *args;
+ Datum values[3];
+ bool nulls[3];
+ TupleDesc tupdesc;
+ HeapTuple htup;
+
+ address.classId = classid;
+ address.objectId = objid;
+ address.objectSubId = subobjid;
+
+ /*
+ * Construct a tuple descriptor for the result row. This must match this
+ * function's pg_proc entry!
+ */
+ tupdesc = CreateTemplateTupleDesc(3, false);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 1, "type",
+ TEXTOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 2, "object_names",
+ TEXTARRAYOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 3, "object_args",
+ TEXTARRAYOID, -1, 0);
+
+ tupdesc = BlessTupleDesc(tupdesc);
+
+ /* object type */
+ values[0] = CStringGetTextDatum(getObjectTypeDescription(&address));
+ nulls[0] = false;
+
+ /* object identity */
+ identity = getObjectIdentityParts(&address, &names, &args);
+ pfree(identity);
+
+ /* object_names */
+ values[1] = PointerGetDatum(strlist_to_textarray(names));
+ nulls[1] = false;
+
+ /* object_args */
+ if (args)
+ values[2] = PointerGetDatum(strlist_to_textarray(args));
+ else
+ values[2] = PointerGetDatum(construct_empty_array(TEXTOID));
+ nulls[2] = false;
+
+ htup = heap_form_tuple(tupdesc, values, nulls);
+
+ PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+}
+
+/*
* Return a palloc'ed string that describes the type of object that the
* passed address is for.
*
@@ -3215,7 +3277,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.
@@ -3223,14 +3285,42 @@ getProcedureTypeDescription(StringInfo buffer, Oid procid)
char *
getObjectIdentity(const ObjectAddress *object)
{
+ return getObjectIdentityParts(object, NULL, NULL);
+}
+
+/*
+ * As above, but more detailed.
+ *
+ * There are two sets of return values: the identity itself as a palloc'd
+ * string is returned. objname and objargs, if not NULL, are output parameters
+ * that receive lists of C-strings that are useful to give back to
+ * get_object_address() to reconstruct the ObjectAddress.
+ */
+char *
+getObjectIdentityParts(const ObjectAddress *object,
+ List **objname, List **objargs)
+{
StringInfoData buffer;
initStringInfo(&buffer);
+ /*
+ * Make sure that both objname and objargs were passed, or none was; and
+ * initialize them to empty lists. For objname this is useless because it
+ * will be initialized in all cases inside the switch; but we do it anyway
+ * so that we can test below that no branch leaves it unset.
+ */
+ Assert(PointerIsValid(objname) == PointerIsValid(objargs));
+ if (objname)
+ {
+ *objname = NIL;
+ *objargs = NIL;
+ }
+
switch (getObjectClass(object))
{
case OCLASS_CLASS:
- getRelationIdentity(&buffer, object->objectId);
+ getRelationIdentity(&buffer, object->objectId, objname);
if (object->objectSubId != 0)
{
char *attr;
@@ -3238,17 +3328,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:
@@ -3271,6 +3371,12 @@ getObjectIdentity(const ObjectAddress *object)
format_type_be_qualified(castForm->castsource),
format_type_be_qualified(castForm->casttarget));
+ if (objname)
+ {
+ *objname = list_make1(format_type_be_qualified(castForm->castsource));
+ *objargs = list_make1(format_type_be_qualified(castForm->casttarget));
+ }
+
heap_close(castRel, AccessShareLock);
break;
}
@@ -3291,6 +3397,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;
}
@@ -3311,19 +3419,35 @@ getObjectIdentity(const ObjectAddress *object)
{
appendStringInfo(&buffer, "%s on ",
quote_identifier(NameStr(con->conname)));
- getRelationIdentity(&buffer, con->conrelid);
+ getRelationIdentity(&buffer, con->conrelid, objname);
+ if (objname)
+ *objname = lappend(*objname, pstrdup(NameStr(con->conname)));
}
else
{
- ObjectAddress domain;
+ HeapTuple domainTup;
+ Form_pg_type typ;
- domain.classId = TypeRelationId;
- domain.objectId = con->contypid;
- domain.objectSubId = 0;
+ Assert(OidIsValid(con->contypid));
+ domainTup = SearchSysCache1(TYPEOID,
+ ObjectIdGetDatum(con->contypid));
+ if (!HeapTupleIsValid(domainTup))
+ elog(ERROR, "cache lookup failed for domain %u",
+ con->contypid);
+ typ = (Form_pg_type) GETSTRUCT(domainTup);
appendStringInfo(&buffer, "%s on %s",
quote_identifier(NameStr(con->conname)),
- getObjectIdentity(&domain));
+ quote_qualified_identifier(get_namespace_name(typ->typnamespace),
+ NameStr(typ->typname)));
+
+ if (objname)
+ {
+ *objname = lappend(*objname, get_namespace_name(typ->typnamespace));
+ *objname = lappend(*objname, NameStr(typ->typname));
+ *objname = lappend(*objname, pstrdup(NameStr(con->conname)));
+ }
+ ReleaseSysCache(domainTup);
}
ReleaseSysCache(conTup);
@@ -3343,6 +3467,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;
}
@@ -3380,7 +3506,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);
@@ -3400,17 +3527,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:
@@ -3441,14 +3574,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:
@@ -3460,6 +3598,10 @@ getObjectIdentity(const ObjectAddress *object)
Form_pg_amop amopForm;
StringInfoData opfam;
+ /* no objname support here */
+ if (objname)
+ *objname = NIL;
+
amopDesc = heap_open(AccessMethodOperatorRelationId,
AccessShareLock);
@@ -3480,7 +3622,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,
@@ -3504,6 +3646,10 @@ getObjectIdentity(const ObjectAddress *object)
Form_pg_amproc amprocForm;
StringInfoData opfam;
+ /* no objname support here */
+ if (objname)
+ *objname = NIL;
+
amprocDesc = heap_open(AccessMethodProcedureRelationId,
AccessShareLock);
@@ -3524,7 +3670,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,
@@ -3557,7 +3703,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;
@@ -3581,7 +3729,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;
@@ -3605,7 +3755,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;
@@ -3621,6 +3773,8 @@ getObjectIdentity(const ObjectAddress *object)
object->objectId);
appendStringInfoString(&buffer,
quote_identifier(nspname));
+ if (objname)
+ *objname = list_make1(nspname);
break;
}
@@ -3640,6 +3794,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;
}
@@ -3660,6 +3817,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;
}
@@ -3680,7 +3840,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;
}
@@ -3701,6 +3863,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;
}
@@ -3710,6 +3875,8 @@ getObjectIdentity(const ObjectAddress *object)
char *username;
username = GetUserNameFromId(object->objectId);
+ if (objname)
+ *objname = list_make1(username);
appendStringInfoString(&buffer,
quote_identifier(username));
break;
@@ -3723,6 +3890,8 @@ getObjectIdentity(const ObjectAddress *object)
if (!datname)
elog(ERROR, "cache lookup failed for database %u",
object->objectId);
+ if (objname)
+ *objname = list_make1(datname);
appendStringInfoString(&buffer,
quote_identifier(datname));
break;
@@ -3736,6 +3905,8 @@ getObjectIdentity(const ObjectAddress *object)
if (!tblspace)
elog(ERROR, "cache lookup failed for tablespace %u",
object->objectId);
+ if (objname)
+ *objname = list_make1(tblspace);
appendStringInfoString(&buffer,
quote_identifier(tblspace));
break;
@@ -3747,6 +3918,8 @@ getObjectIdentity(const ObjectAddress *object)
fdw = GetForeignDataWrapper(object->objectId);
appendStringInfoString(&buffer, quote_identifier(fdw->fdwname));
+ if (objname)
+ *objname = list_make1(pstrdup(fdw->fdwname));
break;
}
@@ -3757,6 +3930,8 @@ getObjectIdentity(const ObjectAddress *object)
srv = GetForeignServer(object->objectId);
appendStringInfoString(&buffer,
quote_identifier(srv->servername));
+ if (objname)
+ *objname = list_make1(pstrdup(srv->servername));
break;
}
@@ -3766,6 +3941,10 @@ getObjectIdentity(const ObjectAddress *object)
Oid useid;
const char *usename;
+ /* no objname support */
+ if (objname)
+ *objname = NIL;
+
tup = SearchSysCache1(USERMAPPINGOID,
ObjectIdGetDatum(object->objectId));
if (!HeapTupleIsValid(tup))
@@ -3790,10 +3969,13 @@ getObjectIdentity(const ObjectAddress *object)
Relation defaclrel;
ScanKeyData skey[1];
SysScanDesc rcscan;
-
HeapTuple tup;
Form_pg_default_acl defacl;
+ /* no objname support */
+ if (objname)
+ *objname = NIL;
+
defaclrel = heap_open(DefaultAclRelationId, AccessShareLock);
ScanKeyInit(&skey[0],
@@ -3860,6 +4042,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;
}
@@ -3868,6 +4052,10 @@ getObjectIdentity(const ObjectAddress *object)
HeapTuple tup;
Form_pg_event_trigger trigForm;
+ /* no objname support here */
+ if (objname)
+ *objname = NIL;
+
tup = SearchSysCache1(EVENTTRIGGEROID,
ObjectIdGetDatum(object->objectId));
if (!HeapTupleIsValid(tup))
@@ -3888,11 +4076,21 @@ getObjectIdentity(const ObjectAddress *object)
break;
}
+ /*
+ * If a get_object_address representation was requested, make sure we are
+ * providing one. We don't check for objargs, because many of the cases
+ * above leave it as NIL.
+ */
+ if (objname && *objname == NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("requested object address for object type that cannot support it")));
+
return buffer.data;
}
static void
-getOpFamilyIdentity(StringInfo buffer, Oid opfid)
+getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **objname, List **objargs)
{
HeapTuple opfTup;
Form_pg_opfamily opfForm;
@@ -3917,6 +4115,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);
}
@@ -3926,7 +4131,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;
@@ -3942,6 +4147,45 @@ getRelationIdentity(StringInfo buffer, Oid relid)
appendStringInfoString(buffer,
quote_qualified_identifier(schema,
NameStr(relForm->relname)));
+ if (objname)
+ *objname = list_make2(schema, pstrdup(NameStr(relForm->relname)));
ReleaseSysCache(relTup);
}
+
+/*
+ * Auxiliary function to return a TEXT array out of a list of C-strings.
+ */
+ArrayType *
+strlist_to_textarray(List *list)
+{
+ ArrayType *arr;
+ Datum *datums;
+ int j = 0;
+ ListCell *cell;
+ MemoryContext memcxt;
+ MemoryContext oldcxt;
+
+ memcxt = AllocSetContextCreate(CurrentMemoryContext,
+ "strlist to array",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+ oldcxt = MemoryContextSwitchTo(memcxt);
+
+ datums = palloc(sizeof(text *) * list_length(list));
+ foreach(cell, list)
+ {
+ char *name = lfirst(cell);
+
+ datums[j++] = CStringGetTextDatum(name);
+ }
+
+ MemoryContextSwitchTo(oldcxt);
+
+ arr = construct_array(datums, list_length(list),
+ TEXTOID, -1, false, 'i');
+ MemoryContextDelete(memcxt);
+
+ return arr;
+}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 34dd3c0..33f9db4 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);
@@ -1390,8 +1393,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 +1437,18 @@ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
else
nulls[i++] = true;
+ /* address_names */
+ if (obj->addrnames)
+ values[i++] = PointerGetDatum(strlist_to_textarray(obj->addrnames));
+ else
+ nulls[i++] = true;
+
+ /* address_args */
+ if (obj->addrargs)
+ values[i++] = PointerGetDatum(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 d885692..27cae44 100644
--- a/src/include/catalog/objectaddress.h
+++ b/src/include/catalog/objectaddress.h
@@ -58,5 +58,8 @@ extern char *getObjectDescriptionOids(Oid classid, Oid objid);
extern int read_objtype_from_string(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);
+extern ArrayType *strlist_to_textarray(List *list);
#endif /* OBJECTADDRESS_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 484b853..54d1f2e 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 = 3382 ( pg_identify_object_as_address PGNSP PGUID 12 1 0 0 0 f f f f t f s 3 0 2249 "26 26 23" "{26,26,23,25,1009,1009}" "{i,i,i,o,o,o}" "{classid,objid,subobjid,type,object_names,object_args}" _null_ pg_identify_object_as_address _null_ _null_ _null_ ));
+DESCR("get identification of SQL object for pg_get_object_address()");
+
DATA(insert OID = 3954 ( pg_get_object_address PGNSP PGUID 12 1 0 0 0 f f f f t 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");
@@ -5078,7 +5081,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..e05ffb2 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);
@@ -1194,6 +1198,7 @@ extern Datum pg_last_committed_xact(PG_FUNCTION_ARGS);
/* catalogs/dependency.c */
extern Datum pg_describe_object(PG_FUNCTION_ARGS);
extern Datum pg_identify_object(PG_FUNCTION_ARGS);
+extern Datum pg_identify_object_as_address(PG_FUNCTION_ARGS);
/* catalog/objectaddress.c */
extern Datum pg_get_object_address(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index ca9a6d6..baa94a8 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -339,46 +339,47 @@ WITH objects (type, name, args) AS (VALUES
-- event trigger
('policy', '{addr_nsp, gentable, genpol}', '{}')
)
-SELECT (pg_identify_object(classid, objid, subobjid)).*
+SELECT (pg_identify_object(classid, objid, subobjid)).*,
+ pg_identify_object_as_address(classid, objid, subobjid)
FROM objects, pg_get_object_address(type, name, args)
ORDER BY classid, objid;
- type | schema | name | identity
----------------------------+------------+-------------------+----------------------------------------------------------------------
- type | pg_catalog | _int4 | integer[]
- type | addr_nsp | gencomptype | addr_nsp.gencomptype
- type | addr_nsp | genenum | addr_nsp.genenum
- type | addr_nsp | gendomain | addr_nsp.gendomain
- function | pg_catalog | | pg_catalog.pg_identify_object(pg_catalog.oid,pg_catalog.oid,integer)
- aggregate | addr_nsp | | addr_nsp.genaggr(integer)
- sequence | addr_nsp | gentable_a_seq | addr_nsp.gentable_a_seq
- table | addr_nsp | gentable | addr_nsp.gentable
- table column | addr_nsp | gentable | addr_nsp.gentable.b
- index | addr_nsp | gentable_pkey | addr_nsp.gentable_pkey
- view | addr_nsp | genview | addr_nsp.genview
- materialized view | addr_nsp | genmatview | addr_nsp.genmatview
- foreign table column | addr_nsp | genftable | addr_nsp.genftable.a
- foreign table | addr_nsp | genftable | addr_nsp.genftable
- role | | regtest_addr_user | regtest_addr_user
- server | | addr_fserv | addr_fserv
- foreign-data wrapper | | addr_fdw | addr_fdw
- default value | | | for addr_nsp.gentable.b
- cast | | | (bigint AS integer)
- table constraint | addr_nsp | | a_chk on addr_nsp.gentable
- domain constraint | addr_nsp | | domconstr on addr_nsp.gendomain
- conversion | pg_catalog | ascii_to_mic | ascii_to_mic
- language | | plpgsql | plpgsql
- schema | | addr_nsp | addr_nsp
- operator class | pg_catalog | int4_ops | pg_catalog.int4_ops for btree
- operator | pg_catalog | | pg_catalog.+(integer,integer)
- rule | | | "_RETURN" on addr_nsp.genview
- trigger | | | t on addr_nsp.gentable
- operator family | pg_catalog | integer_ops | pg_catalog.integer_ops for btree
- policy | | | genpol on addr_nsp.gentable
- collation | pg_catalog | "default" | pg_catalog."default"
- text search dictionary | addr_nsp | addr_ts_dict | addr_nsp.addr_ts_dict
- text search parser | addr_nsp | addr_ts_prs | addr_nsp.addr_ts_prs
- text search configuration | addr_nsp | addr_ts_conf | addr_nsp.addr_ts_conf
- text search template | addr_nsp | addr_ts_temp | addr_nsp.addr_ts_temp
+ type | schema | name | identity | pg_identify_object_as_address
+---------------------------+------------+-------------------+----------------------------------------------------------------------+----------------------------------------------------------------------------------------
+ type | pg_catalog | _int4 | integer[] | (type,{integer[]},{})
+ type | addr_nsp | gencomptype | addr_nsp.gencomptype | (type,{addr_nsp.gencomptype},{})
+ type | addr_nsp | genenum | addr_nsp.genenum | (type,{addr_nsp.genenum},{})
+ type | addr_nsp | gendomain | addr_nsp.gendomain | (type,{addr_nsp.gendomain},{})
+ function | pg_catalog | | pg_catalog.pg_identify_object(pg_catalog.oid,pg_catalog.oid,integer) | (function,"{pg_catalog,pg_identify_object}","{pg_catalog.oid,pg_catalog.oid,integer}")
+ aggregate | addr_nsp | | addr_nsp.genaggr(integer) | (aggregate,"{addr_nsp,genaggr}",{integer})
+ sequence | addr_nsp | gentable_a_seq | addr_nsp.gentable_a_seq | (sequence,"{addr_nsp,gentable_a_seq}",{})
+ table | addr_nsp | gentable | addr_nsp.gentable | (table,"{addr_nsp,gentable}",{})
+ table column | addr_nsp | gentable | addr_nsp.gentable.b | ("table column","{addr_nsp,gentable,b}",{})
+ index | addr_nsp | gentable_pkey | addr_nsp.gentable_pkey | (index,"{addr_nsp,gentable_pkey}",{})
+ view | addr_nsp | genview | addr_nsp.genview | (view,"{addr_nsp,genview}",{})
+ materialized view | addr_nsp | genmatview | addr_nsp.genmatview | ("materialized view","{addr_nsp,genmatview}",{})
+ foreign table column | addr_nsp | genftable | addr_nsp.genftable.a | ("foreign table column","{addr_nsp,genftable,a}",{})
+ foreign table | addr_nsp | genftable | addr_nsp.genftable | ("foreign table","{addr_nsp,genftable}",{})
+ role | | regtest_addr_user | regtest_addr_user | (role,{regtest_addr_user},{})
+ server | | addr_fserv | addr_fserv | (server,{addr_fserv},{})
+ foreign-data wrapper | | addr_fdw | addr_fdw | ("foreign-data wrapper",{addr_fdw},{})
+ default value | | | for addr_nsp.gentable.b | ("default value","{addr_nsp,gentable,b}",{})
+ cast | | | (bigint AS integer) | (cast,{bigint},{integer})
+ table constraint | addr_nsp | | a_chk on addr_nsp.gentable | ("table constraint","{addr_nsp,gentable,a_chk}",{})
+ domain constraint | addr_nsp | | domconstr on addr_nsp.gendomain | ("domain constraint","{addr_nsp.gendomain,domconstr}",{})
+ conversion | pg_catalog | ascii_to_mic | ascii_to_mic | (conversion,{ascii_to_mic},{})
+ language | | plpgsql | plpgsql | (language,{plpgsql},{})
+ schema | | addr_nsp | addr_nsp | (schema,{addr_nsp},{})
+ operator class | pg_catalog | int4_ops | pg_catalog.int4_ops for btree | ("operator class","{pg_catalog,int4_ops}",{btree})
+ operator | pg_catalog | | pg_catalog.+(integer,integer) | (operator,"{pg_catalog,+}","{integer,integer}")
+ rule | | | "_RETURN" on addr_nsp.genview | (rule,"{addr_nsp,genview,_RETURN}",{})
+ trigger | | | t on addr_nsp.gentable | (trigger,"{addr_nsp,gentable,t}",{})
+ operator family | pg_catalog | integer_ops | pg_catalog.integer_ops for btree | ("operator family","{pg_catalog,integer_ops}",{btree})
+ policy | | | genpol on addr_nsp.gentable | (policy,"{addr_nsp,gentable,genpol}",{})
+ collation | pg_catalog | "default" | pg_catalog."default" | (collation,"{pg_catalog,default}",{})
+ text search dictionary | addr_nsp | addr_ts_dict | addr_nsp.addr_ts_dict | ("text search dictionary","{addr_nsp,addr_ts_dict}",{})
+ text search parser | addr_nsp | addr_ts_prs | addr_nsp.addr_ts_prs | ("text search parser","{addr_nsp,addr_ts_prs}",{})
+ text search configuration | addr_nsp | addr_ts_conf | addr_nsp.addr_ts_conf | ("text search configuration","{addr_nsp,addr_ts_conf}",{})
+ text search template | addr_nsp | addr_ts_temp | addr_nsp.addr_ts_temp | ("text search template","{addr_nsp,addr_ts_temp}",{})
(35 rows)
---
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index e5209b9..84032fe 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -159,7 +159,8 @@ WITH objects (type, name, args) AS (VALUES
-- event trigger
('policy', '{addr_nsp, gentable, genpol}', '{}')
)
-SELECT (pg_identify_object(classid, objid, subobjid)).*
+SELECT (pg_identify_object(classid, objid, subobjid)).*,
+ pg_identify_object_as_address(classid, objid, subobjid)
FROM objects, pg_get_object_address(type, name, args)
ORDER BY classid, objid;
Here's a patch that tweaks the grammar to use TypeName in COMMENT,
SECURITY LABEL, and DROP for the type and domain cases. The required
changes in the code are pretty minimal, thankfully. Note the slight
changes to the new object_address test also.
I think I'm pretty much done with this now, so I intend to push this
first thing tomorrow and then the rebased getObjectIdentityParts patch
sometime later.
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
typename.patchtext/x-diff; charset=us-asciiDownload
commit 701a2b7cc3cc6dff865db64ecaeb2fd8bb07b396
Author: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Mon Dec 29 18:55:59 2014 -0300
Use TypeName to represent type names in certain commands
In COMMENT, DROP and SECURITY LABEL we were representing types as a list
of names, rather than the TypeName struct that's used everywhere; this
practice also recently found its way into the new pg_get_object_address.
Right now it doesn't affect anything (other than some code cleanliness),
but it is more problematic for future changes that operate with object
addresses from the SQL interface; type details such as array-ness were
being forgotten.
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 7cb46e1..9ca609d 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -646,13 +646,11 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
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);
+ domaddr = get_object_address_type(OBJECT_DOMAIN, objname, missing_ok);
+ constrname = strVal(linitial(objargs));
address.classId = ConstraintRelationId;
address.objectId = get_domain_constraint_oid(domaddr.objectId,
@@ -1291,14 +1289,13 @@ get_object_address_attrdef(ObjectType objtype, List *objname,
* Find the ObjectAddress for a type or domain
*/
static ObjectAddress
-get_object_address_type(ObjectType objtype,
- List *objname, bool missing_ok)
+get_object_address_type(ObjectType objtype, List *objname, bool missing_ok)
{
ObjectAddress address;
TypeName *typename;
Type tup;
- typename = makeTypeNameFromNameList(objname);
+ typename = (TypeName *) linitial(objname);
address.classId = TypeRelationId;
address.objectId = InvalidOid;
@@ -1428,27 +1425,8 @@ pg_get_object_address(PG_FUNCTION_ARGS)
* given object type. Most use a simple string Values list, but there
* are some exceptions.
*/
- if (type == OBJECT_TYPE || type == OBJECT_DOMAIN)
- {
- Datum *elems;
- bool *nulls;
- int nelems;
- TypeName *typname;
-
- deconstruct_array(namearr, TEXTOID, -1, false, 'i',
- &elems, &nulls, &nelems);
- if (nelems != 1)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("name list length must be exactly %d", 1)));
- if (nulls[0])
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("name or argument lists may not contain nulls")));
- typname = typeStringToTypeName(TextDatumGetCString(elems[0]));
- name = typname->names;
- }
- else if (type == OBJECT_CAST)
+ if (type == OBJECT_TYPE || type == OBJECT_DOMAIN || type == OBJECT_CAST ||
+ type == OBJECT_DOMCONSTRAINT)
{
Datum *elems;
bool *nulls;
@@ -1533,18 +1511,13 @@ pg_get_object_address(PG_FUNCTION_ARGS)
*/
switch (type)
{
- case OBJECT_DOMCONSTRAINT:
- if (list_length(name) < 2)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("name list length must be at least %d", 2)));
- break;
case OBJECT_LARGEOBJECT:
if (list_length(name) != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("name list length must be %d", 1)));
+ errmsg("name list length must be exactly %d", 1)));
break;
+ case OBJECT_DOMCONSTRAINT:
case OBJECT_OPCLASS:
case OBJECT_OPFAMILY:
case OBJECT_CAST:
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 8583581..21a2ae4 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -264,10 +264,14 @@ does_not_exist_skipping(ObjectType objtype, List *objname, List *objargs)
{
case OBJECT_TYPE:
case OBJECT_DOMAIN:
- if (!schema_does_not_exist_skipping(objname, &msg, &name))
{
- msg = gettext_noop("type \"%s\" does not exist, skipping");
- name = TypeNameToString(makeTypeNameFromNameList(objname));
+ TypeName *typ = linitial(objname);
+
+ if (!schema_does_not_exist_skipping(typ->names, &msg, &name))
+ {
+ msg = gettext_noop("type \"%s\" does not exist, skipping");
+ name = TypeNameToString(typ);
+ }
}
break;
case OBJECT_COLLATION:
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 6431601..eaaf2c2 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -351,7 +351,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
opt_column_list columnList opt_name_list
sort_clause opt_sort_clause sortby_list index_params
name_list role_list from_clause from_list opt_array_bounds
- qualified_name_list any_name any_name_list
+ qualified_name_list any_name any_name_list type_name_list
any_operator expr_list attrs
target_list opt_target_list insert_column_list set_target_list
set_clause_list set_clause multiple_set_clause
@@ -5471,6 +5471,46 @@ DropStmt: DROP drop_type IF_P EXISTS any_name_list opt_drop_behavior
n->concurrent = false;
$$ = (Node *)n;
}
+ | DROP TYPE_P type_name_list opt_drop_behavior
+ {
+ DropStmt *n = makeNode(DropStmt);
+ n->removeType = OBJECT_TYPE;
+ n->missing_ok = FALSE;
+ n->objects = $3;
+ n->behavior = $4;
+ n->concurrent = false;
+ $$ = (Node *) n;
+ }
+ | DROP TYPE_P IF_P EXISTS type_name_list opt_drop_behavior
+ {
+ DropStmt *n = makeNode(DropStmt);
+ n->removeType = OBJECT_TYPE;
+ n->missing_ok = TRUE;
+ n->objects = $5;
+ n->behavior = $6;
+ n->concurrent = false;
+ $$ = (Node *) n;
+ }
+ | DROP DOMAIN_P type_name_list opt_drop_behavior
+ {
+ DropStmt *n = makeNode(DropStmt);
+ n->removeType = OBJECT_DOMAIN;
+ n->missing_ok = FALSE;
+ n->objects = $3;
+ n->behavior = $4;
+ n->concurrent = false;
+ $$ = (Node *) n;
+ }
+ | DROP DOMAIN_P IF_P EXISTS type_name_list opt_drop_behavior
+ {
+ DropStmt *n = makeNode(DropStmt);
+ n->removeType = OBJECT_DOMAIN;
+ n->missing_ok = TRUE;
+ n->objects = $5;
+ n->behavior = $6;
+ n->concurrent = false;
+ $$ = (Node *) n;
+ }
| DROP INDEX CONCURRENTLY any_name_list opt_drop_behavior
{
DropStmt *n = makeNode(DropStmt);
@@ -5503,8 +5543,6 @@ drop_type: TABLE { $$ = OBJECT_TABLE; }
| INDEX { $$ = OBJECT_INDEX; }
| FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; }
| EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; }
- | TYPE_P { $$ = OBJECT_TYPE; }
- | DOMAIN_P { $$ = OBJECT_DOMAIN; }
| COLLATION { $$ = OBJECT_COLLATION; }
| CONVERSION_P { $$ = OBJECT_CONVERSION; }
| SCHEMA { $$ = OBJECT_SCHEMA; }
@@ -5530,6 +5568,9 @@ attrs: '.' attr_name
{ $$ = lappend($1, makeString($3)); }
;
+type_name_list:
+ Typename { $$ = list_make1(list_make1($1)); }
+ | type_name_list ',' Typename { $$ = lappend($1, list_make1($3)); }
/*****************************************************************************
*
@@ -5594,6 +5635,24 @@ CommentStmt:
n->comment = $6;
$$ = (Node *) n;
}
+ | COMMENT ON TYPE_P Typename IS comment_text
+ {
+ CommentStmt *n = makeNode(CommentStmt);
+ n->objtype = OBJECT_TYPE;
+ n->objname = list_make1($4);
+ n->objargs = NIL;
+ n->comment = $6;
+ $$ = (Node *) n;
+ }
+ | COMMENT ON DOMAIN_P Typename IS comment_text
+ {
+ CommentStmt *n = makeNode(CommentStmt);
+ n->objtype = OBJECT_DOMAIN;
+ n->objname = list_make1($4);
+ n->objargs = NIL;
+ n->comment = $6;
+ $$ = (Node *) n;
+ }
| COMMENT ON AGGREGATE func_name aggr_args IS comment_text
{
CommentStmt *n = makeNode(CommentStmt);
@@ -5634,8 +5693,13 @@ CommentStmt:
{
CommentStmt *n = makeNode(CommentStmt);
n->objtype = OBJECT_DOMCONSTRAINT;
- n->objname = lappend($7, makeString($4));
- n->objargs = NIL;
+ /*
+ * should use Typename not any_name in the production, but
+ * there's a shift/reduce conflict if we do that, so fix it
+ * up here.
+ */
+ n->objname = list_make1(makeTypeNameFromNameList($7));
+ n->objargs = list_make1(makeString($4));
n->comment = $9;
$$ = (Node *) n;
}
@@ -5730,8 +5794,6 @@ comment_type:
| INDEX { $$ = OBJECT_INDEX; }
| SEQUENCE { $$ = OBJECT_SEQUENCE; }
| TABLE { $$ = OBJECT_TABLE; }
- | DOMAIN_P { $$ = OBJECT_DOMAIN; }
- | TYPE_P { $$ = OBJECT_TYPE; }
| VIEW { $$ = OBJECT_VIEW; }
| MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; }
| COLLATION { $$ = OBJECT_COLLATION; }
@@ -5776,6 +5838,28 @@ SecLabelStmt:
n->label = $8;
$$ = (Node *) n;
}
+ | SECURITY LABEL opt_provider ON TYPE_P Typename
+ IS security_label
+ {
+ SecLabelStmt *n = makeNode(SecLabelStmt);
+ n->provider = $3;
+ n->objtype = OBJECT_TYPE;
+ n->objname = list_make1($6);
+ n->objargs = NIL;
+ n->label = $8;
+ $$ = (Node *) n;
+ }
+ | SECURITY LABEL opt_provider ON DOMAIN_P Typename
+ IS security_label
+ {
+ SecLabelStmt *n = makeNode(SecLabelStmt);
+ n->provider = $3;
+ n->objtype = OBJECT_TYPE;
+ n->objname = list_make1($6);
+ n->objargs = NIL;
+ n->label = $8;
+ $$ = (Node *) n;
+ }
| SECURITY LABEL opt_provider ON AGGREGATE func_name aggr_args
IS security_label
{
@@ -5834,10 +5918,8 @@ security_label_type:
| SCHEMA { $$ = OBJECT_SCHEMA; }
| SEQUENCE { $$ = OBJECT_SEQUENCE; }
| TABLE { $$ = OBJECT_TABLE; }
- | DOMAIN_P { $$ = OBJECT_TYPE; }
| ROLE { $$ = OBJECT_ROLE; }
| TABLESPACE { $$ = OBJECT_TABLESPACE; }
- | TYPE_P { $$ = OBJECT_TYPE; }
| VIEW { $$ = OBJECT_VIEW; }
| MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; }
;
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index ca9a6d6..87c08da 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -171,12 +171,12 @@ WARNING: error for table constraint,{addr_nsp,zwei},{}: relation "addr_nsp" doe
WARNING: error for table constraint,{addr_nsp,zwei},{integer}: relation "addr_nsp" does not exist
WARNING: error for table constraint,{eins,zwei,drei},{}: schema "eins" does not exist
WARNING: error for table constraint,{eins,zwei,drei},{integer}: schema "eins" does not exist
-WARNING: error for domain constraint,{eins},{}: name list length must be at least 2
-WARNING: error for domain constraint,{eins},{integer}: name list length must be at least 2
-WARNING: error for domain constraint,{addr_nsp,zwei},{}: type "addr_nsp" does not exist
-WARNING: error for domain constraint,{addr_nsp,zwei},{integer}: type "addr_nsp" does not exist
-WARNING: error for domain constraint,{eins,zwei,drei},{}: schema "eins" does not exist
-WARNING: error for domain constraint,{eins,zwei,drei},{integer}: schema "eins" does not exist
+WARNING: error for domain constraint,{eins},{}: argument list length must be exactly 1
+WARNING: error for domain constraint,{eins},{integer}: type "eins" does not exist
+WARNING: error for domain constraint,{addr_nsp,zwei},{}: name list length must be exactly 1
+WARNING: error for domain constraint,{addr_nsp,zwei},{integer}: name list length must be exactly 1
+WARNING: error for domain constraint,{eins,zwei,drei},{}: name list length must be exactly 1
+WARNING: error for domain constraint,{eins,zwei,drei},{integer}: name list length must be exactly 1
WARNING: error for conversion,{eins},{}: conversion "eins" does not exist
WARNING: error for conversion,{eins},{integer}: conversion "eins" does not exist
WARNING: error for conversion,{addr_nsp,zwei},{}: conversion "addr_nsp.zwei" does not exist
@@ -312,7 +312,7 @@ WITH objects (type, name, args) AS (VALUES
('cast', '{int8}', '{int4}'),
('collation', '{default}', '{}'),
('table constraint', '{addr_nsp, gentable, a_chk}', '{}'),
- ('domain constraint', '{addr_nsp, gendomain, domconstr}', '{}'),
+ ('domain constraint', '{addr_nsp.gendomain}', '{domconstr}'),
('conversion', '{pg_catalog, ascii_to_mic}', '{}'),
('default value', '{addr_nsp, gentable, b}', '{}'),
('language', '{plpgsql}', '{}'),
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index e5209b9..dc55895 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -132,7 +132,7 @@ WITH objects (type, name, args) AS (VALUES
('cast', '{int8}', '{int4}'),
('collation', '{default}', '{}'),
('table constraint', '{addr_nsp, gentable, a_chk}', '{}'),
- ('domain constraint', '{addr_nsp, gendomain, domconstr}', '{}'),
+ ('domain constraint', '{addr_nsp.gendomain}', '{domconstr}'),
('conversion', '{pg_catalog, ascii_to_mic}', '{}'),
('default value', '{addr_nsp, gentable, b}', '{}'),
('language', '{plpgsql}', '{}'),
On Mon, Dec 29, 2014 at 2:15 PM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:
Here's a patch that tweaks the grammar to use TypeName in COMMENT,
SECURITY LABEL, and DROP for the type and domain cases. The required
changes in the code are pretty minimal, thankfully. Note the slight
changes to the new object_address test also.I think I'm pretty much done with this now, so I intend to push this
first thing tomorrow and then the rebased getObjectIdentityParts patch
sometime later.
This commit 3f88672a4e4d8e648d24ccc65 seems to have broken pg_upgrade for
pg_trgm.
On 9.2.9 freshly initdb and started with default config:
$ createdb jjanes
in psql:
create extension pg_trgm ;
create table foo (x text);
create index on foo using gin (upper(x) gin_trgm_ops);
Then run 9.5's pg_upgrade and it fails at the stage of restoring the
database schema.
The relevant log files are attached
Cheers,
Jeff
On Fri, Jan 2, 2015 at 9:59 PM, Jeff Janes <jeff.janes@gmail.com> wrote:
On Mon, Dec 29, 2014 at 2:15 PM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:Here's a patch that tweaks the grammar to use TypeName in COMMENT,
SECURITY LABEL, and DROP for the type and domain cases. The required
changes in the code are pretty minimal, thankfully. Note the slight
changes to the new object_address test also.I think I'm pretty much done with this now, so I intend to push this
first thing tomorrow and then the rebased getObjectIdentityParts patch
sometime later.This commit 3f88672a4e4d8e648d24ccc65 seems to have broken pg_upgrade for
pg_trgm.On 9.2.9 freshly initdb and started with default config:
$ createdb jjanes
in psql:
create extension pg_trgm ;
create table foo (x text);
create index on foo using gin (upper(x) gin_trgm_ops);Then run 9.5's pg_upgrade and it fails at the stage of restoring the
database schema.
The problem also occurs doing a self-upgrade from 9.5 to 9.5.
The contents of the dump not changed meaningfully between 9.4 and 9.5.
I've isolated the problem to the backend applying the pg_restore of the
dump, regardless of which version created the dump.
After compiling 3c9e4cdbf2ec876dbb7 with CFLAGS="-fno-omit-frame-pointer",
I get this backtrace for the core-dump of postmaster during the pg_restore:
Core was generated by `postgres: jjanes jjanes [local] ALTER EXTENSION
'.
Program terminated with signal 11, Segmentation fault.
#0 0x00000000005135ff in NameListToString (names=0x257fcf8) at
namespace.c:2935
2935 if (IsA(name, String))
(gdb) bt
#0 0x00000000005135ff in NameListToString (names=0x257fcf8) at
namespace.c:2935
#1 0x0000000000512f33 in DeconstructQualifiedName (names=0x257fcf8,
nspname_p=0x7fff419bc960, objname_p=0x7fff419bc958) at namespace.c:2648
#2 0x000000000058a746 in LookupTypeName (pstate=0x0, typeName=0x257fd10,
typmod_p=0x0, missing_ok=0 '\000') at parse_type.c:153
#3 0x00000000005220b4 in get_object_address_type (objtype=OBJECT_TYPE,
objname=0x257fd50, missing_ok=0 '\000') at objectaddress.c:1306
#4 0x0000000000520cf5 in get_object_address (objtype=OBJECT_TYPE,
objname=0x257fd50, objargs=0x0, relp=0x7fff419bcb58, lockmode=4,
missing_ok=0 '\000')
at objectaddress.c:678
#5 0x00000000005c0f36 in ExecAlterExtensionContentsStmt (stmt=0x257fd80)
at extension.c:2906
#6 0x000000000077508c in ProcessUtilitySlow (parsetree=0x257fd80,
queryString=0x254f990 "\n-- For binary upgrade, must preserve pg_type
oid\nSELECT
binary_upgrade.set_next_pg_type_oid('16394'::pg_catalog.oid);\n\n\n-- For
binary upgrade, must preserve pg_type array oid\nSELECT binary_upgrade.se"...,
context=PROCESS_UTILITY_QUERY, params=0x0, dest=0x2581b60,
completionTag=0x7fff419bd100 "") at utility.c:1167
#7 0x000000000077490e in standard_ProcessUtility (parsetree=0x257fd80,
queryString=0x254f990 "\n-- For binary upgrade, must preserve pg_type
oid\nSELECT
binary_upgrade.set_next_pg_type_oid('16394'::pg_catalog.oid);\n\n\n-- For
binary upgrade, must preserve pg_type array oid\nSELECT binary_upgrade.se"...,
context=PROCESS_UTILITY_QUERY, params=0x0, dest=0x2581b60,
completionTag=0x7fff419bd100 "") at utility.c:840
#8 0x0000000000773bcc in ProcessUtility (parsetree=0x257fd80,
queryString=0x254f990 "\n-- For binary upgrade, must preserve pg_type
oid\nSELECT
binary_upgrade.set_next_pg_type_oid('16394'::pg_catalog.oid);\n\n\n-- For
binary upgrade, must preserve pg_type array oid\nSELECT binary_upgrade.se"...,
context=PROCESS_UTILITY_QUERY, params=0x0, dest=0x2581b60,
completionTag=0x7fff419bd100 "") at utility.c:313
#9 0x0000000000772dd6 in PortalRunUtility (portal=0x2505b90,
utilityStmt=0x257fd80, isTopLevel=0 '\000', dest=0x2581b60,
completionTag=0x7fff419bd100 "")
at pquery.c:1187
#10 0x0000000000772f8c in PortalRunMulti (portal=0x2505b90, isTopLevel=0
'\000', dest=0x2581b60, altdest=0x2581b60, completionTag=0x7fff419bd100 "")
at pquery.c:1318
#11 0x0000000000772560 in PortalRun (portal=0x2505b90,
count=9223372036854775807, isTopLevel=0 '\000', dest=0x2581b60,
altdest=0x2581b60,
completionTag=0x7fff419bd100 "") at pquery.c:816
#12 0x000000000076c868 in exec_simple_query (
query_string=0x254f990 "\n-- For binary upgrade, must preserve pg_type
oid\nSELECT
binary_upgrade.set_next_pg_type_oid('16394'::pg_catalog.oid);\n\n\n-- For
binary upgrade, must preserve pg_type array oid\nSELECT binary_upgrade.se"...)
at postgres.c:1045
#13 0x00000000007708a2 in PostgresMain (argc=1, argv=0x24ed5e0,
dbname=0x24ed4b8 "jjanes", username=0x24ed4a0 "jjanes") at postgres.c:4012
#14 0x0000000000701940 in BackendRun (port=0x250c1b0) at postmaster.c:4130
#15 0x0000000000701083 in BackendStartup (port=0x250c1b0) at
postmaster.c:3805
#16 0x00000000006fd8c5 in ServerLoop () at postmaster.c:1573
#17 0x00000000006fd013 in PostmasterMain (argc=18, argv=0x24ec480) at
postmaster.c:1220
#18 0x000000000066476b in main (argc=18, argv=0x24ec480) at main.c:220
Cheers,
Jeff
Jeff Janes wrote:
On Fri, Jan 2, 2015 at 9:59 PM, Jeff Janes <jeff.janes@gmail.com> wrote:
This commit 3f88672a4e4d8e648d24ccc65 seems to have broken pg_upgrade for
pg_trgm.
It seems I failed to notice the get_object_address in the ALTER
EXTENSION path. Should be fixed now. I looked for similar missed
callsites and didn't find anything.
Thanks for the report!
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers