get_object_address: separate domain constraints from table constraints
authorAlvaro Herrera <[email protected]>
Tue, 23 Dec 2014 12:06:44 +0000 (09:06 -0300)
committerAlvaro Herrera <[email protected]>
Tue, 23 Dec 2014 12:06:44 +0000 (09:06 -0300)
Apart from enabling comments on domain constraints, this enables a
future project to replicate object dropping to remote servers: with the
current mechanism there's no way to distinguish between the two types of
constraints, so there's no way to know what to drop.

Also added support for the domain constraint comments in psql's \dd and
pg_dump.

Catalog version bumped due to the change in ObjectType enum.

13 files changed:
doc/src/sgml/ref/comment.sgml
src/backend/catalog/objectaddress.c
src/backend/commands/alter.c
src/backend/commands/event_trigger.c
src/backend/commands/tablecmds.c
src/backend/parser/gram.y
src/backend/parser/parse_utilcmd.c
src/backend/tcop/utility.c
src/bin/pg_dump/pg_dump.c
src/bin/psql/describe.c
src/include/nodes/parsenodes.h
src/test/regress/input/constraints.source
src/test/regress/output/constraints.source

index 36a7312056b67bb321942be4d066b3006d966f66..62e1968c08d7ee7dc1a2648483be9f5b40a9bda1 100644 (file)
@@ -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> |
@@ -126,6 +127,18 @@ COMMENT ON
     </listitem>
    </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>
@@ -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';
index e261307e9d5fbce0943c2572505a5d45a09cc55a..297deb5f3f07bac2ffaa292a012f7c1ea38c6c6b 100644 (file)
@@ -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;
index c9a9bafef7418a586ac51db2d9ba605331a6ad41..e7f4ef3e8e64dec7bad3670575f5c4ab0841a7f9 100644 (file)
@@ -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:
index 8b88ecb359e196d8f54174394849e382be89d1f8..6bdb774987871c385e6fdfd5377b60e0e7ca2601 100644 (file)
@@ -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:
index 81c5ab27c966585b9a7a6a611f5dcc4ab6bf3ad5..3c0cdea265e5e70196852c3359e944ee4a282c1a 100644 (file)
@@ -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;
index 1f4fe9d4943edc7c452e1804a0191eb40c328995..6431601c6658554f2e5901ddc5a5a24224f9c490 100644 (file)
@@ -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;
index b9fbb5b6efdf53b3499153a0fca83b22835df4a4..a85327df2cbfbf55a0f4dfd5e8ee9014fa4afc4c 100644 (file)
@@ -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));
index aa8fe880d7ec3de34f19019c067283a94da8155b..71580e8ec510a37dfdc585d4b402caed8a695fff 100644 (file)
@@ -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:
index 4175ddc823eb88bb93d2d3bf74f8b46f21ab63ff..6658fda83e3ea9dc4e3bcc2d2517aefbafd19b03 100644 (file)
@@ -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);
index 5a9ceca0df5e6ba1da3597d9a73b5f98582289d0..f2d33258d7b30ad4fe077e5f35d7669b00f60ddd 100644 (file)
@@ -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+
     */
index 458eeb0b9ec8f4eec10a57e9dc1a319c43b7a07a..64508f0338ac183bac7bfe91a70be33c67f586d4 100644 (file)
@@ -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,
index 16d38f6d1e754f608929b0bf59ea7bf1751b81ef..8ec00543fb8aaf7c41810abf44f9d51ad5c2d7d7 100644 (file)
@@ -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;
index 2ffd263dd352d8fd2a87f4d10a20d8205c00e543..0d32a9eab6c12e7c2fc2aa43a9ca486933bd4211 100644 (file)
@@ -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;