|
16 | 16 | #include <unistd.h> |
17 | 17 |
|
18 | 18 | #include "access/relation.h" |
| 19 | +#include "catalog/index.h" |
19 | 20 | #include "fmgr.h" |
20 | 21 | #include "miscadmin.h" |
21 | 22 | #include "optimizer/cost.h" |
22 | 23 | #include "storage/bufmgr.h" |
| 24 | +#include "storage/lmgr.h" |
23 | 25 | #include "storage/smgr.h" |
24 | 26 | #include "utils/acl.h" |
25 | 27 | #include "utils/builtins.h" |
@@ -69,6 +71,8 @@ pg_prewarm(PG_FUNCTION_ARGS) |
69 | 71 | char *ttype; |
70 | 72 | PrewarmType ptype; |
71 | 73 | AclResult aclresult; |
| 74 | + char relkind; |
| 75 | + Oid privOid; |
72 | 76 |
|
73 | 77 | /* Basic sanity checking. */ |
74 | 78 | if (PG_ARGISNULL(0)) |
@@ -104,9 +108,43 @@ pg_prewarm(PG_FUNCTION_ARGS) |
104 | 108 | forkString = text_to_cstring(forkName); |
105 | 109 | forkNumber = forkname_to_number(forkString); |
106 | 110 |
|
107 | | - /* Open relation and check privileges. */ |
| 111 | + /* |
| 112 | + * Open relation and check privileges. If the relation is an index, we |
| 113 | + * must check the privileges on its parent table instead. |
| 114 | + */ |
| 115 | + relkind = get_rel_relkind(relOid); |
| 116 | + if (relkind == RELKIND_INDEX || |
| 117 | + relkind == RELKIND_PARTITIONED_INDEX) |
| 118 | + { |
| 119 | + privOid = IndexGetRelation(relOid, true); |
| 120 | + |
| 121 | + /* Lock table before index to avoid deadlock. */ |
| 122 | + if (OidIsValid(privOid)) |
| 123 | + LockRelationOid(privOid, AccessShareLock); |
| 124 | + } |
| 125 | + else |
| 126 | + privOid = relOid; |
| 127 | + |
108 | 128 | rel = relation_open(relOid, AccessShareLock); |
109 | | - aclresult = pg_class_aclcheck(relOid, GetUserId(), ACL_SELECT); |
| 129 | + |
| 130 | + /* |
| 131 | + * It's possible that the relation with OID "privOid" was dropped and the |
| 132 | + * OID was reused before we locked it. If that happens, we could be left |
| 133 | + * with the wrong parent table OID, in which case we must ERROR. It's |
| 134 | + * possible that such a race would change the outcome of |
| 135 | + * get_rel_relkind(), too, but the worst case scenario there is that we'll |
| 136 | + * check privileges on the index instead of its parent table, which isn't |
| 137 | + * too terrible. |
| 138 | + */ |
| 139 | + if (!OidIsValid(privOid) || |
| 140 | + (privOid != relOid && |
| 141 | + privOid != IndexGetRelation(relOid, true))) |
| 142 | + ereport(ERROR, |
| 143 | + (errcode(ERRCODE_UNDEFINED_TABLE), |
| 144 | + errmsg("could not find parent table of index \"%s\"", |
| 145 | + RelationGetRelationName(rel)))); |
| 146 | + |
| 147 | + aclresult = pg_class_aclcheck(privOid, GetUserId(), ACL_SELECT); |
110 | 148 | if (aclresult != ACLCHECK_OK) |
111 | 149 | aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind), get_rel_name(relOid)); |
112 | 150 |
|
@@ -211,8 +249,11 @@ pg_prewarm(PG_FUNCTION_ARGS) |
211 | 249 | } |
212 | 250 | } |
213 | 251 |
|
214 | | - /* Close relation, release lock. */ |
| 252 | + /* Close relation, release locks. */ |
215 | 253 | relation_close(rel, AccessShareLock); |
216 | 254 |
|
| 255 | + if (privOid != relOid) |
| 256 | + UnlockRelationOid(privOid, AccessShareLock); |
| 257 | + |
217 | 258 | PG_RETURN_INT64(blocks_done); |
218 | 259 | } |
0 commit comments