From 196f6af27a615bd304641edbcc892a97f13ecfc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Villemain?= Date: Mon, 17 Mar 2025 14:43:28 +0100 Subject: [PATCH] Change the way ACL are checked whe locking shard In `EnsureTablePermissions()`, which is used in `lock_shard_resources()` but also in few other places, citus does check the ACL at the table level but is not examining attributes ACL. It prevents GRANT INSERT/UPDATE(col) to work as citus requires to lock shared but this GRANT is not a table lock. Change the behavior to check attributes ACL also. Other usage: * `Datum get_shard_id_for_distribution_column()`, looks safe. * `Datum master_create_empty_shard()`, in this case it is apparently really required to be more careful. This is managed in the next commit. --- .../distributed/metadata/metadata_utility.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/backend/distributed/metadata/metadata_utility.c b/src/backend/distributed/metadata/metadata_utility.c index fad263abd..7a8e4f7d8 100644 --- a/src/backend/distributed/metadata/metadata_utility.c +++ b/src/backend/distributed/metadata/metadata_utility.c @@ -2428,14 +2428,27 @@ UpdateNoneDistTableMetadata(Oid relationId, char replicationModel, uint32 coloca /* - * Check that the current user has `mode` permissions on relationId, error out - * if not. Superusers always have such permissions. + * Check that the current user has `mode` permissions on relationId or on at + * least one relationId's attribute, error out if not. + * Superusers always have such permissions. */ void EnsureTablePermissions(Oid relationId, AclMode mode) { AclResult aclresult = pg_class_aclcheck(relationId, GetUserId(), mode); + if (aclresult == ACLCHECK_OK) + { + return; + } + + /* + * Also check the attributes: for example "GRANT ALL(a)" has no table level + * right but user is still allowed to lock table as needed. PostgreSQL will + * still enforce ACL later so it's safe. + */ + aclresult = pg_attribute_aclcheck_all(relationId, GetUserId(), mode, ACLMASK_ANY); + if (aclresult != ACLCHECK_OK) { aclcheck_error(aclresult, OBJECT_TABLE, get_rel_name(relationId));