diff options
Diffstat (limited to 'src/backend/utils/cache/relcache.c')
-rw-r--r-- | src/backend/utils/cache/relcache.c | 73 |
1 files changed, 46 insertions, 27 deletions
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index fccffce5729..fbd11883e17 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -722,6 +722,8 @@ RelationBuildTupleDesc(Relation relation) * entry, because that keeps the update logic in RelationClearRelation() * manageable. The other subsidiary data structures are simple enough * to be easy to free explicitly, anyway. + * + * Note: The relation's reloptions must have been extracted first. */ static void RelationBuildRuleLock(Relation relation) @@ -787,6 +789,7 @@ RelationBuildRuleLock(Relation relation) Datum rule_datum; char *rule_str; RewriteRule *rule; + Oid check_as_user; rule = (RewriteRule *) MemoryContextAlloc(rulescxt, sizeof(RewriteRule)); @@ -826,10 +829,23 @@ RelationBuildRuleLock(Relation relation) pfree(rule_str); /* - * We want the rule's table references to be checked as though by the - * table owner, not the user referencing the rule. Therefore, scan - * through the rule's actions and set the checkAsUser field on all - * rtable entries. We have to look at the qual as well, in case it + * If this is a SELECT rule defining a view, and the view has + * "security_invoker" set, we must perform all permissions checks on + * relations referred to by the rule as the invoking user. + * + * In all other cases (including non-SELECT rules on security invoker + * views), perform the permissions checks as the relation owner. + */ + if (rule->event == CMD_SELECT && + relation->rd_rel->relkind == RELKIND_VIEW && + RelationHasSecurityInvoker(relation)) + check_as_user = InvalidOid; + else + check_as_user = relation->rd_rel->relowner; + + /* + * Scan through the rule's actions and set the checkAsUser field on + * all rtable entries. We have to look at the qual as well, in case it * contains sublinks. * * The reason for doing this when the rule is loaded, rather than when @@ -838,8 +854,8 @@ RelationBuildRuleLock(Relation relation) * the rule tree during load is relatively cheap (compared to * constructing it in the first place), so we do it here. */ - setRuleCheckAsUser((Node *) rule->actions, relation->rd_rel->relowner); - setRuleCheckAsUser(rule->qual, relation->rd_rel->relowner); + setRuleCheckAsUser((Node *) rule->actions, check_as_user); + setRuleCheckAsUser(rule->qual, check_as_user); if (numlocks >= maxlocks) { @@ -1164,27 +1180,6 @@ retry: */ RelationBuildTupleDesc(relation); - /* - * Fetch rules and triggers that affect this relation - */ - if (relation->rd_rel->relhasrules) - RelationBuildRuleLock(relation); - else - { - relation->rd_rules = NULL; - relation->rd_rulescxt = NULL; - } - - if (relation->rd_rel->relhastriggers) - RelationBuildTriggers(relation); - else - relation->trigdesc = NULL; - - if (relation->rd_rel->relrowsecurity) - RelationBuildRowSecurity(relation); - else - relation->rd_rsdesc = NULL; - /* foreign key data is not loaded till asked for */ relation->rd_fkeylist = NIL; relation->rd_fkeyvalid = false; @@ -1217,6 +1212,30 @@ retry: RelationParseRelOptions(relation, pg_class_tuple); /* + * Fetch rules and triggers that affect this relation. + * + * Note that RelationBuildRuleLock() relies on this being done after + * extracting the relation's reloptions. + */ + if (relation->rd_rel->relhasrules) + RelationBuildRuleLock(relation); + else + { + relation->rd_rules = NULL; + relation->rd_rulescxt = NULL; + } + + if (relation->rd_rel->relhastriggers) + RelationBuildTriggers(relation); + else + relation->trigdesc = NULL; + + if (relation->rd_rel->relrowsecurity) + RelationBuildRowSecurity(relation); + else + relation->rd_rsdesc = NULL; + + /* * initialize the relation lock manager information */ RelationInitLockInfo(relation); /* see lmgr.c */ |