From: Michael Paquier <michael@paquier.xyz>
Date: Wed, 12 Jun 2019 02:31:00 +0000 (+0900)
Subject: Fix handling of COMMENT for domain constraints
X-Git-Tag: REL_10_9~18
X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=56a932533aa2de49d654ca401e6593b3228627ce;p=postgresql

Fix handling of COMMENT for domain constraints

For a non-superuser, changing a comment on a domain constraint was
leading to a cache lookup failure as the code tried to perform the
ownership lookup on the constraint OID itself, thinking that it was a
type, but this check needs to happen on the type the domain constraint
relies on.  As the type a domain constraint relies on can be guessed
directly based on the constraint OID, first fetch its type OID and
perform the ownership on it.

This is broken since 7eca575, which has split the handling of comments
for table constraints and domain constraints, so back-patch down to
9.5.

Reported-by: Clemens Ladisch
Author: Daniel Gustafsson, Michael Paquier
Reviewed-by: Álvaro Herrera
Discussion: https://postgr.es/m/15833-808e11904835d26f@postgresql.org
Backpatch-through: 9.5
---

diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index f8e859d7a2..3768e62627 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -2247,10 +2247,32 @@ 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;
+		case OBJECT_DOMCONSTRAINT:
+			{
+				HeapTuple	tuple;
+				Oid			contypid;
+
+				tuple = SearchSysCache1(CONSTROID,
+										ObjectIdGetDatum(address.objectId));
+				if (!HeapTupleIsValid(tuple))
+					elog(ERROR, "constraint with OID %u does not exist",
+						 address.objectId);
+
+				contypid = ((Form_pg_constraint) GETSTRUCT(tuple))->contypid;
+
+				ReleaseSysCache(tuple);
+
+				/*
+				 * Fallback to type ownership check in this case as this is
+				 * what domain constraints rely on.
+				 */
+				if (!pg_type_ownercheck(contypid, roleid))
+					aclcheck_error_type(ACLCHECK_NOT_OWNER, contypid);
+			}
+			break;
 		case OBJECT_AGGREGATE:
 		case OBJECT_FUNCTION:
 			if (!pg_proc_ownercheck(address.objectId, roleid))
diff --git a/src/test/regress/input/constraints.source b/src/test/regress/input/constraints.source
index dbab8f159b..437499637f 100644
--- a/src/test/regress/input/constraints.source
+++ b/src/test/regress/input/constraints.source
@@ -502,6 +502,10 @@ ALTER TABLE deferred_excl ADD EXCLUDE (f1 WITH =);
 DROP TABLE deferred_excl;
 
 -- Comments
+-- Setup a low-level role to enforce non-superuser checks.
+CREATE ROLE regress_constraint_comments;
+SET SESSION AUTHORIZATION regress_constraint_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);
 
@@ -519,5 +523,16 @@ COMMENT ON CONSTRAINT the_constraint ON DOMAIN no_comments_dom IS 'another bad c
 COMMENT ON CONSTRAINT the_constraint ON constraint_comments_tbl IS NULL;
 COMMENT ON CONSTRAINT the_constraint ON DOMAIN constraint_comments_dom IS NULL;
 
+-- unauthorized user
+RESET SESSION AUTHORIZATION;
+CREATE ROLE regress_constraint_comments_noaccess;
+SET SESSION AUTHORIZATION regress_constraint_comments_noaccess;
+COMMENT ON CONSTRAINT the_constraint ON constraint_comments_tbl IS 'no, the comment';
+COMMENT ON CONSTRAINT the_constraint ON DOMAIN constraint_comments_dom IS 'no, another comment';
+RESET SESSION AUTHORIZATION;
+
 DROP TABLE constraint_comments_tbl;
 DROP DOMAIN constraint_comments_dom;
+
+DROP ROLE regress_constraint_comments;
+DROP ROLE regress_constraint_comments_noaccess;
diff --git a/src/test/regress/output/constraints.source b/src/test/regress/output/constraints.source
index bb75165cc2..16389515cf 100644
--- a/src/test/regress/output/constraints.source
+++ b/src/test/regress/output/constraints.source
@@ -676,6 +676,9 @@ ERROR:  could not create exclusion constraint "deferred_excl_f1_excl"
 DETAIL:  Key (f1)=(3) conflicts with key (f1)=(3).
 DROP TABLE deferred_excl;
 -- Comments
+-- Setup a low-level role to enforce non-superuser checks.
+CREATE ROLE regress_constraint_comments;
+SET SESSION AUTHORIZATION regress_constraint_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';
@@ -692,5 +695,16 @@ COMMENT ON CONSTRAINT the_constraint ON DOMAIN no_comments_dom IS 'another bad c
 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;
+-- unauthorized user
+RESET SESSION AUTHORIZATION;
+CREATE ROLE regress_constraint_comments_noaccess;
+SET SESSION AUTHORIZATION regress_constraint_comments_noaccess;
+COMMENT ON CONSTRAINT the_constraint ON constraint_comments_tbl IS 'no, the comment';
+ERROR:  must be owner of relation constraint_comments_tbl
+COMMENT ON CONSTRAINT the_constraint ON DOMAIN constraint_comments_dom IS 'no, another comment';
+ERROR:  must be owner of type constraint_comments_dom
+RESET SESSION AUTHORIZATION;
 DROP TABLE constraint_comments_tbl;
 DROP DOMAIN constraint_comments_dom;
+DROP ROLE regress_constraint_comments;
+DROP ROLE regress_constraint_comments_noaccess;