Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1p62mT-0007V4-5f for pgsql-hackers@arkaria.postgresql.org; Fri, 16 Dec 2022 04:57:09 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1p62mQ-0003ZW-UO for pgsql-hackers@arkaria.postgresql.org; Fri, 16 Dec 2022 04:57:06 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1p62mQ-0003ZN-F8 for pgsql-hackers@lists.postgresql.org; Fri, 16 Dec 2022 04:57:06 +0000 Received: from mail-pg1-x52b.google.com ([2607:f8b0:4864:20::52b]) by makus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1p62mN-0000DE-RM for pgsql-hackers@postgresql.org; Fri, 16 Dec 2022 04:57:05 +0000 Received: by mail-pg1-x52b.google.com with SMTP id 79so1009197pgf.11 for ; Thu, 15 Dec 2022 20:57:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date:message-id:reply-to; bh=yi+5iaZohrbNZ9f/cSHvFWh51vUKvl/BL6ekC/VBc/I=; b=UHvYuNuNUPZLEAObWi44TjNAgme/M8N7orR8gU6DyWhhME6xnf1ff2QKhwALaQKCKX QIgnN3YgRrK/BiTfCk3nJddpAjlNTkU6tZ0Lk8ncNmhThb8ee3rEgchVSvikAdtwSNWW HsL2lR1f1fIgKaQzVt8MY/PJ3HfLdNxiOXY6aysLRYj+wQaVTXkq1UEWSTIXPBNdo1/f catVLHnxPcVZaMy1Hd7OVa3ywc2+0AXObN3rITOGB/ibKpPHAC0B1g13KxJ8N3v3+EvD aRTU/6Ycd+vkbGad8zfPdTCZ/WQCCGTISf7nkpmg31iJ0ikWBjwbjFyJJcUH97e23mfm p60g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=yi+5iaZohrbNZ9f/cSHvFWh51vUKvl/BL6ekC/VBc/I=; b=o7P6kiH8LW0gnFtXXbdpt3rB1h+SM5ykE8MtaWCr9p+2LYkKrH48a5HsRWOTrUTM3o NaPjE4/HUoVJIp35dcTnkR0sfVoQ7b8IkkEylyExgmOTKjOJDYfnsYGEr6qa6L9V9tim pJ1QN0gX+QSp0RW4kg7LYwsxaBbB8pTaHP3rMm/3R+sU72uS0G+xMqTLaDLLISE5QU7y K6CJvpRqMQYR241HC3oA/z7irIG+YgPMBtsF5Tl/GKJd092voGp+sNW6bJ9bkmr6B7Bb PYAhBS/tDfs45tpCrpVSMc2KiAo1sKZViy3XwQSmf9eDEZ/l0RmOq93WrUjslmf/vBLa vCgA== X-Gm-Message-State: ANoB5plscSLSMQn4eTwSgFDSbai63+wrUaakznYUzOzVqhxqeYCf8/eD ceLU/WeYxKQF00vScIgWA2A= X-Google-Smtp-Source: AA0mqf4WErpbZ2pw1+gNiCDNGnx5+EjoKLRDdfPEwORl1LwGZZt+fvhufTI5pxSY7KPaq0hk/AKxlw== X-Received: by 2002:a62:ee14:0:b0:566:900d:6073 with SMTP id e20-20020a62ee14000000b00566900d6073mr29649729pfi.24.1671166622560; Thu, 15 Dec 2022 20:57:02 -0800 (PST) Received: from nathanxps13 ([50.47.162.83]) by smtp.gmail.com with ESMTPSA id g28-20020aa79f1c000000b00574ebfdc721sm493615pfr.16.2022.12.15.20.57.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 15 Dec 2022 20:57:01 -0800 (PST) Date: Thu, 15 Dec 2022 20:57:00 -0800 From: Nathan Bossart To: Robert Haas Cc: Andrew Dunstan , Justin Pryzby , pgsql-hackers@postgresql.org Subject: Re: fix and document CLUSTER privileges Message-ID: <20221216045700.GA804311@nathanxps13> References: <20221207223924.GA4182184@nathanxps13> <20221208022559.GA27893@telsasoft.com> <20221208041313.GA216874@nathanxps13> <20221208181324.GA4385@nathanxps13> <20221214173435.GA690225@nathanxps13> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="Qxx1br4bt0+wmkIi" Content-Disposition: inline In-Reply-To: <20221214173435.GA690225@nathanxps13> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --Qxx1br4bt0+wmkIi Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Here is a new version of the patch. I've moved the privilege checks to a new function, and I added a note in the docs about clustering partitioned tables in a transaction block (it's not allowed). -- Nathan Bossart Amazon Web Services: https://aws.amazon.com --Qxx1br4bt0+wmkIi Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="fix_cluster_privs_v3.patch" diff --git a/doc/src/sgml/ref/cluster.sgml b/doc/src/sgml/ref/cluster.sgml index 145101e6a5..9ca66cd2ee 100644 --- a/doc/src/sgml/ref/cluster.sgml +++ b/doc/src/sgml/ref/cluster.sgml @@ -67,7 +67,8 @@ CLUSTER [VERBOSE] - CLUSTER without any parameter reclusters all the + CLUSTER without a + table_name reclusters all the previously-clustered tables in the current database that the calling user owns or has the MAINTAIN privilege for, or all such tables if called by a superuser or a role with privileges of the @@ -134,6 +135,16 @@ CLUSTER [VERBOSE] Notes + + To cluster a table, one must have the MAINTAIN privilege + on the table or be the table's owner, a superuser, or a role with + privileges of the + pg_maintain + role. Database-wide clusters and clusters on partitioned tables will + silently skip over any tables that the calling user does not have + permission to cluster. + + In cases where you are accessing single rows randomly within a table, the actual order of the data in the @@ -202,7 +213,8 @@ CLUSTER [VERBOSE] Clustering a partitioned table clusters each of its partitions using the partition of the specified partitioned index. When clustering a partitioned - table, the index may not be omitted. + table, the index may not be omitted. CLUSTER on a + partitioned table cannot be executed inside a transaction block. diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 8966b75bd1..14328abfa6 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -80,6 +80,7 @@ static void copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, static List *get_tables_to_cluster(MemoryContext cluster_context); static List *get_tables_to_cluster_partitioned(MemoryContext cluster_context, Oid indexOid); +static bool cluster_is_permitted_for_relation(Oid relid, Oid userid); /*--------------------------------------------------------------------------- @@ -366,8 +367,7 @@ cluster_rel(Oid tableOid, Oid indexOid, ClusterParams *params) if (recheck) { /* Check that the user still has privileges for the relation */ - if (!object_ownercheck(RelationRelationId, tableOid, save_userid) && - pg_class_aclcheck(tableOid, save_userid, ACL_MAINTAIN) != ACLCHECK_OK) + if (!cluster_is_permitted_for_relation(tableOid, save_userid)) { relation_close(OldHeap, AccessExclusiveLock); goto out; @@ -1646,8 +1646,7 @@ get_tables_to_cluster(MemoryContext cluster_context) index = (Form_pg_index) GETSTRUCT(indexTuple); - if (!object_ownercheck(RelationRelationId, index->indrelid, GetUserId()) && - pg_class_aclcheck(index->indrelid, GetUserId(), ACL_MAINTAIN) != ACLCHECK_OK) + if (!cluster_is_permitted_for_relation(index->indrelid, GetUserId())) continue; /* Use a permanent memory context for the result list */ @@ -1696,10 +1695,7 @@ get_tables_to_cluster_partitioned(MemoryContext cluster_context, Oid indexOid) continue; /* Silently skip partitions which the user has no access to. */ - if (!object_ownercheck(RelationRelationId, relid, GetUserId()) && - pg_class_aclcheck(relid, GetUserId(), ACL_MAINTAIN) != ACLCHECK_OK && - (!object_ownercheck(DatabaseRelationId, MyDatabaseId, GetUserId()) || - IsSharedRelation(relid))) + if (!cluster_is_permitted_for_relation(relid, GetUserId())) continue; /* Use a permanent memory context for the result list */ @@ -1715,3 +1711,10 @@ get_tables_to_cluster_partitioned(MemoryContext cluster_context, Oid indexOid) return rtcs; } + +static bool +cluster_is_permitted_for_relation(Oid relid, Oid userid) +{ + return object_ownercheck(RelationRelationId, relid, userid) || + pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK; +} --Qxx1br4bt0+wmkIi--