summaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/commands/lockcmds.c9
-rw-r--r--src/backend/commands/view.c107
-rw-r--r--src/backend/nodes/outfuncs.c7
-rw-r--r--src/backend/nodes/readfuncs.c7
-rw-r--r--src/backend/optimizer/plan/setrefs.c26
-rw-r--r--src/backend/parser/parse_relation.c2
-rw-r--r--src/backend/rewrite/rewriteDefine.c7
-rw-r--r--src/backend/rewrite/rewriteHandler.c38
-rw-r--r--src/backend/utils/cache/plancache.c3
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;
/*