diff options
Diffstat (limited to 'src/backend')
| -rw-r--r-- | src/backend/commands/lockcmds.c | 9 | ||||
| -rw-r--r-- | src/backend/commands/view.c | 107 | ||||
| -rw-r--r-- | src/backend/nodes/outfuncs.c | 7 | ||||
| -rw-r--r-- | src/backend/nodes/readfuncs.c | 7 | ||||
| -rw-r--r-- | src/backend/optimizer/plan/setrefs.c | 26 | ||||
| -rw-r--r-- | src/backend/parser/parse_relation.c | 2 | ||||
| -rw-r--r-- | src/backend/rewrite/rewriteDefine.c | 7 | ||||
| -rw-r--r-- | src/backend/rewrite/rewriteHandler.c | 38 | ||||
| -rw-r--r-- | src/backend/utils/cache/plancache.c | 3 |
9 files changed, 165 insertions, 41 deletions
diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c index 9755d6ed13..99e68bff85 100644 --- a/src/backend/commands/lockcmds.c +++ b/src/backend/commands/lockcmds.c @@ -194,6 +194,15 @@ LockViewRecurse_walker(Node *node, LockViewRecurse_context *context) char relkind = rte->relkind; char *relname = get_rel_name(relid); + /* + * The OLD and NEW placeholder entries in the view's rtable are + * skipped. + */ + if (relid == context->viewoid && + (strcmp(rte->eref->aliasname, "old") == 0 || + strcmp(rte->eref->aliasname, "new") == 0)) + continue; + /* Currently, we only allow plain tables or views to be locked. */ if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE && relkind != RELKIND_VIEW) diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index ff98c773f5..0bacb819e5 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -353,6 +353,107 @@ DefineViewRules(Oid viewOid, Query *viewParse, bool replace) */ } +/*--------------------------------------------------------------- + * UpdateRangeTableOfViewParse + * + * Update the range table of the given parsetree. + * This update consists of adding two new entries IN THE BEGINNING + * of the range table (otherwise the rule system will die a slow, + * horrible and painful death, and we do not want that now, do we?) + * one for the OLD relation and one for the NEW one (both of + * them refer in fact to the "view" relation). + * + * Of course we must also increase the 'varnos' of all the Var nodes + * by 2... + * + * These extra RT entries are not actually used in the query, + * except for run-time locking. + *--------------------------------------------------------------- + */ +static Query * +UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) +{ + Relation viewRel; + List *new_rt; + ParseNamespaceItem *nsitem; + RangeTblEntry *rt_entry1, + *rt_entry2; + RTEPermissionInfo *rte_perminfo1; + ParseState *pstate; + ListCell *lc; + + /* + * Make a copy of the given parsetree. It's not so much that we don't + * want to scribble on our input, it's that the parser has a bad habit of + * outputting multiple links to the same subtree for constructs like + * BETWEEN, and we mustn't have OffsetVarNodes increment the varno of a + * Var node twice. copyObject will expand any multiply-referenced subtree + * into multiple copies. + */ + viewParse = copyObject(viewParse); + + /* Create a dummy ParseState for addRangeTableEntryForRelation */ + pstate = make_parsestate(NULL); + + /* need to open the rel for addRangeTableEntryForRelation */ + viewRel = relation_open(viewOid, AccessShareLock); + + /* + * Create the 2 new range table entries and form the new range table... + * OLD first, then NEW.... + */ + nsitem = addRangeTableEntryForRelation(pstate, viewRel, + AccessShareLock, + makeAlias("old", NIL), + false, false); + rt_entry1 = nsitem->p_rte; + rte_perminfo1 = nsitem->p_perminfo; + nsitem = addRangeTableEntryForRelation(pstate, viewRel, + AccessShareLock, + makeAlias("new", NIL), + false, false); + rt_entry2 = nsitem->p_rte; + + /* + * Add only the "old" RTEPermissionInfo at the head of view query's list + * and update the other RTEs' perminfoindex accordingly. When rewriting a + * query on the view, ApplyRetrieveRule() will transfer the view + * relation's permission details into this RTEPermissionInfo. That's + * needed because the view's RTE itself will be transposed into a subquery + * RTE that can't carry the permission details; see the code stanza toward + * the end of ApplyRetrieveRule() for how that's done. + */ + viewParse->rteperminfos = lcons(rte_perminfo1, viewParse->rteperminfos); + foreach(lc, viewParse->rtable) + { + RangeTblEntry *rte = lfirst(lc); + + if (rte->perminfoindex > 0) + rte->perminfoindex += 1; + } + + /* + * Also make the "new" RTE's RTEPermissionInfo undiscoverable. This is a + * bit of a hack given that all the non-child RTE_RELATION entries really + * should have a RTEPermissionInfo, but this dummy "new" RTE is going to + * go away anyway in the very near future. + */ + rt_entry2->perminfoindex = 0; + + new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); + + viewParse->rtable = new_rt; + + /* + * Now offset all var nodes by 2, and jointree RT indexes too. + */ + OffsetVarNodes((Node *) viewParse, 2, 0); + + relation_close(viewRel, AccessShareLock); + + return viewParse; +} + /* * DefineView * Execute a CREATE VIEW command. @@ -516,6 +617,12 @@ void StoreViewQuery(Oid viewOid, Query *viewParse, bool replace) { /* + * The range table of 'viewParse' does not contain entries for the "OLD" + * and "NEW" relations. So... add them! + */ + viewParse = UpdateRangeTableOfViewParse(viewOid, viewParse); + + /* * Now create the rules associated with the view. */ DefineViewRules(viewOid, viewParse, replace); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 6b368b08b2..69324d5a9a 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -512,10 +512,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) case RTE_SUBQUERY: WRITE_NODE_FIELD(subquery); WRITE_BOOL_FIELD(security_barrier); - /* we re-use these RELATION fields, too: */ - WRITE_OID_FIELD(relid); - WRITE_INT_FIELD(rellockmode); - WRITE_UINT_FIELD(perminfoindex); break; case RTE_JOIN: WRITE_ENUM_FIELD(jointype, JoinType); @@ -549,11 +545,10 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) case RTE_NAMEDTUPLESTORE: WRITE_STRING_FIELD(enrname); WRITE_FLOAT_FIELD(enrtuples); + WRITE_OID_FIELD(relid); WRITE_NODE_FIELD(coltypes); WRITE_NODE_FIELD(coltypmods); WRITE_NODE_FIELD(colcollations); - /* we re-use these RELATION fields, too: */ - WRITE_OID_FIELD(relid); break; case RTE_RESULT: /* no extra fields */ diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index f3629cdfd1..30cd7a0da6 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -478,10 +478,6 @@ _readRangeTblEntry(void) case RTE_SUBQUERY: READ_NODE_FIELD(subquery); READ_BOOL_FIELD(security_barrier); - /* we re-use these RELATION fields, too: */ - READ_OID_FIELD(relid); - READ_INT_FIELD(rellockmode); - READ_UINT_FIELD(perminfoindex); break; case RTE_JOIN: READ_ENUM_FIELD(jointype, JoinType); @@ -524,11 +520,10 @@ _readRangeTblEntry(void) case RTE_NAMEDTUPLESTORE: READ_STRING_FIELD(enrname); READ_FLOAT_FIELD(enrtuples); + READ_OID_FIELD(relid); READ_NODE_FIELD(coltypes); READ_NODE_FIELD(coltypmods); READ_NODE_FIELD(colcollations); - /* we re-use these RELATION fields, too: */ - READ_OID_FIELD(relid); break; case RTE_RESULT: /* no extra fields */ diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 85ba9d1ca1..ed9c1e6187 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -405,15 +405,13 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) * * At top level, we must add all RTEs so that their indexes in the * flattened rangetable match up with their original indexes. When - * recursing, we only care about extracting relation RTEs (and subquery - * RTEs that were once relation RTEs). + * recursing, we only care about extracting relation RTEs. */ foreach(lc, root->parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); - if (!recursing || rte->rtekind == RTE_RELATION || - (rte->rtekind == RTE_SUBQUERY && OidIsValid(rte->relid))) + if (!recursing || rte->rtekind == RTE_RELATION) add_rte_to_flat_rtable(glob, root->parse->rteperminfos, rte); } @@ -503,9 +501,8 @@ flatten_rtes_walker(Node *node, flatten_rtes_walker_context *cxt) { RangeTblEntry *rte = (RangeTblEntry *) node; - /* As above, we need only save relation RTEs and former relations */ - if (rte->rtekind == RTE_RELATION || - (rte->rtekind == RTE_SUBQUERY && OidIsValid(rte->relid))) + /* As above, we need only save relation RTEs */ + if (rte->rtekind == RTE_RELATION) add_rte_to_flat_rtable(cxt->glob, cxt->query->rteperminfos, rte); return false; } @@ -563,8 +560,7 @@ add_rte_to_flat_rtable(PlannerGlobal *glob, List *rteperminfos, glob->finalrtable = lappend(glob->finalrtable, newrte); /* - * If it's a plain relation RTE (or a subquery that was once a view - * reference), add the relation OID to relationOids. + * If it's a plain relation RTE, add the table to relationOids. * * We do this even though the RTE might be unreferenced in the plan tree; * this would correspond to cases such as views that were expanded, child @@ -574,8 +570,7 @@ add_rte_to_flat_rtable(PlannerGlobal *glob, List *rteperminfos, * Note we don't bother to avoid making duplicate list entries. We could, * but it would probably cost more cycles than it would save. */ - if (newrte->rtekind == RTE_RELATION || - (newrte->rtekind == RTE_SUBQUERY && OidIsValid(newrte->relid))) + if (newrte->rtekind == RTE_RELATION) glob->relationOids = lappend_oid(glob->relationOids, newrte->relid); /* @@ -3408,11 +3403,14 @@ extract_query_dependencies_walker(Node *node, PlannerInfo *context) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); - if (rte->rtekind == RTE_RELATION || - (rte->rtekind == RTE_SUBQUERY && OidIsValid(rte->relid)) || - (rte->rtekind == RTE_NAMEDTUPLESTORE && OidIsValid(rte->relid))) + if (rte->rtekind == RTE_RELATION) context->glob->relationOids = lappend_oid(context->glob->relationOids, rte->relid); + else if (rte->rtekind == RTE_NAMEDTUPLESTORE && + OidIsValid(rte->relid)) + context->glob->relationOids = + lappend_oid(context->glob->relationOids, + rte->relid); } /* And recurse into the query's subexpressions */ diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index b490541f03..5389a0eddb 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -3834,7 +3834,7 @@ addRTEPermissionInfo(List **rteperminfos, RangeTblEntry *rte) { RTEPermissionInfo *perminfo; - Assert(OidIsValid(rte->relid)); + Assert(rte->rtekind == RTE_RELATION); Assert(rte->perminfoindex == 0); /* Nope, so make one and add to the list. */ diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index e36fc72e1e..5227220288 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -633,6 +633,13 @@ checkRuleResultList(List *targetList, TupleDesc resultDesc, bool isSelect, * setRuleCheckAsUser * Recursively scan a query or expression tree and set the checkAsUser * field to the given userid in all RTEPermissionInfos of the query. + * + * Note: for a view (ON SELECT rule), the checkAsUser field of the OLD + * RTE entry's RTEPermissionInfo will be overridden when the view rule is + * expanded, and the checkAsUser for the NEW RTE entry's RTEPermissionInfo is + * irrelevant because its requiredPerms bits will always be zero. However, for + * other types of rules it's important to set these fields to match the rule + * owner. So we just set them always. */ void setRuleCheckAsUser(Node *node, Oid userid) diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index c74bac20b1..1960dad701 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1715,7 +1715,10 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte; + RangeTblEntry *rte, + *subrte; + RTEPermissionInfo *perminfo, + *sub_perminfo; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1827,21 +1830,33 @@ ApplyRetrieveRule(Query *parsetree, * original RTE to a subquery RTE. */ rte = rt_fetch(rt_index, parsetree->rtable); + perminfo = getRTEPermissionInfo(parsetree->rteperminfos, rte); rte->rtekind = RTE_SUBQUERY; rte->subquery = rule_action; rte->security_barrier = RelationIsSecurityView(relation); - - /* - * Clear fields that should not be set in a subquery RTE. Note that we - * leave the relid, rellockmode, and perminfoindex fields set, so that the - * view relation can be appropriately locked before execution and its - * permissions checked. - */ + /* Clear fields that should not be set in a subquery RTE */ + rte->relid = InvalidOid; rte->relkind = 0; + rte->rellockmode = 0; rte->tablesample = NULL; + rte->perminfoindex = 0; /* no permission checking for this RTE */ rte->inh = false; /* must not be set for a subquery */ + /* + * We move the view's permission check data down to its RTEPermissionInfo + * contained in the view query, which the OLD entry in its range table + * points to. + */ + subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); + Assert(subrte->relid == relation->rd_id); + sub_perminfo = getRTEPermissionInfo(rule_action->rteperminfos, subrte); + sub_perminfo->requiredPerms = perminfo->requiredPerms; + sub_perminfo->checkAsUser = perminfo->checkAsUser; + sub_perminfo->selectedCols = perminfo->selectedCols; + sub_perminfo->insertedCols = perminfo->insertedCols; + sub_perminfo->updatedCols = perminfo->updatedCols; + return parsetree; } @@ -1852,10 +1867,9 @@ ApplyRetrieveRule(Query *parsetree, * aggregate. We leave it to the planner to detect that. * * NB: this must agree with the parser's transformLockingClause() routine. - * However, we used to have to avoid marking a view's OLD and NEW rels for - * updating, which motivated scanning the jointree to determine which rels - * are used. Possibly that could now be simplified into just scanning the - * rangetable as the parser does. + * However, unlike the parser we have to be careful not to mark a view's + * OLD and NEW rels for updating. The best way to handle that seems to be + * to scan the jointree to determine which rels are used. */ static void markQueryForLocking(Query *qry, Node *jtnode, diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c index 77c2ba3f8f..92f6d5795f 100644 --- a/src/backend/utils/cache/plancache.c +++ b/src/backend/utils/cache/plancache.c @@ -1769,8 +1769,7 @@ AcquireExecutorLocks(List *stmt_list, bool acquire) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc2); - if (!(rte->rtekind == RTE_RELATION || - (rte->rtekind == RTE_SUBQUERY && OidIsValid(rte->relid)))) + if (rte->rtekind != RTE_RELATION) continue; /* |
