Skip to content

Commit d850a66

Browse files
committed
Fix pg_dumpall to cope with dangling OIDs in pg_auth_members.
There is a race condition between "GRANT role" and "DROP ROLE", which allows GRANT to install pg_auth_members entries that refer to dropped roles. (Commit 6566133 prevented that for the grantor field, but not for the granted or grantee roles.) We'll soon fix that, at least in HEAD, but pg_dumpall needs to cope with the situation in case of pre-existing inconsistency. As pg_dumpall stands, it will emit invalid commands like 'GRANT foo TO ""', which causes pg_upgrade to fail. Fix it to emit warnings and skip those GRANTs, instead. There was some discussion of removing the problem by changing dumpRoleMembership's query to use JOIN not LEFT JOIN, but that would result in silently ignoring such entries. It seems better to produce a warning. Pre-v16 branches already coped with dangling grantor OIDs by simply omitting the GRANTED BY clause. I left that behavior as-is, although it's somewhat inconsistent with the behavior of later branches. Reported-by: Virender Singla <virender.cse@gmail.com> Discussion: https://postgr.es/m/CAM6Zo8woa62ZFHtMKox6a4jb8qQ=w87R2L0K8347iE-juQL2EA@mail.gmail.com Backpatch-through: 13
1 parent b2b112b commit d850a66

File tree

1 file changed

+57
-9
lines changed

1 file changed

+57
-9
lines changed

src/bin/pg_dump/pg_dumpall.c

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -960,6 +960,13 @@ dumpRoleMembership(PGconn *conn)
960960
total;
961961
bool dump_grantors;
962962
bool dump_grant_options;
963+
int i_role;
964+
int i_member;
965+
int i_grantor;
966+
int i_roleid;
967+
int i_memberid;
968+
int i_grantorid;
969+
int i_admin_option;
963970
int i_inherit_option;
964971
int i_set_option;
965972

@@ -969,6 +976,10 @@ dumpRoleMembership(PGconn *conn)
969976
* they didn't have ADMIN OPTION on the role, or a user that no longer
970977
* existed. To avoid dump and restore failures, don't dump the grantor
971978
* when talking to an old server version.
979+
*
980+
* Also, in older versions the roleid and/or member could be role OIDs
981+
* that no longer exist. If we find such cases, print a warning and skip
982+
* the entry.
972983
*/
973984
dump_grantors = (PQserverVersion(conn) >= 160000);
974985

@@ -980,8 +991,10 @@ dumpRoleMembership(PGconn *conn)
980991
/* Generate and execute query. */
981992
printfPQExpBuffer(buf, "SELECT ur.rolname AS role, "
982993
"um.rolname AS member, "
983-
"ug.oid AS grantorid, "
984994
"ug.rolname AS grantor, "
995+
"a.roleid AS roleid, "
996+
"a.member AS memberid, "
997+
"a.grantor AS grantorid, "
985998
"a.admin_option");
986999
if (dump_grant_options)
9871000
appendPQExpBufferStr(buf, ", a.inherit_option, a.set_option");
@@ -990,8 +1003,15 @@ dumpRoleMembership(PGconn *conn)
9901003
"LEFT JOIN %s um on um.oid = a.member "
9911004
"LEFT JOIN %s ug on ug.oid = a.grantor "
9921005
"WHERE NOT (ur.rolname ~ '^pg_' AND um.rolname ~ '^pg_')"
993-
"ORDER BY 1,2,4", role_catalog, role_catalog, role_catalog);
1006+
"ORDER BY 1,2,3", role_catalog, role_catalog, role_catalog);
9941007
res = executeQuery(conn, buf->data);
1008+
i_role = PQfnumber(res, "role");
1009+
i_member = PQfnumber(res, "member");
1010+
i_grantor = PQfnumber(res, "grantor");
1011+
i_roleid = PQfnumber(res, "roleid");
1012+
i_memberid = PQfnumber(res, "memberid");
1013+
i_grantorid = PQfnumber(res, "grantorid");
1014+
i_admin_option = PQfnumber(res, "admin_option");
9951015
i_inherit_option = PQfnumber(res, "inherit_option");
9961016
i_set_option = PQfnumber(res, "set_option");
9971017

@@ -1015,24 +1035,32 @@ dumpRoleMembership(PGconn *conn)
10151035
total = PQntuples(res);
10161036
while (start < total)
10171037
{
1018-
char *role = PQgetvalue(res, start, 0);
1038+
char *role = PQgetvalue(res, start, i_role);
10191039
int i;
10201040
bool *done;
10211041
int remaining;
10221042
int prev_remaining = 0;
10231043
rolename_hash *ht;
10241044

1045+
/* If we hit a null roleid, we're done (nulls sort to the end). */
1046+
if (PQgetisnull(res, start, i_role))
1047+
{
1048+
/* translator: %s represents a numeric role OID */
1049+
pg_log_warning("found orphaned pg_auth_members entry for role %s",
1050+
PQgetvalue(res, start, i_roleid));
1051+
break;
1052+
}
1053+
10251054
/* All memberships for a single role should be adjacent. */
10261055
for (end = start; end < total; ++end)
10271056
{
10281057
char *otherrole;
10291058

1030-
otherrole = PQgetvalue(res, end, 0);
1059+
otherrole = PQgetvalue(res, end, i_role);
10311060
if (strcmp(role, otherrole) != 0)
10321061
break;
10331062
}
10341063

1035-
role = PQgetvalue(res, start, 0);
10361064
remaining = end - start;
10371065
done = pg_malloc0(remaining * sizeof(bool));
10381066
ht = rolename_create(remaining, NULL);
@@ -1072,10 +1100,30 @@ dumpRoleMembership(PGconn *conn)
10721100
if (done[i - start])
10731101
continue;
10741102

1075-
member = PQgetvalue(res, i, 1);
1076-
grantorid = PQgetvalue(res, i, 2);
1077-
grantor = PQgetvalue(res, i, 3);
1078-
admin_option = PQgetvalue(res, i, 4);
1103+
/* Complain about, then ignore, entries with orphaned OIDs. */
1104+
if (PQgetisnull(res, i, i_member))
1105+
{
1106+
/* translator: %s represents a numeric role OID */
1107+
pg_log_warning("found orphaned pg_auth_members entry for role %s",
1108+
PQgetvalue(res, i, i_memberid));
1109+
done[i - start] = true;
1110+
--remaining;
1111+
continue;
1112+
}
1113+
if (PQgetisnull(res, i, i_grantor))
1114+
{
1115+
/* translator: %s represents a numeric role OID */
1116+
pg_log_warning("found orphaned pg_auth_members entry for role %s",
1117+
PQgetvalue(res, i, i_grantorid));
1118+
done[i - start] = true;
1119+
--remaining;
1120+
continue;
1121+
}
1122+
1123+
member = PQgetvalue(res, i, i_member);
1124+
grantor = PQgetvalue(res, i, i_grantor);
1125+
grantorid = PQgetvalue(res, i, i_grantorid);
1126+
admin_option = PQgetvalue(res, i, i_admin_option);
10791127
if (dump_grant_options)
10801128
set_option = PQgetvalue(res, i, i_set_option);
10811129

0 commit comments

Comments
 (0)