diff --git a/doc/src/sgml/ref/comment.sgml b/doc/src/sgml/ref/comment.sgml index 36a7312056..62e1968c08 100644 --- a/doc/src/sgml/ref/comment.sgml +++ b/doc/src/sgml/ref/comment.sgml @@ -28,6 +28,7 @@ COMMENT ON COLLATION object_name | COLUMN relation_name.column_name | CONSTRAINT constraint_name ON table_name | + CONSTRAINT constraint_name ON DOMAIN domain_name | CONVERSION object_name | DATABASE object_name | DOMAIN object_name | @@ -126,6 +127,18 @@ COMMENT ON + + table_name + domain_name + + + 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. + + + + source_type @@ -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 e261307e9d..297deb5f3f 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 c9a9bafef7..e7f4ef3e8e 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 8b88ecb359..6bdb774987 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 81c5ab27c9..3c0cdea265 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 1f4fe9d494..6431601c66 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -5572,6 +5572,7 @@ opt_restart_seqs: * CAST ( AS ) | * COLUMN . | * CONSTRAINT ON | + * CONSTRAINT ON DOMAIN | * FUNCTION (arg1, arg2, ...) | * LARGE OBJECT | * OPERATOR (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 b9fbb5b6ef..a85327df2c 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 aa8fe880d7..71580e8ec5 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 4175ddc823..6658fda83e 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 5a9ceca0df..f2d33258d7 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 458eeb0b9e..64508f0338 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 16d38f6d1e..8ec00543fb 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 2ffd263dd3..0d32a9eab6 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;