Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2023-01-18 18:23:57 +0000
committerTom Lane2023-01-18 18:23:57 +0000
commit47bb9db75996232ea71fc1e1888ffb0e70579b54 (patch)
tree745e1a7755802a7e92cd267bce642358a1f37a0a /src/backend
parent8d83a5d0a2673174dc478e707de1f502935391a5 (diff)
Get rid of the "new" and "old" entries in a view's rangetable.
The rule system needs "old" and/or "new" pseudo-RTEs in rule actions that are ON INSERT/UPDATE/DELETE. Historically it's put such entries into the ON SELECT rules of views as well, but those are really quite vestigial. The only thing we've used them for is to carry the view's relid forward to AcquireExecutorLocks (so that we can re-lock the view to verify it hasn't changed before re-using a plan) and to carry its relid and permissions data forward to execution-time permissions checks. What we can do instead of that is to retain these fields of the RTE_RELATION RTE for the view even after we convert it to an RTE_SUBQUERY RTE. This requires a tiny amount of extra complication in the planner and AcquireExecutorLocks, but on the other hand we can get rid of the logic that moves that data from one place to another. The principal immediate benefit of doing this, aside from a small saving in the pg_rewrite data for views, is that these pseudo-RTEs no longer trigger ruleutils.c's heuristic about qualifying variable names when the rangetable's length is more than 1. That results in quite a number of small simplifications in regression test outputs, which are all to the good IMO. Bump catversion because we need to dump a few more fields of RTE_SUBQUERY RTEs. While those will always be zeroes anyway in stored rules (because we'd never populate them until query rewrite) they are useful for debugging, and it seems like we'd better make sure to transmit such RTEs accurately in plans sent to parallel workers. I don't think the executor actually examines these fields after startup, but someday it might. This is a second attempt at committing 1b4d280ea. The difference from the first time is that now we can add some filtering rules to AdjustUpgrade.pm to allow cross-version upgrade testing to pass despite all the cosmetic changes in CREATE VIEW outputs. Amit Langote (filtering rules by me) Discussion: https://postgr.es/m/CA+HiwqEf7gPN4Hn+LoZ4tP2q_Qt7n3vw7-6fJKOf92tSEnX6Gg@mail.gmail.com Discussion: https://postgr.es/m/891521.1673657296@sss.pgh.pa.us
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, 41 insertions, 165 deletions
diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c
index 6bf1b815f01..43c7d7f4bb2 100644
--- a/src/backend/commands/lockcmds.c
+++ b/src/backend/commands/lockcmds.c
@@ -195,15 +195,6 @@ 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 0bacb819e52..ff98c773f55 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -353,107 +353,6 @@ 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.
@@ -617,12 +516,6 @@ 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 69324d5a9aa..6b368b08b21 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -512,6 +512,10 @@ _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);
@@ -545,10 +549,11 @@ _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 30cd7a0da6b..f3629cdfd14 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -478,6 +478,10 @@ _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);
@@ -520,10 +524,11 @@ _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 ed9c1e61876..85ba9d1ca1e 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -405,13 +405,15 @@ 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.
+ * recursing, we only care about extracting relation RTEs (and subquery
+ * RTEs that were once relation RTEs).
*/
foreach(lc, root->parse->rtable)
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
- if (!recursing || rte->rtekind == RTE_RELATION)
+ if (!recursing || rte->rtekind == RTE_RELATION ||
+ (rte->rtekind == RTE_SUBQUERY && OidIsValid(rte->relid)))
add_rte_to_flat_rtable(glob, root->parse->rteperminfos, rte);
}
@@ -501,8 +503,9 @@ flatten_rtes_walker(Node *node, flatten_rtes_walker_context *cxt)
{
RangeTblEntry *rte = (RangeTblEntry *) node;
- /* As above, we need only save relation RTEs */
- if (rte->rtekind == RTE_RELATION)
+ /* As above, we need only save relation RTEs and former relations */
+ if (rte->rtekind == RTE_RELATION ||
+ (rte->rtekind == RTE_SUBQUERY && OidIsValid(rte->relid)))
add_rte_to_flat_rtable(cxt->glob, cxt->query->rteperminfos, rte);
return false;
}
@@ -560,7 +563,8 @@ add_rte_to_flat_rtable(PlannerGlobal *glob, List *rteperminfos,
glob->finalrtable = lappend(glob->finalrtable, newrte);
/*
- * If it's a plain relation RTE, add the table to relationOids.
+ * If it's a plain relation RTE (or a subquery that was once a view
+ * reference), add the relation OID 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
@@ -570,7 +574,8 @@ 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)
+ if (newrte->rtekind == RTE_RELATION ||
+ (newrte->rtekind == RTE_SUBQUERY && OidIsValid(newrte->relid)))
glob->relationOids = lappend_oid(glob->relationOids, newrte->relid);
/*
@@ -3403,14 +3408,11 @@ extract_query_dependencies_walker(Node *node, PlannerInfo *context)
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
- if (rte->rtekind == RTE_RELATION)
+ if (rte->rtekind == RTE_RELATION ||
+ (rte->rtekind == RTE_SUBQUERY && OidIsValid(rte->relid)) ||
+ (rte->rtekind == RTE_NAMEDTUPLESTORE && OidIsValid(rte->relid)))
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 5389a0eddb6..b490541f03d 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(rte->rtekind == RTE_RELATION);
+ Assert(OidIsValid(rte->relid));
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 52272202881..e36fc72e1ed 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -633,13 +633,6 @@ 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 1960dad7013..c74bac20b13 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1715,10 +1715,7 @@ ApplyRetrieveRule(Query *parsetree,
List *activeRIRs)
{
Query *rule_action;
- RangeTblEntry *rte,
- *subrte;
- RTEPermissionInfo *perminfo,
- *sub_perminfo;
+ RangeTblEntry *rte;
RowMarkClause *rc;
if (list_length(rule->actions) != 1)
@@ -1830,32 +1827,20 @@ 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 */
- 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.
+ * 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.
*/
- 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;
+ rte->relkind = 0;
+ rte->tablesample = NULL;
+ rte->inh = false; /* must not be set for a subquery */
return parsetree;
}
@@ -1867,9 +1852,10 @@ ApplyRetrieveRule(Query *parsetree,
* aggregate. We leave it to the planner to detect that.
*
* NB: this must agree with the parser's transformLockingClause() routine.
- * 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.
+ * 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.
*/
static void
markQueryForLocking(Query *qry, Node *jtnode,
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index 92f6d5795fc..77c2ba3f8f4 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -1769,7 +1769,8 @@ AcquireExecutorLocks(List *stmt_list, bool acquire)
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc2);
- if (rte->rtekind != RTE_RELATION)
+ if (!(rte->rtekind == RTE_RELATION ||
+ (rte->rtekind == RTE_SUBQUERY && OidIsValid(rte->relid))))
continue;
/*