Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1weOHm-004lqj-2k for pgsql-hackers@arkaria.postgresql.org; Tue, 30 Jun 2026 02:33:18 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1weOHl-0061hK-1y for pgsql-hackers@arkaria.postgresql.org; Tue, 30 Jun 2026 02:33:17 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1weOHl-0061hB-0w for pgsql-hackers@lists.postgresql.org; Tue, 30 Jun 2026 02:33:17 +0000 Received: from mail-dl1-x1229.google.com ([2607:f8b0:4864:20::1229]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1weOHi-00000000uJh-246s for pgsql-hackers@postgresql.org; Tue, 30 Jun 2026 02:33:16 +0000 Received: by mail-dl1-x1229.google.com with SMTP id a92af1059eb24-139f71a5201so174997c88.0 for ; Mon, 29 Jun 2026 19:33:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=leadboat.com; s=google; t=1782786791; x=1783391591; darn=postgresql.org; h=user-agent:content-disposition:mime-version:message-id:subject:to :from:date:from:to:cc:subject:date:message-id:reply-to; bh=nzm5b7LZ2OTiPC6U/cYubTmf2oYgp3uxjE69wuKVHY8=; b=VBUI3Qq3wlsZ6oRYx3o0+S+y6oFyYtxfYnOxt4jUG5wTX49QHS8lew3XtDjIcqqqv3 b1FiIxqgRfoaCCEN+oOa3nOBgAKta6bE0tM+5xLoOF5OD3UUauUZBREr/tJHig34gJF6 W91VYOiMHFkuqprvxAdYIXJ1JZNQI25VVw2fQ= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1782786791; x=1783391591; h=user-agent:content-disposition:mime-version:message-id:subject:to :from:date:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=nzm5b7LZ2OTiPC6U/cYubTmf2oYgp3uxjE69wuKVHY8=; b=l6W3QF0PuXqjh/q/rf8KCdb++FR8nU+dZKlDfdovboEk+UG9dDX+pM7w8TVfqGv62X JmCod1ZItYljzlmV+4uJZFw8On8mOCZ9AhqzwiYJpXyKvO4SYpViJqSkOG3WL2UMoMwM gzwSLRQ9VjOZ3lDBUo+AuvdIl8rfkHdL+s33GFe3NB6sNt3/0TSqxNgboKgq2KHPTwlt VpuSehYX4SJH2flcRCjp5zX5mzNLG8vTtRt8mMoWPLyt0IdEha4iny54RPGl/IVgtzPw CSZh1mAW+oeH4RTjvZcQgEnGyypWFRCAoR/XEBXMN4KLX6yqtJ42Moq+jdtTMBJjTH1A mlSA== X-Gm-Message-State: AOJu0Yzlq31A+YxV3/k6fDEzshypDv+nbSyc2S0XMXFWK3RdHKhkKMx3 hGBsWQtuO7oYJjxFD8h6uOWtYC4xXmfyO1tsuzcYEx7p/3GIq8dwB6nZoBQySLLKCD0uNLvqdT5 rZmsPBA== X-Gm-Gg: AfdE7cm+krKBFMg3djjvEonkRuPWmMDT61QB+LVuOjI1kO4Vf5SgtaC3BQKY8qQq1sF yfYUJlrffyeHG4N0Hcs9hcQ2GKjvf/tdEKKXcmEcXcdtj0JBbThhqS5BPdcBYvCDOXTHQgUYd1K mxkHrbdZ0Ism3hR6DCREm7OgXvYwpGtySYIjA+2ryxk+rOuk7aHBwKYrC0pNDeUyE+efOlWe8YF ZYXbEtkqUs6WMc2FebdS790P40+boXx1n5ArRvFo68FXohcTRzlz6b8rJLA27JrG6vR10y/ct3b OjwkWxCtj4BoB4nPBb0qLJfgjl/V+ylZ3ynkbYfNmPtpV3lW5PCT6MuVwDSflFZl2gogUT0VtoO 1hJkyiWEzhJG/PlGxIy98yLnC4c67LCpyINd/X+eVO6jz+UCcCBiLIPaHJ4Sq3pDntm00QnNJDz mgUpvl7sHO7znOGYsiduXCw6SOsAkR2MPt+/h4BEHTOoKF X-Received: by 2002:a05:693c:41d9:20b0:30e:e:70e6 with SMTP id 5a478bee46e88-30ee18b1273mr940196eec.21.1782786790762; Mon, 29 Jun 2026 19:33:10 -0700 (PDT) Received: from microsoft.com (c-73-15-160-255.hsd1.ca.comcast.net. [73.15.160.255]) by smtp.gmail.com with ESMTPSA id a92af1059eb24-13b2abbba8dsm2568190c88.13.2026.06.29.19.33.09 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Mon, 29 Jun 2026 19:33:10 -0700 (PDT) Date: Mon, 29 Jun 2026 19:33:08 -0700 From: Noah Misch To: pgsql-hackers@postgresql.org Subject: PROPERTY GRAPH pg_dump ACL minimization Message-ID: <20260630023308.c7.noahmisch@microsoft.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="YOLDBvrW7ljwmSt9" Content-Disposition: inline User-Agent: Mutt/2.3.0 (2026-01-25) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --YOLDBvrW7ljwmSt9 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline pg_dump doesn't do its normal ACL minimization for the new PROPERTY GRAPH feature. Patch attached. See log message for details. Most of the patch bulk (modest as it is) exists to keep support for dumping from beta1. I'm not sure whether it was worth bothering. Breaking dump from a beta is without precedent known to me, so I just erred on the side of not breaking it. If we were to decide pg_dump could drop support for betas, I'd be fine with that. This entails a catversion bump on the v19 branch. If the master branch already has a post-branch catversion bump by then, the two catversions should remain distinct. I'll use yyyymmdd1 for v19 and yyyymmdd2 for master. That feels cleanest to me, since it uses the *2 value where it will be shortest-lived. There's precedent in 20b6847 (master) / e256312 (v15). --YOLDBvrW7ljwmSt9 Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=acldefault-propgraph-v1.patch From: Noah Misch Fix pg_dump ACL minimization for PROPERTY GRAPH. Adding a GRANT caused pg_dump to emit a useless REVOKE + GRANT of owner privileges, as seen in a dump of the regression database: REVOKE ALL ON PROPERTY GRAPH graph_rls_schema.cabinet FROM nm; GRANT ALL ON PROPERTY GRAPH graph_rls_schema.cabinet TO nm; GRANT ALL ON PROPERTY GRAPH graph_rls_schema.cabinet TO PUBLIC; For normal dumps, this has no functional consequences. For --no-owner restores, the extra statements may fail or locate unrelated users of the destination cluster. The problem was pg_dump assuming NULL relacl implies acldefault('r'), the default for TABLE. Fix by teaching acldefault() to retrieve the PROPERTY GRAPH default ACL. So pg_dump can still dump from 19beta1, use acldefault('g') for v20+ only. For v19, use a hard-coded snapshot of the v19 default. information_schema.pg_property_graph_privileges also misused acldefault('r'), but its "c.prtype IN ('SELECT')" predicate compensated for it. Switch to the new acldefault('g') for clarity. Bump catversion since a new view won't work with old binaries. Back-patch to v19, which introduced PROPERTY GRAPH. Reviewed-by: FIXME Discussion: https://postgr.es/m/FIXME Backpatch-through: 19 diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 4f0e249..624d538 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -3328,7 +3328,7 @@ CREATE VIEW pg_property_graph_privileges AS THEN 'YES' ELSE 'NO' END AS yes_or_no) AS is_grantable FROM ( - SELECT oid, relname, relnamespace, relkind, relowner, (aclexplode(coalesce(relacl, acldefault('r', relowner)))).* FROM pg_class + SELECT oid, relname, relnamespace, relkind, relowner, (aclexplode(coalesce(relacl, acldefault('g', relowner)))).* FROM pg_class ) AS c (oid, relname, relnamespace, relkind, relowner, grantor, grantee, prtype, grantable), pg_namespace nc, pg_authid u_grantor, diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index 01caa12..e2547d7 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -956,6 +956,9 @@ acldefault_sql(PG_FUNCTION_ARGS) case 'c': objtype = OBJECT_COLUMN; break; + case 'g': + objtype = OBJECT_PROPGRAPH; + break; case 'r': objtype = OBJECT_TABLE; break; diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index c56437d..41b9531 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -7358,8 +7358,17 @@ getTables(Archive *fout, int *numTables) "c.relhastriggers, c.relpersistence, " "c.reloftype, " "c.relacl, " - "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE) - " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, " + "acldefault(CASE" + " WHEN c.relkind = " CppAsString2(RELKIND_PROPGRAPH)); + /* 19beta1 didn't support acldefault('g'), so we'll fix that below */ + appendPQExpBufferStr(query, + fout->remoteVersion >= 200000 ? + " THEN 'g'::\"char\"" : + " THEN NULL"); + appendPQExpBufferStr(query, + " WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE) + " THEN 's'::\"char\"" + " ELSE 'r'::\"char\" END, c.relowner) AS acldefault, " "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN " "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) " "ELSE 0 END AS foreignserver, " @@ -7579,7 +7588,7 @@ getTables(Archive *fout, int *numTables) tblinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_relnamespace))); tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl)); - tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault)); + /* acldefault computed below */ tblinfo[i].dacl.privtype = 0; tblinfo[i].dacl.initprivs = NULL; tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind)); @@ -7631,6 +7640,28 @@ getTables(Archive *fout, int *numTables) tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0); tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0); + if (tblinfo[i].relkind == RELKIND_PROPGRAPH && + !(fout->remoteVersion >= 200000)) + { + PQExpBuffer aclarray = createPQExpBuffer(); + PQExpBuffer aclitem = createPQExpBuffer(); + + /* Standard ACL as of v19 is {owner=r/owner} */ + appendPQExpBufferChar(aclarray, '{'); + quoteAclUserName(aclitem, tblinfo[i].rolname); + appendPQExpBufferStr(aclitem, "=r/"); + quoteAclUserName(aclitem, tblinfo[i].rolname); + appendPGArray(aclarray, aclitem->data); + appendPQExpBufferChar(aclarray, '}'); + + tblinfo[i].dacl.acldefault = pstrdup(aclarray->data); + + destroyPQExpBuffer(aclarray); + destroyPQExpBuffer(aclitem); + } + else + tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault)); + /* other fields were zeroed above */ /* --YOLDBvrW7ljwmSt9--